Improve hacky XLib code

This commit is contained in:
Kristóf Tóth 2021-10-16 22:37:23 +02:00
parent bae3b95b6c
commit 86ed0f7989
2 changed files with 139 additions and 58 deletions

View File

@ -1,5 +1,5 @@
get-x-slave-devnodes: get-x-slave-devnodes.c get-x-slave-devnodes: get-x-slave-devnodes.c
$(CC) -O2 --std=c99 -pedantic -Wall get-x-slave-devnodes.c -o get-x-slave-devnodes -lX11 -lXi $(CC) -O2 --std=gnu99 -pedantic -Wall get-x-slave-devnodes.c -o get-x-slave-devnodes -lX11 -lXi
clean: clean:
rm -f get-x-slave-devnodes rm -f get-x-slave-devnodes

View File

@ -6,6 +6,8 @@
* https://github.com/anko/xkbcat * https://github.com/anko/xkbcat
*/ */
#define _GNU_SOURCE
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -28,7 +30,7 @@ char* get_property_xi2(Display* dpy, int deviceid, Atom property, const char* pr
char* ret = NULL; char* ret = NULL;
name = XGetAtomName(dpy, property); name = XGetAtomName(dpy, property);
int interested_in_property = (strcmp(name, property_name) == 0); bool interested_in_property = (strcmp(name, property_name) == 0);
XFree(name); XFree(name);
if (!interested_in_property) if (!interested_in_property)
return ret; return ret;
@ -51,22 +53,18 @@ char* get_property_xi2(Display* dpy, int deviceid, Atom property, const char* pr
} }
int len = strlen((char*)ptr); int len = strlen((char*)ptr);
ret = (char*)malloc(len+1); ret = strdup((const char*)ptr);
strcpy(ret, (const char*)ptr);
j += len; /* The loop's j++ jumps over the j += len; // The loop's j++ jumps over the terminating 0
terminating 0 */ ptr += len; // ptr += size below jumps over the terminating 0
ptr += len; /* ptr += size below jumps over
the terminating 0 */
break; break;
default: default:
// Unknown type // Unknown type
done = True; done = True;
break; break;
} }
ptr += act_format/8; ptr += act_format/8;
if (done == True) if (done == True)
break; break;
} }
@ -75,65 +73,145 @@ char* get_property_xi2(Display* dpy, int deviceid, Atom property, const char* pr
return ret; return ret;
} }
int is_xinput2_available(Display* disp)
void assert_xinput2(Display* disp)
{ {
int xiOpcode; int xiOpcode;
{ // Test for XInput 2 extension int queryEvent, queryError;
int queryEvent, queryError; if (! XQueryExtension(disp, "XInputExtension", &xiOpcode, &queryEvent, &queryError))
if (! XQueryExtension(disp, "XInputExtension", &xiOpcode, &queryEvent, &queryError)) {
{ fprintf(stderr, "XInput extension not available\n");
fprintf(stderr, "X Input extension not available\n"); return -1;
exit(2);
}
} }
{ // Request XInput 2.0, to guard against changes in future versions
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)
{ {
fprintf(stderr, "Need XI 2.0 support (got %d.%d)\n", major, minor); fprintf(stderr, "XInput 2.0 support required (got %d.%d)\n", major, minor);
exit(3); return -2;
} }
else if (queryResult != Success) else if (queryResult != Success)
{ {
fprintf(stderr, "XIQueryVersion failed!\n"); fprintf(stderr, "XIQueryVersion failed\n");
exit(4); return -3;
} }
return 0;
}
typedef struct SlaveInfo
{
char* slave_names;
char* dev_nodes;
} SlaveInfo;
SlaveInfo new_slave_info()
{
SlaveInfo si;
si.slave_names = NULL;
si.dev_nodes = NULL;
return si;
}
void free_slave_info(SlaveInfo si)
{
free(si.slave_names);
free(si.dev_nodes);
si.slave_names = NULL;
si.dev_nodes = NULL;
}
bool is_empty_slave_info(SlaveInfo si)
{
return (si.slave_names == NULL || si.dev_nodes == NULL);
}
void replace(char* str, char old, char new)
{
int i = 0;
while (str[i] != '\0')
{
if (str[i] == old)
str[i] = new;
i++;
} }
} }
int append_as_line(char** dst, char* line)
void print_device_nodes(Display* disp)
{ {
XIDeviceInfo* all_slaves; uint len_newstr = strlen(line) + 2;
XIDeviceInfo* current_slave; if (*dst != NULL)
int num_slaves; len_newstr += strlen(*dst);
char* newstr = (char*)malloc(len_newstr);
if (newstr == NULL)
return -1;
newstr[0] = '\0';
replace(line, '\n', ' ');
if (*dst != NULL)
strcat(newstr, *dst);
strcat(newstr, line);
strcat(newstr, "\n");
free(*dst);
*dst = newstr;
return 0;
}
SlaveInfo get_slave_info(Display* disp)
{
int num_slaves;
XIDeviceInfo* all_slaves = XIQueryDevice(disp, XIAllDevices, &num_slaves);
SlaveInfo slaveinfo = new_slave_info();
bool fail = false;
all_slaves = XIQueryDevice(disp, XIAllDevices, &num_slaves);
for (int i = 0; i < num_slaves; i++) for (int i = 0; i < num_slaves; i++)
{ {
current_slave = &all_slaves[i]; XIDeviceInfo* current_slave = &all_slaves[i];
int nprops; int nprops;
Atom* props; Atom* props = XIListProperties(disp, current_slave->deviceid, &nprops);
props = XIListProperties(disp, current_slave->deviceid, &nprops);
if (!nprops)
printf("Device '%s' does not report any properties.\n", current_slave->name);
while(nprops--) while(nprops-- && !fail)
{ {
char* property = 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 (property != NULL) if (current_slave->name != NULL && devnode != NULL)
{ {
printf("%s=%s\n", current_slave->name, property); int append_slave_success = append_as_line(&slaveinfo.slave_names, current_slave->name);
free(property); int append_devnode_success = append_as_line(&slaveinfo.dev_nodes, devnode);
if (append_slave_success != 0 || append_devnode_success != 0)
fail = true;
} }
free(devnode);
} }
XFree(props); XFree(props);
} if (fail)
{
free_slave_info(slaveinfo);
break;
}
}
XIFreeDeviceInfo(all_slaves); XIFreeDeviceInfo(all_slaves);
return slaveinfo;
}
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;
} }
@ -142,19 +220,22 @@ int main(int argc, char * argv[])
char* xDisplayName = getenv("DISPLAY"); char* xDisplayName = getenv("DISPLAY");
if (xDisplayName == NULL) if (xDisplayName == NULL)
{ {
printf("DISPLAY environment variable not found\n"); fprintf(stderr, "DISPLAY environment variable not found\n");
exit(1); exit(1);
} }
// Connect to X display Display* disp = connect_to_x(xDisplayName);
Display* disp = XOpenDisplay(xDisplayName); if (disp == NULL)
if (NULL == disp) exit(1);
SlaveInfo si = get_slave_info(disp);
if (!is_empty_slave_info(si))
{ {
fprintf(stderr, "Cannot open X display '%s'\n", xDisplayName); printf("%s", si.slave_names);
exit(1); printf("%s", si.dev_nodes);
} }
assert_xinput2(disp);
print_device_nodes(disp); free_slave_info(si);
XCloseDisplay(disp);
exit(0); exit(0);
} }