Improve XLib-XInput error handling

This commit is contained in:
Kristóf Tóth 2021-10-24 14:09:00 +02:00
parent 56b65b8449
commit 00bacdec3e
7 changed files with 86 additions and 74 deletions

View File

@ -1,5 +0,0 @@
main: main.c
$(CC) -O2 --std=gnu99 -pedantic -Wall main.c xslaves.c devnodes.c -o devnodes -lX11 -lXi
clean:
rm -f devnodes

View File

@ -1,13 +1,11 @@
/* /*
* Loosely based on a mix of: * Loosely based on a mix of:
* - xorg-xinput * - xorg-xinput
* https://github.com/freedesktop/xorg-xinput/blob/master/src/property.c::print_property_xi2 * https://github.com/freedesktop/xorg-xinput/blob/master/src/property.c::print_property_xi2
* - xkbcat * - xkbcat
* https://github.com/anko/xkbcat * https://github.com/anko/xkbcat
*/ */
#define _GNU_SOURCE
#include "devnodes.h" #include "devnodes.h"
#include <stdlib.h> #include <stdlib.h>
@ -18,6 +16,7 @@
#include <X11/extensions/XInput2.h> #include <X11/extensions/XInput2.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <X11/Xlib.h>
char* get_property_xi2(Display* dpy, int deviceid, Atom property, const char* property_name) char* get_property_xi2(Display* dpy, int deviceid, Atom property, const char* property_name)
@ -93,8 +92,8 @@ XSlaves* build_slave_info(Display* disp)
char* devnode = get_property_xi2(disp, current_slave->deviceid, props[nprops], "Device Node"); char* devnode = get_property_xi2(disp, current_slave->deviceid, props[nprops], "Device Node");
if (current_slave->name != NULL && devnode != NULL) if (current_slave->name != NULL && devnode != NULL)
{ {
int success = add_slaveinfo(slaves, current_slave->name, devnode); int err = add_slaveinfo(slaves, current_slave->name, devnode);
if (success != 0) if (err)
fail = true; fail = true;
} }
free(devnode); free(devnode);
@ -103,6 +102,7 @@ XSlaves* build_slave_info(Display* disp)
if (fail) if (fail)
{ {
free_xslaves(slaves); free_xslaves(slaves);
slaves = NULL;
break; break;
} }
@ -116,73 +116,48 @@ int is_xinput2_available(Display* disp)
int xiOpcode; int xiOpcode;
int queryEvent, queryError; int queryEvent, queryError;
if (! XQueryExtension(disp, "XInputExtension", &xiOpcode, &queryEvent, &queryError)) if (! XQueryExtension(disp, "XInputExtension", &xiOpcode, &queryEvent, &queryError))
{ return XINPUTEXTUNAVAIL;
fprintf(stderr, "XInput extension not available\n");
return -1;
}
int major = 2, minor = 0; int major = 2, minor = 0;
int queryResult = XIQueryVersion(disp, &major, &minor); int queryResult = XIQueryVersion(disp, &major, &minor);
if (queryResult == BadRequest) if (queryResult == BadRequest)
{ return XINPUT20UNAVAIL;
fprintf(stderr, "XInput 2.0 support required (got %d.%d)\n", major, minor);
return -2;
}
else if (queryResult != Success) else if (queryResult != Success)
{ return XINPUTQUERYVERSIONFAILED;
fprintf(stderr, "XIQueryVersion failed\n");
return -3;
}
return 0; return 0;
} }
Display* connect_to_x(const char* xDisplayName) Display* connect_to_x(const char* xDisplayName, int* error)
{ {
Display* disp = XOpenDisplay(xDisplayName); Display* disp = XOpenDisplay(xDisplayName);
if (NULL == disp) if (disp == NULL)
{ {
fprintf(stderr, "Cannot open X display '%s'\n", xDisplayName); *error = BADDISPLAY;
return NULL; return NULL;
} }
if (is_xinput2_available(disp) != 0) int err = is_xinput2_available(disp);
if (err)
{ {
XCloseDisplay(disp); XCloseDisplay(disp);
*error = err;
return NULL; return NULL;
} }
return disp; return disp;
} }
XSlaves* get_slave_info() XSlaves* get_slave_info(const char* xDisplayName, int* error)
{ {
char* xDisplayName = getenv("DISPLAY"); Display* disp = connect_to_x(xDisplayName, error);
if (xDisplayName == NULL) if (*error)
{ return NULL;
fprintf(stderr, "DISPLAY environment variable not found\n");
exit(1);
}
Display* disp = connect_to_x(xDisplayName);
if (disp == NULL)
exit(1);
XSlaves* s = build_slave_info(disp); XSlaves* s = build_slave_info(disp);
if (s == NULL)
*error = XSLAVEINFOQUERYFAILED;
XCloseDisplay(disp); XCloseDisplay(disp);
return s; return s;
} }
// int main(int argc, char * argv[])
// {
// XSlaves* s = get_slave_info();
// for (int i = 0; i < s->length; i++)
// {
// XSlaveInfo si = s->slaves[i];
// printf("Name: %s\n", si.name);
// printf("Node: %s\n", si.dev_node);
// printf("\n");
// }
// free_xslaves(s);
// }

View File

@ -3,4 +3,11 @@
#include "xslaves.h" #include "xslaves.h"
XSlaves* get_slave_info(); #define BADDISPLAY -3
#define XINPUTEXTUNAVAIL -4
#define XINPUT20UNAVAIL -5
#define XINPUTQUERYVERSIONFAILED -6
#define XSLAVEINFOQUERYFAILED -7
XSlaves* get_slave_info(const char* xDisplayName, int* error);

View File

@ -1,16 +0,0 @@
#include "devnodes.h"
#include "xslaves.h"
#include <stdio.h>
int main(int argc, char** argv) {
XSlaves* xs = get_slave_info();
for (int i=0; i < xs->length; i++)
{
XSlaveInfo si = xs->slaves[i];
printf("%s = %s\n", si.name, si.dev_node);
}
free_xslaves(xs);
}

View File

@ -10,6 +10,8 @@ import (
"fmt" "fmt"
"unsafe" "unsafe"
"regexp" "regexp"
"errors"
"os"
) )
@ -39,7 +41,15 @@ func Print() {
func GetXSlaveInfo() ([]C.XSlaveInfo, func()) { func GetXSlaveInfo() ([]C.XSlaveInfo, func()) {
xslaves := C.get_slave_info() xDisplayName := C.CString(tryGetXDisplay())
defer C.free(unsafe.Pointer(xDisplayName))
var error C.int
xslaves := C.get_slave_info(xDisplayName, &error)
if (error != C.int(0)) {
panic(convertErrorIntToGoError(error))
}
arr := (*[1 << 30]C.XSlaveInfo)(unsafe.Pointer(xslaves.slaves)) arr := (*[1 << 30]C.XSlaveInfo)(unsafe.Pointer(xslaves.slaves))
slice := arr[:xslaves.length:xslaves.length] slice := arr[:xslaves.length:xslaves.length]
freeMethod := func() { freeMethod := func() {
@ -49,3 +59,33 @@ func GetXSlaveInfo() ([]C.XSlaveInfo, func()) {
return slice, freeMethod return slice, freeMethod
} }
func tryGetXDisplay() string {
display := os.Getenv("DISPLAY")
if display == "" {
panic(errors.New("DISPLAY envvar not set"))
}
return display
}
func convertErrorIntToGoError(err C.int) error {
errorMsg := ""
switch err {
case C.MALLOCFAIL:
errorMsg = "Memory allocation failed"
case C.XSLAVESFULL:
errorMsg = "XSlaves full"
case C.BADDISPLAY:
errorMsg = "Failed to open X display"
case C.XINPUTEXTUNAVAIL:
errorMsg = "XInput extension unavailable"
case C.XINPUT20UNAVAIL:
errorMsg = "XInput extension is not 2.0"
case C.XINPUTQUERYVERSIONFAILED:
errorMsg = "Failed to query XInput extension version"
case C.XSLAVEINFOQUERYFAILED:
errorMsg = "Failed to query XInput slaves"
default:
errorMsg = fmt.Sprintf("Unknown error from devnodes.h: %v", err)
}
return errors.New(errorMsg)
}

View File

@ -45,11 +45,18 @@ int add_slaveinfo(XSlaves* slaves, char* name, char* dev_node)
XSlaveInfo si = new_slave_info(); XSlaveInfo si = new_slave_info();
si.name = strdup(name); si.name = strdup(name);
si.dev_node = strdup(dev_node); si.dev_node = strdup(dev_node);
if (si.name == NULL || si.dev_node == NULL)
{
free(si.name);
free(si.dev_node);
return MALLOCFAIL;
}
if (slaves->length < slaves->capacity) if (slaves->length < slaves->capacity)
{ {
slaves->slaves[slaves->length] = si; slaves->slaves[slaves->length] = si;
slaves->length++; slaves->length++;
return 0; return 0;
} }
return -1; return XSLAVESFULL;
} }

View File

@ -3,6 +3,10 @@
#include <stddef.h> #include <stddef.h>
#define MALLOCFAIL -1
#define XSLAVESFULL -2
typedef struct XSlaveInfo typedef struct XSlaveInfo
{ {
char* name; char* name;