# Xlib.ext.xinput -- XInput extension module # # Copyright (C) 2012 Outpost Embedded, LLC # Forest Bond # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # as published by the Free Software Foundation; either version 2.1 # of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., # 59 Temple Place, # Suite 330, # Boston, MA 02111-1307 USA ''' A very incomplete implementation of the XInput extension. ''' import sys import array import struct # Python 2/3 compatibility. from six import integer_types from Xlib.protocol import rq from Xlib import X extname = 'XInputExtension' PropertyDeleted = 0 PropertyCreated = 1 PropertyModified = 2 NotifyNormal = 0 NotifyGrab = 1 NotifyUngrab = 2 NotifyWhileGrabbed = 3 NotifyPassiveGrab = 4 NotifyPassiveUngrab = 5 NotifyAncestor = 0 NotifyVirtual = 1 NotifyInferior = 2 NotifyNonlinear = 3 NotifyNonlinearVirtual = 4 NotifyPointer = 5 NotifyPointerRoot = 6 NotifyDetailNone = 7 GrabtypeButton = 0 GrabtypeKeycode = 1 GrabtypeEnter = 2 GrabtypeFocusIn = 3 GrabtypeTouchBegin = 4 AnyModifier = (1 << 31) AnyButton = 0 AnyKeycode = 0 AsyncDevice = 0 SyncDevice = 1 ReplayDevice = 2 AsyncPairedDevice = 3 AsyncPair = 4 SyncPair = 5 SlaveSwitch = 1 DeviceChange = 2 MasterAdded = (1 << 0) MasterRemoved = (1 << 1) SlaveAdded = (1 << 2) SlaveRemoved = (1 << 3) SlaveAttached = (1 << 4) SlaveDetached = (1 << 5) DeviceEnabled = (1 << 6) DeviceDisabled = (1 << 7) AddMaster = 1 RemoveMaster = 2 AttachSlave = 3 DetachSlave = 4 AttachToMaster = 1 Floating = 2 ModeRelative = 0 ModeAbsolute = 1 MasterPointer = 1 MasterKeyboard = 2 SlavePointer = 3 SlaveKeyboard = 4 FloatingSlave = 5 KeyClass = 0 ButtonClass = 1 ValuatorClass = 2 ScrollClass = 3 TouchClass = 8 KeyRepeat = (1 << 16) AllDevices = 0 AllMasterDevices = 1 DeviceChanged = 1 KeyPress = 2 KeyRelease = 3 ButtonPress = 4 ButtonRelease = 5 Motion = 6 Enter = 7 Leave = 8 FocusIn = 9 FocusOut = 10 HierarchyChanged = 11 PropertyEvent = 12 RawKeyPress = 13 RawKeyRelease = 14 RawButtonPress = 15 RawButtonRelease = 16 RawMotion = 17 DeviceChangedMask = (1 << DeviceChanged) KeyPressMask = (1 << KeyPress) KeyReleaseMask = (1 << KeyRelease) ButtonPressMask = (1 << ButtonPress) ButtonReleaseMask = (1 << ButtonRelease) MotionMask = (1 << Motion) EnterMask = (1 << Enter) LeaveMask = (1 << Leave) FocusInMask = (1 << FocusIn) FocusOutMask = (1 << FocusOut) HierarchyChangedMask = (1 << HierarchyChanged) PropertyEventMask = (1 << PropertyEvent) RawKeyPressMask = (1 << RawKeyPress) RawKeyReleaseMask = (1 << RawKeyRelease) RawButtonPressMask = (1 << RawButtonPress) RawButtonReleaseMask = (1 << RawButtonRelease) RawMotionMask = (1 << RawMotion) GrabModeSync = 0 GrabModeAsync = 1 GrabModeTouch = 2 DEVICEID = rq.Card16 DEVICE = rq.Card16 DEVICEUSE = rq.Card8 PROPERTY_TYPE_FLOAT = 'FLOAT' class FP1616(rq.Int32): def check_value(self, value): return int(value * 65536.0) def parse_value(self, value, display): return float(value) / float(1 << 16) class FP3232(rq.ValueField): structcode = 'lL' structvalues = 2 def check_value(self, value): return value def parse_value(self, value, display): integral, frac = value ret = float(integral) # optimised math.ldexp(float(frac), -32) ret += float(frac) * (1.0 / (1 << 32)) return ret class XIQueryVersion(rq.ReplyRequest): _request = rq.Struct( rq.Card8('opcode'), rq.Opcode(47), rq.RequestLength(), rq.Card16('major_version'), rq.Card16('minor_version'), ) _reply = rq.Struct( rq.ReplyCode(), rq.Pad(1), rq.Card16('sequence_number'), rq.ReplyLength(), rq.Card16('major_version'), rq.Card16('minor_version'), rq.Pad(20), ) def query_version(self): return XIQueryVersion( display=self.display, opcode=self.display.get_extension_major(extname), major_version=2, minor_version=0, ) class Mask(rq.List): def __init__(self, name): rq.List.__init__(self, name, rq.Card32, pad=0) def pack_value(self, val): mask_seq = array.array(rq.struct_to_array_codes['L']) if isinstance(val, integer_types): # We need to build a "binary mask" that (as far as I can tell) is # encoded in native byte order from end to end. The simple case is # with a single unsigned 32-bit value, for which we construct an # array with just one item. For values too big to fit inside 4 # bytes we build a longer array, being careful to maintain native # byte order across the entire set of values. if sys.byteorder == 'little': def fun(val): mask_seq.insert(0, val) elif sys.byteorder == 'big': fun = mask_seq.append else: raise AssertionError(sys.byteorder) while val: fun(val & 0xFFFFFFFF) val = val >> 32 else: mask_seq.extend(val) return rq.encode_array(mask_seq), len(mask_seq), None EventMask = rq.Struct( DEVICE('deviceid'), rq.LengthOf('mask', 2), Mask('mask'), ) class XISelectEvents(rq.Request): _request = rq.Struct( rq.Card8('opcode'), rq.Opcode(46), rq.RequestLength(), rq.Window('window'), rq.LengthOf('masks', 2), rq.Pad(2), rq.List('masks', EventMask), ) def select_events(self, event_masks): ''' select_events(event_masks) event_masks: Sequence of (deviceid, mask) pairs, where deviceid is a numerical device ID, or AllDevices or AllMasterDevices, and mask is either an unsigned integer or sequence of 32 bits unsigned values ''' return XISelectEvents( display=self.display, opcode=self.display.get_extension_major(extname), window=self, masks=event_masks, ) AnyInfo = rq.Struct( rq.Card16('type'), rq.Card16('length'), rq.Card16('sourceid'), rq.Pad(2), ) class ButtonMask(object): def __init__(self, value, length): self._value = value self._length = length def __len__(self): return self._length def __getitem__(self, key): return self._value & (1 << key) def __str__(self): return repr(self) def __repr__(self): return '0b{value:0{width}b}'.format(value=self._value, width=self._length) class ButtonState(rq.ValueField): structcode = None def __init__(self, name): rq.ValueField.__init__(self, name) def parse_binary_value(self, data, display, length, fmt): # Mask: bitfield of button states. mask_len = 4 * ((((length + 7) >> 3) + 3) >> 2) mask_data = data[:mask_len] mask_value = 0 for byte in reversed(struct.unpack('={0:d}B'.format(mask_len), mask_data)): mask_value <<= 8 mask_value |= byte data = data[mask_len:] assert (mask_value & 1) == 0 return ButtonMask(mask_value >> 1, length), data ButtonInfo = rq.Struct( rq.Card16('type'), rq.Card16('length'), rq.Card16('sourceid'), rq.LengthOf(('state', 'labels'), 2), ButtonState('state'), rq.List('labels', rq.Card32), ) KeyInfo = rq.Struct( rq.Card16('type'), rq.Card16('length'), rq.Card16('sourceid'), rq.LengthOf('keycodes', 2), rq.List('keycodes', rq.Card32), ) ValuatorInfo = rq.Struct( rq.Card16('type'), rq.Card16('length'), rq.Card16('sourceid'), rq.Card16('number'), rq.Card32('label'), FP3232('min'), FP3232('max'), FP3232('value'), rq.Card32('resolution'), rq.Card8('mode'), rq.Pad(3), ) ScrollInfo = rq.Struct( rq.Card16('type'), rq.Card16('length'), rq.Card16('sourceid'), rq.Card16('number'), rq.Card16('scroll_type'), rq.Pad(2), rq.Card32('flags'), FP3232('increment'), ) TouchInfo = rq.Struct( rq.Card16('type'), rq.Card16('length'), rq.Card16('sourceid'), rq.Card8('mode'), rq.Card8('num_touches'), ) INFO_CLASSES = { KeyClass: KeyInfo, ButtonClass: ButtonInfo, ValuatorClass: ValuatorInfo, ScrollClass: ScrollInfo, TouchClass: TouchInfo, } class ClassInfoClass(object): structcode = None def parse_binary(self, data, display): class_type, length = struct.unpack('=HH', data[:4]) class_struct = INFO_CLASSES.get(class_type, AnyInfo) class_data, _ = class_struct.parse_binary(data, display) data = data[length * 4:] return class_data, data ClassInfo = ClassInfoClass() DeviceInfo = rq.Struct( DEVICEID('deviceid'), rq.Card16('use'), rq.Card16('attachment'), rq.LengthOf('classes', 2), rq.LengthOf('name', 2), rq.Bool('enabled'), rq.Pad(1), rq.String8('name', 4), rq.List('classes', ClassInfo), ) class XIQueryDevice(rq.ReplyRequest): _request = rq.Struct( rq.Card8('opcode'), rq.Opcode(48), rq.RequestLength(), DEVICEID('deviceid'), rq.Pad(2), ) _reply = rq.Struct( rq.ReplyCode(), rq.Pad(1), rq.Card16('sequence_number'), rq.ReplyLength(), rq.LengthOf('devices', 2), rq.Pad(22), rq.List('devices', DeviceInfo), ) def query_device(self, deviceid): return XIQueryDevice( display=self.display, opcode=self.display.get_extension_major(extname), deviceid=deviceid, ) class XIListProperties(rq.ReplyRequest): _request = rq.Struct( rq.Card8('opcode'), rq.Opcode(56), rq.RequestLength(), DEVICEID('deviceid'), rq.Pad(2), ) _reply = rq.Struct( rq.ReplyCode(), rq.Pad(1), rq.Card16('sequence_number'), rq.ReplyLength(), rq.LengthOf('atoms', 2), rq.Pad(22), rq.List('atoms', rq.Card32Obj), ) def list_device_properties(self, deviceid): return XIListProperties( display=self.display, opcode=self.display.get_extension_major(extname), deviceid=deviceid, ) class XIGetProperty(rq.ReplyRequest): _request = rq.Struct( rq.Card8('opcode'), rq.Opcode(59), rq.RequestLength(), DEVICEID('deviceid'), rq.Card8('delete'), rq.Pad(1), rq.Card32('property'), rq.Card32('type'), rq.Card32('offset'), rq.Card32('length'), ) _reply = rq.Struct( rq.ReplyCode(), rq.Pad(1), rq.Card16('sequence_number'), rq.ReplyLength(), rq.Card32('type'), rq.Card32('bytes_after'), rq.LengthOf('value', 4), rq.Format('value', 1), rq.Pad(11), rq.PropertyData('value') ) def get_device_property(self, deviceid, property, type, offset, length, delete=False): return XIGetProperty( display=self.display, opcode=self.display.get_extension_major(extname), deviceid=deviceid, property=property, type=type, offset=offset, length=length, delete=delete, ) class XIChangeProperty(rq.Request): _request = rq.Struct( rq.Card8('opcode'), rq.Opcode(57), rq.RequestLength(), DEVICEID('deviceid'), rq.Card8('mode'), rq.Format('value', 1), rq.Card32('property'), rq.Card32('type'), rq.LengthOf('value', 4), rq.PropertyData('value'), ) def change_device_property(self, deviceid, property, type, mode, value): return XIChangeProperty( display=self.display, opcode=self.display.get_extension_major(extname), deviceid=deviceid, property=property, type=type, mode=mode, value=value, ) class XIDeleteProperty(rq.Request): _request = rq.Struct( rq.Card8('opcode'), rq.Opcode(58), rq.RequestLength(), DEVICEID('deviceid'), rq.Pad(2), rq.Card32('property'), ) def delete_device_property(self, deviceid, property): return XIDeleteProperty( display=self.display, opcode=self.display.get_extension_major(extname), deviceid=deviceid, property=property, ) class XIGrabDevice(rq.ReplyRequest): _request = rq.Struct( rq.Card8('opcode'), rq.Opcode(51), rq.RequestLength(), rq.Window('grab_window'), rq.Card32('time'), rq.Cursor('cursor', (X.NONE, )), DEVICEID('deviceid'), rq.Set('grab_mode', 1, (GrabModeSync, GrabModeAsync)), rq.Set('paired_device_mode', 1, (GrabModeSync, GrabModeAsync)), rq.Bool('owner_events'), rq.Pad(1), rq.LengthOf('mask', 2), Mask('mask'), ) _reply = rq.Struct( rq.ReplyCode(), rq.Pad(1), rq.Card16('sequence_number'), rq.ReplyLength(), rq.Card8('status'), rq.Pad(23), ) def grab_device(self, deviceid, time, grab_mode, paired_device_mode, owner_events, event_mask): return XIGrabDevice( display=self.display, opcode=self.display.get_extension_major(extname), deviceid=deviceid, grab_window=self, time=time, cursor=X.NONE, grab_mode=grab_mode, paired_device_mode=paired_device_mode, owner_events=owner_events, mask=event_mask, ) class XIUngrabDevice(rq.Request): _request = rq.Struct( rq.Card8('opcode'), rq.Opcode(52), rq.RequestLength(), rq.Card32('time'), DEVICEID('deviceid'), rq.Pad(2), ) def ungrab_device(self, deviceid, time): return XIUngrabDevice( display=self.display, opcode=self.display.get_extension_major(extname), time=time, deviceid=deviceid, ) class XIPassiveGrabDevice(rq.ReplyRequest): _request = rq.Struct( rq.Card8('opcode'), rq.Opcode(54), rq.RequestLength(), rq.Card32('time'), rq.Window('grab_window'), rq.Cursor('cursor', (X.NONE, )), rq.Card32('detail'), DEVICEID('deviceid'), rq.LengthOf('modifiers', 2), rq.LengthOf('mask', 2), rq.Set('grab_type', 1, (GrabtypeButton, GrabtypeKeycode, GrabtypeEnter, GrabtypeFocusIn, GrabtypeTouchBegin)), rq.Set('grab_mode', 1, (GrabModeSync, GrabModeAsync)), rq.Set('paired_device_mode', 1, (GrabModeSync, GrabModeAsync)), rq.Bool('owner_events'), rq.Pad(2), Mask('mask'), rq.List('modifiers', rq.Card32), ) _reply = rq.Struct( rq.ReplyCode(), rq.Pad(1), rq.Card16('sequence_number'), rq.ReplyLength(), rq.LengthOf('modifiers', 2), rq.Pad(22), rq.List('modifiers', rq.Card32), ) def passive_grab_device(self, deviceid, time, detail, grab_type, grab_mode, paired_device_mode, owner_events, event_mask, modifiers): return XIPassiveGrabDevice( display=self.display, opcode=self.display.get_extension_major(extname), deviceid=deviceid, grab_window=self, time=time, cursor=X.NONE, detail=detail, grab_type=grab_type, grab_mode=grab_mode, paired_device_mode=paired_device_mode, owner_events=owner_events, mask=event_mask, modifiers=modifiers, ) def grab_keycode(self, deviceid, time, keycode, grab_mode, paired_device_mode, owner_events, event_mask, modifiers): return passive_grab_device(self, deviceid, time, keycode, GrabtypeKeycode, grab_mode, paired_device_mode, owner_events, event_mask, modifiers) class XIPassiveUngrabDevice(rq.Request): _request = rq.Struct( rq.Card8('opcode'), rq.Opcode(55), rq.RequestLength(), rq.Window('grab_window'), rq.Card32('detail'), DEVICEID('deviceid'), rq.LengthOf('modifiers', 2), rq.Set('grab_type', 1, (GrabtypeButton, GrabtypeKeycode, GrabtypeEnter, GrabtypeFocusIn, GrabtypeTouchBegin)), rq.Pad(3), rq.List('modifiers', rq.Card32), ) def passive_ungrab_device(self, deviceid, detail, grab_type, modifiers): return XIPassiveUngrabDevice( display=self.display, opcode=self.display.get_extension_major(extname), deviceid=deviceid, grab_window=self, detail=detail, grab_type=grab_type, modifiers=modifiers, ) def ungrab_keycode(self, deviceid, keycode, modifiers): return passive_ungrab_device(self, deviceid, keycode, GrabtypeKeycode, modifiers) HierarchyInfo = rq.Struct( DEVICEID('deviceid'), DEVICEID('attachment'), DEVICEUSE('type'), rq.Bool('enabled'), rq.Pad(2), rq.Card32('flags'), ) HierarchyEventData = rq.Struct( DEVICEID('deviceid'), rq.Card32('time'), rq.Card32('flags'), rq.LengthOf('info', 2), rq.Pad(10), rq.List('info', HierarchyInfo), ) ModifierInfo = rq.Struct( rq.Card32('base_mods'), rq.Card32('latched_mods'), rq.Card32('locked_mods'), rq.Card32('effective_mods'), ) GroupInfo = rq.Struct( rq.Card8('base_group'), rq.Card8('latched_group'), rq.Card8('locked_group'), rq.Card8('effective_group'), ) DeviceEventData = rq.Struct( DEVICEID('deviceid'), rq.Card32('time'), rq.Card32('detail'), rq.Window('root'), rq.Window('event'), rq.Window('child'), FP1616('root_x'), FP1616('root_y'), FP1616('event_x'), FP1616('event_y'), rq.LengthOf('buttons', 2), rq.Card16('valulators_len'), DEVICEID('sourceid'), rq.Pad(2), rq.Card32('flags'), rq.Object('mods', ModifierInfo), rq.Object('groups', GroupInfo), ButtonState('buttons'), ) DeviceChangedEventData = rq.Struct( DEVICEID('deviceid'), rq.Card32('time'), rq.LengthOf('classes', 2), DEVICEID('sourceid'), rq.Card8('reason'), rq.Pad(11), rq.List('classes', ClassInfo), ) PropertyEventData = rq.Struct( DEVICEID('deviceid'), rq.Card32('time'), rq.Card32('property'), rq.Card8('what'), rq.Pad(11), ) def init(disp, info): disp.extension_add_method('display', 'xinput_query_version', query_version) disp.extension_add_method('window', 'xinput_select_events', select_events) disp.extension_add_method('display', 'xinput_query_device', query_device) disp.extension_add_method('window', 'xinput_grab_device', grab_device) disp.extension_add_method('display', 'xinput_ungrab_device', ungrab_device) disp.extension_add_method('window', 'xinput_grab_keycode', grab_keycode) disp.extension_add_method('window', 'xinput_ungrab_keycode', ungrab_keycode) disp.extension_add_method('display', 'xinput_get_device_property', get_device_property) disp.extension_add_method('display', 'xinput_list_device_properties', list_device_properties) disp.extension_add_method('display', 'xinput_change_device_property', change_device_property) disp.extension_add_method('display', 'xinput_delete_device_property', delete_device_property) if hasattr(disp,"ge_add_event_data"): for device_event in (ButtonPress, ButtonRelease, KeyPress, KeyRelease, Motion): disp.ge_add_event_data(info.major_opcode, device_event, DeviceEventData) disp.ge_add_event_data(info.major_opcode, DeviceChanged, DeviceEventData) disp.ge_add_event_data(info.major_opcode, HierarchyChanged, HierarchyEventData) disp.ge_add_event_data(info.major_opcode, PropertyEvent, PropertyEventData)