Skip to content

Commit 96aec9d

Browse files
committed
Add partial support for the XInput extension, including an example.
1 parent b37a428 commit 96aec9d

File tree

3 files changed

+341
-0
lines changed

3 files changed

+341
-0
lines changed

Xlib/ext/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
('RANDR', 'randr'),
3333
('XFIXES', 'xfixes'),
3434
('SECURITY', 'security'),
35+
('XInputExtension', 'xinput'),
3536
]
3637

3738
__all__ = map(lambda x: x[1], __extensions__)

Xlib/ext/xinput.py

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
# Xlib.ext.xinput -- XInput extension module
2+
#
3+
# Copyright (C) 2012 Outpost Embedded, LLC
4+
# Forest Bond <forest.bond@rapidrollout.com>
5+
#
6+
# This program is free software; you can redistribute it and/or modify
7+
# it under the terms of the GNU General Public License as published by
8+
# the Free Software Foundation; either version 2 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# This program is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License
17+
# along with this program; if not, write to the Free Software
18+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19+
20+
'''
21+
A very incomplete implementation of the XInput extension.
22+
'''
23+
24+
import sys, array
25+
26+
from Xlib.protocol import rq
27+
28+
29+
extname = 'XInputExtension'
30+
31+
PropertyDeleted = 0
32+
PropertyCreated = 1
33+
PropertyModified = 2
34+
35+
NotifyNormal = 0
36+
NotifyGrab = 1
37+
NotifyUngrab = 2
38+
NotifyWhileGrabbed = 3
39+
NotifyPassiveGrab = 4
40+
NotifyPassiveUngrab = 5
41+
42+
NotifyAncestor = 0
43+
NotifyVirtual = 1
44+
NotifyInferior = 2
45+
NotifyNonlinear = 3
46+
NotifyNonlinearVirtual = 4
47+
NotifyPointer = 5
48+
NotifyPointerRoot = 6
49+
NotifyDetailNone = 7
50+
51+
GrabtypeButton = 0
52+
GrabtypeKeycode = 1
53+
GrabtypeEnter = 2
54+
GrabtypeFocusIn = 3
55+
56+
AnyModifier = (1 << 31)
57+
AnyButton = 0
58+
AnyKeycode = 0
59+
60+
AsyncDevice = 0
61+
SyncDevice = 1
62+
ReplayDevice = 2
63+
AsyncPairedDevice = 3
64+
AsyncPair = 4
65+
SyncPair = 5
66+
67+
SlaveSwitch = 1
68+
DeviceChange = 2
69+
70+
MasterAdded = (1 << 0)
71+
MasterRemoved = (1 << 1)
72+
SlaveAdded = (1 << 2)
73+
SlaveRemoved = (1 << 3)
74+
SlaveAttached = (1 << 4)
75+
SlaveDetached = (1 << 5)
76+
DeviceEnabled = (1 << 6)
77+
DeviceDisabled = (1 << 7)
78+
79+
AddMaster = 1
80+
RemoveMaster = 2
81+
AttachSlave = 3
82+
DetachSlave = 4
83+
84+
AttachToMaster = 1
85+
Floating = 2
86+
87+
ModeRelative = 0
88+
ModeAbsolute = 1
89+
90+
MasterPointer = 1
91+
MasterKeyboard = 2
92+
SlavePointer = 3
93+
SlaveKeyboard = 4
94+
FloatingSlave = 5
95+
96+
KeyClass = 0
97+
ButtonClass = 1
98+
ValuatorClass = 2
99+
100+
KeyRepeat = (1 << 16)
101+
102+
AllDevices = 0
103+
AllMasterDevices = 1
104+
105+
DeviceChanged = 1
106+
KeyPress = 2
107+
KeyRelease = 3
108+
ButtonPress = 4
109+
ButtonRelease = 5
110+
Motion = 6
111+
Enter = 7
112+
Leave = 8
113+
FocusIn = 9
114+
FocusOut = 10
115+
HierarchyChanged = 11
116+
PropertyEvent = 12
117+
RawKeyPress = 13
118+
RawKeyRelease = 14
119+
RawButtonPress = 15
120+
RawButtonRelease = 16
121+
RawMotion = 17
122+
123+
DeviceChangedMask = (1 << DeviceChanged)
124+
KeyPressMask = (1 << KeyPress)
125+
KeyReleaseMask = (1 << KeyRelease)
126+
ButtonPressMask = (1 << ButtonPress)
127+
ButtonReleaseMask = (1 << ButtonRelease)
128+
MotionMask = (1 << Motion)
129+
EnterMask = (1 << Enter)
130+
LeaveMask = (1 << Leave)
131+
FocusInMask = (1 << FocusIn)
132+
FocusOutMask = (1 << FocusOut)
133+
HierarchyChangedMask = (1 << HierarchyChanged)
134+
PropertyEventMask = (1 << PropertyEvent)
135+
RawKeyPressMask = (1 << RawKeyPress)
136+
RawKeyReleaseMask = (1 << RawKeyRelease)
137+
RawButtonPressMask = (1 << RawButtonPress)
138+
RawButtonReleaseMask = (1 << RawButtonRelease)
139+
RawMotionMask = (1 << RawMotion)
140+
141+
DEVICEID = rq.Card16
142+
DEVICE = rq.Card16
143+
DEVICEUSE = rq.Card8
144+
145+
146+
class XIQueryVersion(rq.ReplyRequest):
147+
_request = rq.Struct(
148+
rq.Card8('opcode'),
149+
rq.Opcode(47),
150+
rq.RequestLength(),
151+
rq.Card16('major_version'),
152+
rq.Card16('minor_version'),
153+
)
154+
_reply = rq.Struct(
155+
rq.ReplyCode(),
156+
rq.Pad(1),
157+
rq.Card16('sequence_number'),
158+
rq.ReplyLength(),
159+
rq.Card32('major_version'),
160+
rq.Card32('minor_version'),
161+
rq.Pad(16),
162+
)
163+
164+
165+
def query_version(self):
166+
return XIQueryVersion(
167+
display=self.display,
168+
opcode=self.display.get_extension_major(extname),
169+
major_version=2,
170+
minor_version=0,
171+
)
172+
173+
174+
EventMask = rq.Struct(
175+
DEVICE('deviceid'),
176+
rq.LengthOf('mask', 2),
177+
rq.List('mask', rq.Card32),
178+
)
179+
180+
181+
class XISelectEvents(rq.Request):
182+
_request = rq.Struct(
183+
rq.Card8('opcode'),
184+
rq.Opcode(46),
185+
rq.RequestLength(),
186+
rq.Window('window'),
187+
rq.LengthOf('masks', 2),
188+
rq.Pad(2),
189+
rq.List('masks', EventMask),
190+
)
191+
192+
193+
def select_events(self, event_masks):
194+
'''
195+
select_events(event_masks)
196+
197+
event_masks:
198+
Sequence of (deviceid, mask) pairs, where deviceid is a numerical device
199+
ID, or AllDevices or AllMasterDevices, and mask is either an unsigned
200+
integer or sequence of 32 byte unsigned values
201+
'''
202+
203+
masks = []
204+
for deviceid, mask in event_masks:
205+
mask_seq = array.array(rq.struct_to_array_codes['L'])
206+
207+
if isinstance(mask, (int, long)):
208+
# We need to build a "binary mask" that (as far as I can tell) is
209+
# encoded in native byte order from end to end. The simple case is
210+
# with a single unsigned 32-bit value, for which we construct an
211+
# array with just one item. For values too big to fit inside 4
212+
# bytes we build a longer array, being careful to maintain native
213+
# byte order across the entire set of values.
214+
if sys.byteorder == 'little':
215+
f = lambda v: mask_seq.insert(0, v)
216+
elif sys.byteorder == 'big':
217+
f = mask_seq.append
218+
else:
219+
raise AssertionError(sys.byteorder)
220+
while mask:
221+
f(mask & 0xFFFFFFFF)
222+
mask = mask >> 32
223+
else:
224+
mask_seq.extend(mask)
225+
226+
masks.append({'deviceid': deviceid, 'mask': mask_seq})
227+
228+
return XISelectEvents(
229+
display=self.display,
230+
opcode=self.display.get_extension_major(extname),
231+
window=self,
232+
masks=masks,
233+
)
234+
235+
236+
HierarchyInfo = rq.Struct(
237+
DEVICEID('deviceid'),
238+
DEVICEID('attachment'),
239+
DEVICEUSE('type'),
240+
rq.Bool('enabled'),
241+
rq.Pad(2),
242+
rq.Card32('flags'),
243+
)
244+
245+
246+
HierarchyEventData = rq.Struct(
247+
DEVICEID('deviceid'),
248+
rq.Card32('time'),
249+
rq.Card32('flags'),
250+
rq.LengthOf('info', 2),
251+
rq.Pad(10),
252+
rq.List('info', HierarchyInfo),
253+
)
254+
255+
256+
def init(disp, info):
257+
disp.extension_add_method('display', 'xinput_query_version', query_version)
258+
disp.extension_add_method('window', 'xinput_select_events', select_events)
259+
260+
disp.ge_add_event_data(info.major_opcode, 11, HierarchyEventData)

examples/xinput.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/python
2+
#
3+
# examples/xinput.py -- demonstrate the XInput extension
4+
#
5+
# Copyright (C) 2012 Outpost Embedded, LLC
6+
# Forest Bond <forest.bond@rapidrollout.com>
7+
#
8+
# This program is free software; you can redistribute it and/or modify
9+
# it under the terms of the GNU General Public License as published by
10+
# the Free Software Foundation; either version 2 of the License, or
11+
# (at your option) any later version.
12+
#
13+
# This program is distributed in the hope that it will be useful,
14+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
# GNU General Public License for more details.
17+
#
18+
# You should have received a copy of the GNU General Public License
19+
# along with this program; if not, write to the Free Software
20+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21+
22+
import sys
23+
24+
from Xlib.display import Display
25+
from Xlib.ext import xinput
26+
27+
28+
def print_hierarchy_changed_event(event):
29+
print '<deviceid=%s time=%s flags=%s info={' % (
30+
event.data.deviceid,
31+
event.data.time,
32+
event.data.flags,
33+
)
34+
for info in event.data.info:
35+
print_info(info)
36+
print '}>'
37+
38+
39+
def print_info(info):
40+
print ' <deviceid=%s attachment=%s type=%s enabled=%s flags=%s>' % (
41+
info.deviceid,
42+
info.attachment,
43+
info.type,
44+
info.enabled,
45+
info.flags,
46+
)
47+
48+
49+
def main(argv):
50+
display = Display()
51+
try:
52+
extension_info = display.query_extension('XInputExtension')
53+
xinput_major = extension_info.major_opcode
54+
55+
version_info = display.xinput_query_version()
56+
print 'Found XInput version %u.%u' % (
57+
version_info.major_version,
58+
version_info.minor_version,
59+
)
60+
61+
screen = display.screen()
62+
screen.root.xinput_select_events([
63+
(xinput.AllDevices, xinput.HierarchyChangedMask),
64+
])
65+
66+
while True:
67+
event = display.next_event()
68+
if (
69+
event.type == display.extension_event.GenericEvent
70+
and event.extension == xinput_major
71+
and event.evtype == 11
72+
):
73+
print_hierarchy_changed_event(event)
74+
75+
finally:
76+
display.close()
77+
78+
79+
if __name__ == '__main__':
80+
sys.exit(main(sys.argv))

0 commit comments

Comments
 (0)