189 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			189 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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);
 | |
| // }
 |