Skip to content

Commit e73cbed

Browse files
committed
xinput: add query_device support
1 parent 35fe44d commit e73cbed

1 file changed

Lines changed: 153 additions & 1 deletion

File tree

Xlib/ext/xinput.py

Lines changed: 153 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
A very incomplete implementation of the XInput extension.
2222
'''
2323

24-
import sys, array
24+
import sys, array, struct
2525

2626
from Xlib.protocol import rq
2727
from Xlib import X
@@ -98,6 +98,8 @@
9898
KeyClass = 0
9999
ButtonClass = 1
100100
ValuatorClass = 2
101+
ScrollClass = 3
102+
TouchClass = 8
101103

102104
KeyRepeat = (1 << 16)
103105

@@ -237,6 +239,155 @@ def select_events(self, event_masks):
237239
masks=masks,
238240
)
239241

242+
DeviceInfo = rq.Struct(
243+
DEVICEID('deviceid'),
244+
rq.Card16('use'),
245+
rq.Card16('attachment'),
246+
rq.Card16('num_classes'),
247+
rq.LengthOf('name', 2),
248+
rq.Bool('enabled'),
249+
rq.Pad(1),
250+
rq.String8('name', 4),
251+
# <num_classes> classes follow.
252+
)
253+
254+
AnyInfo = rq.Struct(
255+
rq.Card16('type'),
256+
rq.Card16('length'),
257+
rq.Card16('sourceid'),
258+
rq.Pad(2),
259+
)
260+
261+
ButtonInfo = rq.Struct(
262+
rq.Card16('type'),
263+
rq.Card16('length'),
264+
rq.Card16('sourceid'),
265+
rq.Card16('num_buttons'),
266+
# state mask and label atoms follow.
267+
)
268+
269+
KeyInfo = rq.Struct(
270+
rq.Card16('type'),
271+
rq.Card16('length'),
272+
rq.Card16('sourceid'),
273+
rq.LengthOf('keycodes', 2),
274+
rq.List('keycodes', rq.Card32),
275+
)
276+
277+
class FP3232(rq.ValueField):
278+
structcode = 'lL'
279+
structvalues = 2
280+
281+
def check_value(self, value):
282+
return value
283+
284+
def parse_value(self, value, display):
285+
integral, frac = value
286+
ret = float(integral)
287+
# optimised math.ldexp(float(frac), -32)
288+
ret += float(frac) * (1.0 / (1 << 32))
289+
return ret
290+
291+
ValuatorInfo = rq.Struct(
292+
rq.Card16('type'),
293+
rq.Card16('length'),
294+
rq.Card16('sourceid'),
295+
rq.Card16('number'),
296+
rq.Card32('label'),
297+
FP3232('min'),
298+
FP3232('max'),
299+
FP3232('value'),
300+
rq.Card32('resolution'),
301+
rq.Card8('mode'),
302+
rq.Pad(3),
303+
)
304+
305+
ScrollInfo = rq.Struct(
306+
rq.Card16('type'),
307+
rq.Card16('length'),
308+
rq.Card16('sourceid'),
309+
rq.Card16('number'),
310+
rq.Card16('scroll_type'),
311+
rq.Pad(2),
312+
rq.Card32('flags'),
313+
FP3232('increment'),
314+
)
315+
316+
TouchInfo = rq.Struct(
317+
rq.Card16('type'),
318+
rq.Card16('length'),
319+
rq.Card16('sourceid'),
320+
rq.Card8('mode'),
321+
rq.Card8('num_touches'),
322+
)
323+
324+
INFO_CLASSES = {
325+
KeyClass: KeyInfo,
326+
ButtonClass: ButtonInfo,
327+
ValuatorClass: ValuatorInfo,
328+
ScrollClass: ScrollInfo,
329+
TouchClass: TouchInfo,
330+
}
331+
332+
class XIQueryDevice(rq.ReplyRequest):
333+
_request = rq.Struct(
334+
rq.Card8('opcode'),
335+
rq.Opcode(48),
336+
rq.RequestLength(),
337+
DEVICEID('deviceid'),
338+
rq.Pad(2),
339+
)
340+
341+
_reply = rq.Struct(
342+
rq.ReplyCode(),
343+
rq.Pad(1),
344+
rq.Card16('sequence_number'),
345+
rq.ReplyLength(),
346+
rq.Card16('num_devices'),
347+
rq.Pad(22),
348+
)
349+
350+
def _parse_response(self, data):
351+
self._response_lock.acquire()
352+
self._data, d = self._reply.parse_binary(data, self._display)
353+
self._data.devices = devices = []
354+
for _ in range(self._data.num_devices):
355+
devinfo, d = DeviceInfo.parse_binary(d, self._display)
356+
devinfo.classes = classes = []
357+
for _ in range(devinfo.num_classes):
358+
class_type, length = struct.unpack('=HH', d[:4])
359+
class_struct = INFO_CLASSES.get(class_type, None)
360+
if class_struct is not None:
361+
class_data, _ = class_struct.parse_binary(d, self._display)
362+
classes.append(class_data)
363+
# Parse ButtonInfo state mask and label atoms.
364+
if ButtonClass == class_type:
365+
# Mask: bitfield of size a multiple of 4.
366+
mask_offset = ButtonInfo.static_size
367+
mask_len = 4 * ((((class_data.num_buttons + 7) >> 3) + 3) >> 2)
368+
mask_data = d[mask_offset:mask_offset+mask_len]
369+
mask = 0
370+
for b in reversed(struct.unpack('=%uB' % mask_len, mask_data)):
371+
mask <<= 8
372+
mask |= b
373+
class_data.state = mask
374+
# Labels: a list of <num_buttons> atoms.
375+
labels_offset = mask_offset + mask_len
376+
labels_len = class_data.num_buttons * 4
377+
labels_data = d[labels_offset:labels_offset+labels_len]
378+
labels = struct.unpack('=%uL' % class_data.num_buttons, labels_data)
379+
class_data.labels = labels
380+
d = buffer(d, length * 4)
381+
devices.append(devinfo)
382+
self._response_lock.release()
383+
384+
def query_device(self, deviceid):
385+
return XIQueryDevice(
386+
display=self.display,
387+
opcode=self.display.get_extension_major(extname),
388+
deviceid=deviceid,
389+
)
390+
240391
class XIGrabDevice(rq.ReplyRequest):
241392
_request = rq.Struct(
242393
rq.Card8('opcode'),
@@ -453,6 +604,7 @@ def parse_value(self, value, display):
453604
def init(disp, info):
454605
disp.extension_add_method('display', 'xinput_query_version', query_version)
455606
disp.extension_add_method('window', 'xinput_select_events', select_events)
607+
disp.extension_add_method('display', 'xinput_query_device', query_device)
456608
disp.extension_add_method('window', 'xinput_grab_device', grab_device)
457609
disp.extension_add_method('display', 'xinput_ungrab_device', ungrab_device)
458610
disp.extension_add_method('window', 'xinput_grab_keycode', grab_keycode)

0 commit comments

Comments
 (0)