Further improve XLib code and include go code capable of calling it
This commit is contained in:
		
							
								
								
									
										5
									
								
								xdg/xinput_devnodes/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								xdg/xinput_devnodes/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
main: main.c
 | 
			
		||||
	$(CC) -O2 --std=gnu99 -pedantic -Wall main.c xslaves.c devnodes.c -o devnodes -lX11 -lXi
 | 
			
		||||
clean:
 | 
			
		||||
	rm -f devnodes
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										188
									
								
								xdg/xinput_devnodes/devnodes.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								xdg/xinput_devnodes/devnodes.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,188 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Loosely based on a mix of:
 | 
			
		||||
 *   - xorg-xinput
 | 
			
		||||
 *     https://github.com/freedesktop/xorg-xinput/blob/master/src/property.c::print_property_xi2
 | 
			
		||||
 *   - xkbcat
 | 
			
		||||
 *     https://github.com/anko/xkbcat
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
 | 
			
		||||
#include "devnodes.h"
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <X11/extensions/XInput2.h>
 | 
			
		||||
#include <X11/Xatom.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
char* get_property_xi2(Display* dpy, int deviceid, Atom property, const char* property_name)
 | 
			
		||||
{
 | 
			
		||||
    Atom act_type;
 | 
			
		||||
    char* name;
 | 
			
		||||
    int act_format;
 | 
			
		||||
    unsigned long nitems, bytes_after;
 | 
			
		||||
    unsigned char* data;
 | 
			
		||||
    unsigned char* ptr;
 | 
			
		||||
    int j, done = False;
 | 
			
		||||
    char* ret = NULL;
 | 
			
		||||
 | 
			
		||||
    name = XGetAtomName(dpy, property);
 | 
			
		||||
    bool interested_in_property = (strcmp(name, property_name) == 0);
 | 
			
		||||
    XFree(name);
 | 
			
		||||
    if (!interested_in_property)
 | 
			
		||||
        return ret;
 | 
			
		||||
 | 
			
		||||
    if (XIGetProperty(dpy, deviceid, property, 0, 1000, False,
 | 
			
		||||
                           AnyPropertyType, &act_type, &act_format,
 | 
			
		||||
                           &nitems, &bytes_after, &data) == Success)
 | 
			
		||||
    {
 | 
			
		||||
        ptr = data;
 | 
			
		||||
        for (j = 0; j < nitems; j++)
 | 
			
		||||
        {
 | 
			
		||||
            switch(act_type)
 | 
			
		||||
            {
 | 
			
		||||
                case XA_STRING:
 | 
			
		||||
                    if (act_format != 8)
 | 
			
		||||
                    {
 | 
			
		||||
                        //Unknown string format
 | 
			
		||||
                        done = True;
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    int len = strlen((char*)ptr);
 | 
			
		||||
                    ret = strdup((const char*)ptr);
 | 
			
		||||
 | 
			
		||||
                    j += len;   // The loop's j++ jumps over the terminating 0
 | 
			
		||||
                    ptr += len; // ptr += size below jumps over the terminating 0
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                default:
 | 
			
		||||
                    // Unknown type
 | 
			
		||||
                    done = True;
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            ptr += act_format/8;
 | 
			
		||||
            if (done == True)
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        XFree(data);
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
XSlaves* build_slave_info(Display* disp)
 | 
			
		||||
{
 | 
			
		||||
	int num_slaves;
 | 
			
		||||
	XIDeviceInfo* all_slaves = XIQueryDevice(disp, XIAllDevices, &num_slaves);
 | 
			
		||||
    XSlaves* slaves = new_xslaves(num_slaves);
 | 
			
		||||
    bool fail = false;
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < num_slaves; i++)
 | 
			
		||||
	{
 | 
			
		||||
		XIDeviceInfo* current_slave = &all_slaves[i];
 | 
			
		||||
        int nprops;
 | 
			
		||||
        Atom* props = XIListProperties(disp, current_slave->deviceid, &nprops);
 | 
			
		||||
 | 
			
		||||
        while(nprops-- && !fail)
 | 
			
		||||
        {
 | 
			
		||||
            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)
 | 
			
		||||
                    fail = true;
 | 
			
		||||
            }
 | 
			
		||||
            free(devnode);
 | 
			
		||||
        }
 | 
			
		||||
        XFree(props);
 | 
			
		||||
        if (fail)
 | 
			
		||||
        {
 | 
			
		||||
            free_xslaves(slaves);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
	XIFreeDeviceInfo(all_slaves);
 | 
			
		||||
    return slaves;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
    else if (queryResult != Success)
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "XIQueryVersion failed\n");
 | 
			
		||||
        return -3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Display* connect_to_x(const char* xDisplayName)
 | 
			
		||||
{
 | 
			
		||||
    Display* disp = XOpenDisplay(xDisplayName);
 | 
			
		||||
    if (NULL == disp)
 | 
			
		||||
    {
 | 
			
		||||
        fprintf(stderr, "Cannot open X display '%s'\n", xDisplayName);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (is_xinput2_available(disp) != 0)
 | 
			
		||||
    {
 | 
			
		||||
        XCloseDisplay(disp);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return disp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
XSlaves* get_slave_info()
 | 
			
		||||
{
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    XSlaves* s = build_slave_info(disp);
 | 
			
		||||
    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);
 | 
			
		||||
// }
 | 
			
		||||
							
								
								
									
										6
									
								
								xdg/xinput_devnodes/devnodes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								xdg/xinput_devnodes/devnodes.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "xslaves.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
XSlaves* get_slave_info();
 | 
			
		||||
							
								
								
									
										16
									
								
								xdg/xinput_devnodes/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								xdg/xinput_devnodes/main.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
#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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										51
									
								
								xdg/xinput_devnodes/xinput_devnodes.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								xdg/xinput_devnodes/xinput_devnodes.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
package xinput_devnodes
 | 
			
		||||
 | 
			
		||||
// #cgo CFLAGS: -g --std=gnu99 -pedantic -Wall
 | 
			
		||||
// #cgo LDFLAGS: -lX11 -lXi 
 | 
			
		||||
// #include "devnodes.h"
 | 
			
		||||
// #include <stdlib.h>
 | 
			
		||||
// #include <string.h>
 | 
			
		||||
import "C"
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
	"regexp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func FindKeyboardDevNode() string {
 | 
			
		||||
	xslaveinfos, freeMethod := GetXSlaveInfo()
 | 
			
		||||
	defer freeMethod()
 | 
			
		||||
 | 
			
		||||
	re := regexp.MustCompile(`(?i).*keyboard.*`)
 | 
			
		||||
	for _, xslaveinfo := range xslaveinfos {
 | 
			
		||||
		name, devNode := C.GoString(xslaveinfo.name), C.GoString(xslaveinfo.dev_node)
 | 
			
		||||
		if re.MatchString(name) {
 | 
			
		||||
			return devNode
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func Print() {
 | 
			
		||||
	xslaveinfos, freeMethod := GetXSlaveInfo()
 | 
			
		||||
	defer freeMethod()
 | 
			
		||||
 | 
			
		||||
	for _, xslaveinfo := range xslaveinfos {
 | 
			
		||||
		fmt.Printf("%s = %s\n", C.GoString(xslaveinfo.name), C.GoString(xslaveinfo.dev_node))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func GetXSlaveInfo() ([]C.XSlaveInfo, func()) {
 | 
			
		||||
	xslaves := C.get_slave_info()
 | 
			
		||||
	arr := (*[1 << 30]C.XSlaveInfo)(unsafe.Pointer(xslaves.slaves))
 | 
			
		||||
	slice := arr[:xslaves.length:xslaves.length]
 | 
			
		||||
	freeMethod := func() {
 | 
			
		||||
		C.free_xslaves(xslaves)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return slice, freeMethod
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										55
									
								
								xdg/xinput_devnodes/xslaves.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								xdg/xinput_devnodes/xslaves.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
#include "xslaves.h"
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
XSlaveInfo new_slave_info()
 | 
			
		||||
{
 | 
			
		||||
    XSlaveInfo si;
 | 
			
		||||
    si.name = NULL;
 | 
			
		||||
    si.dev_node = NULL;
 | 
			
		||||
    return si;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void free_slave_info(XSlaveInfo si)
 | 
			
		||||
{
 | 
			
		||||
    free(si.name);
 | 
			
		||||
    free(si.dev_node);
 | 
			
		||||
    si.name = NULL;
 | 
			
		||||
    si.dev_node = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
XSlaves* new_xslaves(size_t capacity)
 | 
			
		||||
{
 | 
			
		||||
    XSlaves* s = (XSlaves*)malloc(sizeof(XSlaves));
 | 
			
		||||
    s->capacity = capacity;
 | 
			
		||||
    s->length = 0;
 | 
			
		||||
    s->slaves = (XSlaveInfo*)malloc(capacity*sizeof(XSlaveInfo));
 | 
			
		||||
    return s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void free_xslaves(XSlaves* slaves)
 | 
			
		||||
{
 | 
			
		||||
    for (int i = 0; i < slaves->length; i++)
 | 
			
		||||
        free_slave_info(slaves->slaves[i]);
 | 
			
		||||
    free(slaves->slaves);
 | 
			
		||||
    slaves->slaves = NULL;
 | 
			
		||||
    slaves->length = 0;
 | 
			
		||||
    slaves->capacity = 0;
 | 
			
		||||
    free(slaves);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 (slaves->length < slaves->capacity)
 | 
			
		||||
    {
 | 
			
		||||
        slaves->slaves[slaves->length] = si;
 | 
			
		||||
        slaves->length++;
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								xdg/xinput_devnodes/xslaves.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								xdg/xinput_devnodes/xslaves.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef struct XSlaveInfo
 | 
			
		||||
{
 | 
			
		||||
    char* name;
 | 
			
		||||
    char* dev_node;
 | 
			
		||||
} XSlaveInfo;
 | 
			
		||||
 | 
			
		||||
typedef struct XSalves
 | 
			
		||||
{
 | 
			
		||||
    XSlaveInfo* slaves;
 | 
			
		||||
    size_t capacity;
 | 
			
		||||
    size_t length;
 | 
			
		||||
} XSlaves;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
XSlaveInfo new_slave_info();
 | 
			
		||||
void free_slave_info(XSlaveInfo si);
 | 
			
		||||
 | 
			
		||||
XSlaves* new_xslaves(size_t capacity);
 | 
			
		||||
void free_xslaves(XSlaves* slaves);
 | 
			
		||||
int add_slaveinfo(XSlaves* slaves, char* name, char* dev_node);
 | 
			
		||||
		Reference in New Issue
	
	Block a user