2021-10-15 19:04:03 +00:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2021-10-16 20:37:23 +00:00
|
|
|
#define _GNU_SOURCE
|
|
|
|
|
2021-10-22 13:15:11 +00:00
|
|
|
#include "devnodes.h"
|
|
|
|
|
2021-10-15 19:04:03 +00:00
|
|
|
#include <stdlib.h>
|
2021-10-22 13:15:11 +00:00
|
|
|
#include <stdio.h>
|
2021-10-15 19:04:03 +00:00
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdbool.h>
|
2021-10-22 13:15:11 +00:00
|
|
|
#include <string.h>
|
2021-10-15 19:04:03 +00:00
|
|
|
|
|
|
|
#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);
|
2021-10-16 20:37:23 +00:00
|
|
|
bool interested_in_property = (strcmp(name, property_name) == 0);
|
2021-10-15 19:04:03 +00:00
|
|
|
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);
|
2021-10-16 20:37:23 +00:00
|
|
|
ret = strdup((const char*)ptr);
|
2021-10-15 19:04:03 +00:00
|
|
|
|
2021-10-16 20:37:23 +00:00
|
|
|
j += len; // The loop's j++ jumps over the terminating 0
|
|
|
|
ptr += len; // ptr += size below jumps over the terminating 0
|
2021-10-15 19:04:03 +00:00
|
|
|
break;
|
2021-10-16 20:37:23 +00:00
|
|
|
|
2021-10-15 19:04:03 +00:00
|
|
|
default:
|
|
|
|
// Unknown type
|
|
|
|
done = True;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ptr += act_format/8;
|
|
|
|
if (done == True)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
XFree(data);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-10-22 13:15:11 +00:00
|
|
|
XSlaves* build_slave_info(Display* disp)
|
2021-10-15 19:04:03 +00:00
|
|
|
{
|
|
|
|
int num_slaves;
|
2021-10-16 20:37:23 +00:00
|
|
|
XIDeviceInfo* all_slaves = XIQueryDevice(disp, XIAllDevices, &num_slaves);
|
2021-10-22 13:15:11 +00:00
|
|
|
XSlaves* slaves = new_xslaves(num_slaves);
|
2021-10-16 20:37:23 +00:00
|
|
|
bool fail = false;
|
2021-10-15 19:04:03 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < num_slaves; i++)
|
|
|
|
{
|
2021-10-16 20:37:23 +00:00
|
|
|
XIDeviceInfo* current_slave = &all_slaves[i];
|
2021-10-15 19:04:03 +00:00
|
|
|
int nprops;
|
2021-10-16 20:37:23 +00:00
|
|
|
Atom* props = XIListProperties(disp, current_slave->deviceid, &nprops);
|
2021-10-15 19:04:03 +00:00
|
|
|
|
2021-10-16 20:37:23 +00:00
|
|
|
while(nprops-- && !fail)
|
2021-10-15 19:04:03 +00:00
|
|
|
{
|
2021-10-16 20:37:23 +00:00
|
|
|
char* devnode = get_property_xi2(disp, current_slave->deviceid, props[nprops], "Device Node");
|
|
|
|
if (current_slave->name != NULL && devnode != NULL)
|
2021-10-15 19:04:03 +00:00
|
|
|
{
|
2021-10-22 13:15:11 +00:00
|
|
|
int success = add_slaveinfo(slaves, current_slave->name, devnode);
|
|
|
|
if (success != 0)
|
2021-10-16 20:37:23 +00:00
|
|
|
fail = true;
|
2021-10-15 19:04:03 +00:00
|
|
|
}
|
2021-10-16 20:37:23 +00:00
|
|
|
free(devnode);
|
2021-10-15 19:04:03 +00:00
|
|
|
}
|
|
|
|
XFree(props);
|
2021-10-16 20:37:23 +00:00
|
|
|
if (fail)
|
|
|
|
{
|
2021-10-22 13:15:11 +00:00
|
|
|
free_xslaves(slaves);
|
2021-10-16 20:37:23 +00:00
|
|
|
break;
|
|
|
|
}
|
2021-10-15 19:04:03 +00:00
|
|
|
|
2021-10-16 20:37:23 +00:00
|
|
|
}
|
2021-10-15 19:04:03 +00:00
|
|
|
XIFreeDeviceInfo(all_slaves);
|
2021-10-22 13:15:11 +00:00
|
|
|
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;
|
2021-10-16 20:37:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2021-10-15 19:04:03 +00:00
|
|
|
}
|
|
|
|
|
2021-10-22 13:15:11 +00:00
|
|
|
XSlaves* get_slave_info()
|
2021-10-15 19:04:03 +00:00
|
|
|
{
|
|
|
|
char* xDisplayName = getenv("DISPLAY");
|
|
|
|
if (xDisplayName == NULL)
|
|
|
|
{
|
2021-10-16 20:37:23 +00:00
|
|
|
fprintf(stderr, "DISPLAY environment variable not found\n");
|
2021-10-15 19:04:03 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2021-10-16 20:37:23 +00:00
|
|
|
Display* disp = connect_to_x(xDisplayName);
|
|
|
|
if (disp == NULL)
|
2021-10-15 19:04:03 +00:00
|
|
|
exit(1);
|
2021-10-16 20:37:23 +00:00
|
|
|
|
2021-10-22 13:15:11 +00:00
|
|
|
XSlaves* s = build_slave_info(disp);
|
2021-10-16 20:37:23 +00:00
|
|
|
XCloseDisplay(disp);
|
2021-10-22 13:15:11 +00:00
|
|
|
return s;
|
2021-10-15 19:04:03 +00:00
|
|
|
}
|
2021-10-22 13:15:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
// }
|