/* * 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 */ #include #include #include #include #include #include #include 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); int 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 = (char*)malloc(len+1); strcpy(ret, (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; } void assert_xinput2(Display* disp) { int xiOpcode; { // Test for XInput 2 extension int queryEvent, queryError; if (! XQueryExtension(disp, "XInputExtension", &xiOpcode, &queryEvent, &queryError)) { fprintf(stderr, "X Input extension not available\n"); exit(2); } } { // Request XInput 2.0, to guard against changes in future versions int major = 2, minor = 0; int queryResult = XIQueryVersion(disp, &major, &minor); if (queryResult == BadRequest) { fprintf(stderr, "Need XI 2.0 support (got %d.%d)\n", major, minor); exit(3); } else if (queryResult != Success) { fprintf(stderr, "XIQueryVersion failed!\n"); exit(4); } } } void print_device_nodes(Display* disp) { XIDeviceInfo* all_slaves; XIDeviceInfo* current_slave; int num_slaves; all_slaves = XIQueryDevice(disp, XIAllDevices, &num_slaves); for (int i = 0; i < num_slaves; i++) { current_slave = &all_slaves[i]; int nprops; Atom* props; props = XIListProperties(disp, current_slave->deviceid, &nprops); if (!nprops) printf("Device '%s' does not report any properties.\n", current_slave->name); while(nprops--) { char* property = get_property_xi2(disp, current_slave->deviceid, props[nprops], "Device Node"); if (property != NULL) { printf("%s=%s\n", current_slave->name, property); free(property); } } XFree(props); } XIFreeDeviceInfo(all_slaves); } int main(int argc, char * argv[]) { char* xDisplayName = getenv("DISPLAY"); if (xDisplayName == NULL) { printf("DISPLAY environment variable not found\n"); exit(1); } // Connect to X display Display* disp = XOpenDisplay(xDisplayName); if (NULL == disp) { fprintf(stderr, "Cannot open X display '%s'\n", xDisplayName); exit(1); } assert_xinput2(disp); print_device_nodes(disp); exit(0); }