Further improve XLib code and include go code capable of calling it

This commit is contained in:
2021-10-22 15:15:11 +02:00
parent 0729d9a9ee
commit 9ca48b7bf8
8 changed files with 215 additions and 115 deletions

View 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

View 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);
// }

View File

@ -0,0 +1,6 @@
#pragma once
#include "xslaves.h"
XSlaves* get_slave_info();

View 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);
}

View 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
}

View 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;
}

View 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);