Skip to content

Commit b37a428

Browse files
committed
Add support for the Generic Event Extension.
While most of this functionality is implemented in the new "ge" extension module, some changes to the core protocol implementation were also required due to the need to read event packets longer than 32 bytes.
1 parent df944a7 commit b37a428

File tree

3 files changed

+141
-14
lines changed

3 files changed

+141
-14
lines changed

Xlib/ext/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
# protocol. extmod is the name of the module in this package.
2222

2323
__extensions__ = [
24+
# We load this first so other extensions can register generic event data
25+
# structures.
26+
('Generic Event Extension', 'ge'),
2427
('XTEST', 'xtest'),
2528
('SHAPE', 'shape'),
2629
('XINERAMA', 'xinerama'),

Xlib/ext/ge.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Xlib.ext.ge -- Generic Event 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+
ge - Generic Event Extension
22+
'''
23+
24+
from Xlib.protocol import rq
25+
26+
extname = 'Generic Event Extension'
27+
28+
29+
GenericEventCode = 35
30+
31+
32+
class GEQueryVersion(rq.ReplyRequest):
33+
_request = rq.Struct(
34+
rq.Card8('opcode'),
35+
rq.Opcode(0),
36+
rq.RequestLength(),
37+
rq.Card32('major_version'),
38+
rq.Card32('minor_version'),
39+
)
40+
_reply = rq.Struct(
41+
rq.ReplyCode(),
42+
rq.Pad(1),
43+
rq.Card16('sequence_number'),
44+
rq.ReplyLength(),
45+
rq.Card32('major_version'),
46+
rq.Card32('minor_version'),
47+
rq.Pad(16),
48+
)
49+
50+
51+
def query_version(self):
52+
return GEQueryVersion(
53+
display=self.display,
54+
opcode=self.display.get_extension_major(extname),
55+
major_version=1,
56+
minor_version=0,
57+
)
58+
59+
60+
class GenericEvent(rq.Event):
61+
_code = GenericEventCode
62+
_fields = rq.Struct(
63+
rq.Card8('type'),
64+
rq.Card8('extension'),
65+
rq.Card16('sequence_number'),
66+
rq.Card32('length'),
67+
rq.Card16('evtype'),
68+
# Some generic events make use of this space, but with
69+
# others the data is simply discarded. In any case we
70+
# don't need to explicitly pad this out as we are
71+
# always given at least 32 bytes and we save
72+
# everything after the first ten as the "data" field.
73+
#rq.Pad(22),
74+
)
75+
76+
def __init__(self, binarydata = None, display = None, **keys):
77+
if binarydata:
78+
data = binarydata[10:]
79+
binarydata = binarydata[:10]
80+
else:
81+
data = ''
82+
83+
rq.Event.__init__(
84+
self,
85+
binarydata=binarydata,
86+
display=display,
87+
**keys
88+
)
89+
90+
if display:
91+
ge_event_data = getattr(display, 'ge_event_data', None)
92+
if ge_event_data:
93+
estruct = ge_event_data.get((self.extension, self.evtype), None)
94+
if estruct:
95+
data, _ = estruct.parse_binary(data, display)
96+
97+
self._data['data'] = data
98+
99+
100+
def add_event_data(self, extension, evtype, estruct):
101+
if not hasattr(self.display, 'ge_event_data'):
102+
self.display.ge_event_data = {}
103+
self.display.ge_event_data[(extension, evtype)] = estruct
104+
105+
106+
def init(disp, info):
107+
disp.extension_add_method('display', 'ge_query_version', query_version)
108+
disp.extension_add_method('display', 'ge_add_event_data', add_event_data)
109+
disp.extension_add_event(GenericEventCode, GenericEvent)

Xlib/protocol/display.py

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
# Xlib modules
2929
from Xlib import error
30+
from Xlib.ext import ge
3031

3132
from Xlib.support import lock, connect
3233

@@ -639,34 +640,39 @@ def parse_response(self, request):
639640
# Parse ordinary server response
640641
gotreq = 0
641642
while 1:
642-
# Are we're waiting for additional data for a request response?
643+
if self.data_recv:
644+
# Check the first byte to find out what kind of response it is
645+
rtype = ord(self.data_recv[0])
646+
647+
# Are we're waiting for additional data for the current packet?
643648
if self.recv_packet_len:
644649
if len(self.data_recv) < self.recv_packet_len:
645650
return gotreq
646-
else:
647-
gotreq = self.parse_request_response(request) or gotreq
648651

652+
if rtype == 1:
653+
gotreq = self.parse_request_response(request) or gotreq
654+
elif rtype & 0x7f == ge.GenericEventCode:
655+
self.parse_event_response(rtype)
656+
else:
657+
raise AssertionError(rtype)
649658

650659
# Every response is at least 32 bytes long, so don't bother
651660
# until we have recieved that much
652661
if len(self.data_recv) < 32:
653662
return gotreq
654663

655-
# Check the first byte to find out what kind of response it is
656-
rtype = ord(self.data_recv[0])
657-
658664
# Error resposne
659665
if rtype == 0:
660666
gotreq = self.parse_error_response(request) or gotreq
661667

662-
# Request response
663-
elif rtype == 1:
668+
# Request response or generic event.
669+
elif rtype == 1 or rtype & 0x7f == ge.GenericEventCode:
664670
# Set reply length, and loop around to see if
665671
# we have got the full response
666672
rlen = int(struct.unpack('=L', self.data_recv[4:8])[0])
667673
self.recv_packet_len = 32 + rlen * 4
668674

669-
# Else event response
675+
# Else non-generic event
670676
else:
671677
self.parse_event_response(rtype)
672678

@@ -752,16 +758,25 @@ def parse_request_response(self, request):
752758

753759

754760
def parse_event_response(self, etype):
755-
# Skip bit 8 at lookup, that is set if this event came from an
756-
# SendEvent
757-
estruct = self.event_classes.get(etype & 0x7f, event.AnyEvent)
761+
# Skip bit 8, that is set if this event came from an SendEvent
762+
etype = etype & 0x7f
763+
764+
if etype == ge.GenericEventCode:
765+
length = self.recv_packet_len
766+
else:
767+
length = 32
768+
769+
estruct = self.event_classes.get(etype, event.AnyEvent)
758770
if type(estruct) == dict:
759771
# this etype refers to a set of sub-events with individual subcodes
760772
estruct = estruct[ord(self.data_recv[1])]
761773

762-
e = estruct(display = self, binarydata = self.data_recv[:32])
774+
e = estruct(display = self, binarydata = self.data_recv[:length])
763775

764-
self.data_recv = buffer(self.data_recv, 32)
776+
if etype == ge.GenericEventCode:
777+
self.recv_packet_len = 0
778+
779+
self.data_recv = buffer(self.data_recv, length)
765780

766781
# Drop all requests having an error handler,
767782
# but which obviously succeded.

0 commit comments

Comments
 (0)