kde-lockscreen-suspend-display/xdg/xinput_devnodes/devnodes.c

164 lines
4.0 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
*/
#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>
#include <X11/Xlib.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 err = add_slaveinfo(slaves, current_slave->name, devnode);
if (err)
fail = true;
}
free(devnode);
}
XFree(props);
if (fail)
{
free_xslaves(slaves);
slaves = NULL;
break;
}
}
XIFreeDeviceInfo(all_slaves);
return slaves;
}
int is_xinput2_available(Display* disp)
{
int xiOpcode;
int queryEvent, queryError;
if (! XQueryExtension(disp, "XInputExtension", &xiOpcode, &queryEvent, &queryError))
return XINPUTEXTUNAVAIL;
int major = 2, minor = 0;
int queryResult = XIQueryVersion(disp, &major, &minor);
if (queryResult == BadRequest)
return XINPUT20UNAVAIL;
else if (queryResult != Success)
return XINPUTQUERYVERSIONFAILED;
return 0;
}
Display* connect_to_x(const char* xDisplayName, int* error)
{
Display* disp = XOpenDisplay(xDisplayName);
if (disp == NULL)
{
*error = BADDISPLAY;
return NULL;
}
int err = is_xinput2_available(disp);
if (err)
{
XCloseDisplay(disp);
*error = err;
return NULL;
}
return disp;
}
XSlaves* get_slave_info(const char* xDisplayName, int* error)
{
Display* disp = connect_to_x(xDisplayName, error);
if (*error)
return NULL;
XSlaves* s = build_slave_info(disp);
if (s == NULL)
*error = XSLAVEINFOQUERYFAILED;
XCloseDisplay(disp);
return s;
}