Improve XLib-XInput error handling
This commit is contained in:
		@@ -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
 | 
			
		||||
 | 
			
		||||
@@ -6,8 +6,6 @@
 | 
			
		||||
*     https://github.com/anko/xkbcat
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
 | 
			
		||||
#include "devnodes.h"
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
@@ -18,6 +16,7 @@
 | 
			
		||||
 | 
			
		||||
#include <X11/extensions/XInput2.h>
 | 
			
		||||
#include <X11/Xatom.h>
 | 
			
		||||
#include <X11/Xlib.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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");
 | 
			
		||||
            if (current_slave->name != NULL && devnode != NULL)
 | 
			
		||||
            {
 | 
			
		||||
                int success = add_slaveinfo(slaves, current_slave->name, devnode);
 | 
			
		||||
                if (success != 0)
 | 
			
		||||
                int err = add_slaveinfo(slaves, current_slave->name, devnode);
 | 
			
		||||
                if (err)
 | 
			
		||||
                    fail = true;
 | 
			
		||||
            }
 | 
			
		||||
            free(devnode);
 | 
			
		||||
@@ -103,6 +102,7 @@ XSlaves* build_slave_info(Display* disp)
 | 
			
		||||
        if (fail)
 | 
			
		||||
        {
 | 
			
		||||
            free_xslaves(slaves);
 | 
			
		||||
            slaves = NULL;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -116,73 +116,48 @@ int is_xinput2_available(Display* disp)
 | 
			
		||||
    int xiOpcode;
 | 
			
		||||
    int queryEvent, queryError;
 | 
			
		||||
    if (! XQueryExtension(disp, "XInputExtension", &xiOpcode, &queryEvent, &queryError))
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "XInput extension not available\n");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
        return XINPUTEXTUNAVAIL;
 | 
			
		||||
 | 
			
		||||
    int major = 2, minor = 0;
 | 
			
		||||
    int queryResult = XIQueryVersion(disp, &major, &minor);
 | 
			
		||||
    if (queryResult == BadRequest)
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "XInput 2.0 support required (got %d.%d)\n", major, minor);
 | 
			
		||||
        return -2;
 | 
			
		||||
    }
 | 
			
		||||
        return XINPUT20UNAVAIL;
 | 
			
		||||
    else if (queryResult != Success)
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "XIQueryVersion failed\n");
 | 
			
		||||
        return -3;
 | 
			
		||||
    }
 | 
			
		||||
        return XINPUTQUERYVERSIONFAILED;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Display* connect_to_x(const char* xDisplayName)
 | 
			
		||||
Display* connect_to_x(const char* xDisplayName, int* error)
 | 
			
		||||
{
 | 
			
		||||
    Display* disp = XOpenDisplay(xDisplayName);
 | 
			
		||||
    if (NULL == disp)
 | 
			
		||||
    if (disp == NULL)
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "Cannot open X display '%s'\n", xDisplayName);
 | 
			
		||||
        *error = BADDISPLAY;
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (is_xinput2_available(disp) != 0)
 | 
			
		||||
    int err = is_xinput2_available(disp);
 | 
			
		||||
    if (err)
 | 
			
		||||
    {
 | 
			
		||||
        XCloseDisplay(disp);
 | 
			
		||||
        *error = err;
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return disp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
XSlaves* get_slave_info()
 | 
			
		||||
XSlaves* get_slave_info(const char* xDisplayName, int* error)
 | 
			
		||||
{
 | 
			
		||||
    char* xDisplayName = getenv("DISPLAY");
 | 
			
		||||
    if (xDisplayName == NULL)
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "DISPLAY environment variable not found\n");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Display* disp = connect_to_x(xDisplayName);
 | 
			
		||||
    if (disp == NULL)
 | 
			
		||||
        exit(1);
 | 
			
		||||
    Display* disp = connect_to_x(xDisplayName, error);
 | 
			
		||||
    if (*error)
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
    XSlaves* s = build_slave_info(disp);
 | 
			
		||||
    if (s == NULL)
 | 
			
		||||
        *error = XSLAVEINFOQUERYFAILED;
 | 
			
		||||
 | 
			
		||||
    XCloseDisplay(disp);
 | 
			
		||||
    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);
 | 
			
		||||
// }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,4 +3,11 @@
 | 
			
		||||
#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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
@@ -10,6 +10,8 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -39,7 +41,15 @@ func Print() {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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))
 | 
			
		||||
	slice := arr[:xslaves.length:xslaves.length]
 | 
			
		||||
	freeMethod := func() {
 | 
			
		||||
@@ -49,3 +59,33 @@ func GetXSlaveInfo() ([]C.XSlaveInfo, func()) {
 | 
			
		||||
	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)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -45,11 +45,18 @@ int add_slaveinfo(XSlaves* slaves, char* name, char* dev_node)
 | 
			
		||||
    XSlaveInfo si = new_slave_info();
 | 
			
		||||
    si.name = strdup(name);
 | 
			
		||||
    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)
 | 
			
		||||
    {
 | 
			
		||||
        slaves->slaves[slaves->length] = si;
 | 
			
		||||
        slaves->length++;
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
    return XSLAVESFULL;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,10 @@
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define MALLOCFAIL -1
 | 
			
		||||
#define XSLAVESFULL -2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef struct XSlaveInfo
 | 
			
		||||
{
 | 
			
		||||
    char* name;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user