Spaces:
Runtime error
Runtime error
# Xlib.ext.xinput -- XInput extension module | |
# | |
# Copyright (C) 2012 Outpost Embedded, LLC | |
# Forest Bond <forest.bond@rapidrollout.com> | |
# | |
# 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 <length> 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) | |