2 # SPDX-License-Identifier: GPL-2.0
3 # -*- coding: utf-8 -*-
5 # Copyright (c) 2018 Benjamin Tissoires <benjamin.tissoires@gmail.com>
6 # Copyright (c) 2018 Red Hat, Inc.
14 logger
= logging
.getLogger("hidtools.test.keyboard")
17 class InvalidHIDCommunication(Exception):
21 class KeyboardData(object):
25 class BaseKeyboard(base
.UHIDTestDevice
):
26 def __init__(self
, rdesc
, name
=None, input_info
=None):
27 assert rdesc
is not None
28 super().__init
__(name
, "Key", input_info
=input_info
, rdesc
=rdesc
)
31 def _update_key_state(self
, keys
):
33 Update the internal state of keys with the new state given.
35 :param key: a tuple of chars for the currently pressed keys.
37 # First remove the already released keys
38 unused_keys
= [k
for k
, v
in self
.keystates
.items() if not v
]
39 for key
in unused_keys
:
40 del self
.keystates
[key
]
42 # self.keystates contains now the list of currently pressed keys,
44 for key
in self
.keystates
.keys():
45 self
.keystates
[key
] = False
47 # ...and press those that are in parameter
49 self
.keystates
[key
] = True
51 def _create_report_data(self
):
52 keyboard
= KeyboardData()
53 for key
, value
in self
.keystates
.items():
54 key
= key
.replace(" ", "").lower()
55 setattr(keyboard
, key
, value
)
58 def create_array_report(self
, keys
, reportID
=None, application
=None):
60 Return an input report for this device.
62 :param keys: a tuple of chars for the pressed keys. The class maintains
63 the list of currently pressed keys, so to release a key, the caller
64 needs to call again this function without the key in this tuple.
65 :param reportID: the numeric report ID for this report, if needed
67 self
._update
_key
_state
(keys
)
68 reportID
= reportID
or self
.default_reportID
70 keyboard
= self
._create
_report
_data
()
71 return self
.create_report(keyboard
, reportID
=reportID
, application
=application
)
73 def event(self
, keys
, reportID
=None, application
=None):
75 Send an input event on the default report ID.
77 :param keys: a tuple of chars for the pressed keys. The class maintains
78 the list of currently pressed keys, so to release a key, the caller
79 needs to call again this function without the key in this tuple.
81 r
= self
.create_array_report(keys
, reportID
, application
)
82 self
.call_input_event(r
)
86 class PlainKeyboard(BaseKeyboard
):
89 0x05, 0x01, # Usage Page (Generic Desktop)
90 0x09, 0x06, # Usage (Keyboard)
91 0xa1, 0x01, # Collection (Application)
92 0x85, 0x01, # .Report ID (1)
93 0x05, 0x07, # .Usage Page (Keyboard)
94 0x19, 0xe0, # .Usage Minimum (224)
95 0x29, 0xe7, # .Usage Maximum (231)
96 0x15, 0x00, # .Logical Minimum (0)
97 0x25, 0x01, # .Logical Maximum (1)
98 0x75, 0x01, # .Report Size (1)
99 0x95, 0x08, # .Report Count (8)
100 0x81, 0x02, # .Input (Data,Var,Abs)
101 0x19, 0x00, # .Usage Minimum (0)
102 0x29, 0x97, # .Usage Maximum (151)
103 0x15, 0x00, # .Logical Minimum (0)
104 0x25, 0x01, # .Logical Maximum (1)
105 0x75, 0x01, # .Report Size (1)
106 0x95, 0x98, # .Report Count (152)
107 0x81, 0x02, # .Input (Data,Var,Abs)
108 0xc0, # End Collection
112 def __init__(self
, rdesc
=report_descriptor
, name
=None, input_info
=None):
113 super().__init
__(rdesc
, name
, input_info
)
114 self
.default_reportID
= 1
117 class ArrayKeyboard(BaseKeyboard
):
119 report_descriptor
= [
120 0x05, 0x01, # Usage Page (Generic Desktop)
121 0x09, 0x06, # Usage (Keyboard)
122 0xa1, 0x01, # Collection (Application)
123 0x05, 0x07, # .Usage Page (Keyboard)
124 0x19, 0xe0, # .Usage Minimum (224)
125 0x29, 0xe7, # .Usage Maximum (231)
126 0x15, 0x00, # .Logical Minimum (0)
127 0x25, 0x01, # .Logical Maximum (1)
128 0x75, 0x01, # .Report Size (1)
129 0x95, 0x08, # .Report Count (8)
130 0x81, 0x02, # .Input (Data,Var,Abs)
131 0x95, 0x06, # .Report Count (6)
132 0x75, 0x08, # .Report Size (8)
133 0x15, 0x00, # .Logical Minimum (0)
134 0x26, 0xa4, 0x00, # .Logical Maximum (164)
135 0x05, 0x07, # .Usage Page (Keyboard)
136 0x19, 0x00, # .Usage Minimum (0)
137 0x29, 0xa4, # .Usage Maximum (164)
138 0x81, 0x00, # .Input (Data,Arr,Abs)
139 0xc0, # End Collection
143 def __init__(self
, rdesc
=report_descriptor
, name
=None, input_info
=None):
144 super().__init
__(rdesc
, name
, input_info
)
146 def _create_report_data(self
):
147 data
= KeyboardData()
150 hut
= hidtools
.hut
.HUT
152 # strip modifiers from the array
153 for k
, v
in self
.keystates
.items():
154 # we ignore depressed keys
158 usage
= hut
[0x07].from_name
[k
].usage
159 if usage
>= 224 and usage
<= 231:
161 setattr(data
, k
.lower(), 1)
165 # if array length is bigger than 6, report ErrorRollOver
167 array
= ["ErrorRollOver"] * 6
169 data
.keyboard
= array
173 class LEDKeyboard(ArrayKeyboard
):
175 report_descriptor
= [
176 0x05, 0x01, # Usage Page (Generic Desktop)
177 0x09, 0x06, # Usage (Keyboard)
178 0xa1, 0x01, # Collection (Application)
179 0x05, 0x07, # .Usage Page (Keyboard)
180 0x19, 0xe0, # .Usage Minimum (224)
181 0x29, 0xe7, # .Usage Maximum (231)
182 0x15, 0x00, # .Logical Minimum (0)
183 0x25, 0x01, # .Logical Maximum (1)
184 0x75, 0x01, # .Report Size (1)
185 0x95, 0x08, # .Report Count (8)
186 0x81, 0x02, # .Input (Data,Var,Abs)
187 0x95, 0x01, # .Report Count (1)
188 0x75, 0x08, # .Report Size (8)
189 0x81, 0x01, # .Input (Cnst,Arr,Abs)
190 0x95, 0x05, # .Report Count (5)
191 0x75, 0x01, # .Report Size (1)
192 0x05, 0x08, # .Usage Page (LEDs)
193 0x19, 0x01, # .Usage Minimum (1)
194 0x29, 0x05, # .Usage Maximum (5)
195 0x91, 0x02, # .Output (Data,Var,Abs)
196 0x95, 0x01, # .Report Count (1)
197 0x75, 0x03, # .Report Size (3)
198 0x91, 0x01, # .Output (Cnst,Arr,Abs)
199 0x95, 0x06, # .Report Count (6)
200 0x75, 0x08, # .Report Size (8)
201 0x15, 0x00, # .Logical Minimum (0)
202 0x26, 0xa4, 0x00, # .Logical Maximum (164)
203 0x05, 0x07, # .Usage Page (Keyboard)
204 0x19, 0x00, # .Usage Minimum (0)
205 0x29, 0xa4, # .Usage Maximum (164)
206 0x81, 0x00, # .Input (Data,Arr,Abs)
207 0xc0, # End Collection
211 def __init__(self
, rdesc
=report_descriptor
, name
=None, input_info
=None):
212 super().__init
__(rdesc
, name
, input_info
)
215 # Some Primax manufactured keyboards set the Usage Page after having defined
216 # some local Usages. It relies on the fact that the specification states that
217 # Usages are to be concatenated with Usage Pages upon finding a Main item (see
218 # 6.2.2.8). This test covers this case.
219 class PrimaxKeyboard(ArrayKeyboard
):
221 report_descriptor
= [
222 0x05, 0x01, # Usage Page (Generic Desktop)
223 0x09, 0x06, # Usage (Keyboard)
224 0xA1, 0x01, # Collection (Application)
225 0x05, 0x07, # .Usage Page (Keyboard)
226 0x19, 0xE0, # .Usage Minimum (224)
227 0x29, 0xE7, # .Usage Maximum (231)
228 0x15, 0x00, # .Logical Minimum (0)
229 0x25, 0x01, # .Logical Maximum (1)
230 0x75, 0x01, # .Report Size (1)
231 0x95, 0x08, # .Report Count (8)
232 0x81, 0x02, # .Input (Data,Var,Abs)
233 0x75, 0x08, # .Report Size (8)
234 0x95, 0x01, # .Report Count (1)
235 0x81, 0x01, # .Input (Data,Var,Abs)
236 0x05, 0x08, # .Usage Page (LEDs)
237 0x19, 0x01, # .Usage Minimum (1)
238 0x29, 0x03, # .Usage Maximum (3)
239 0x75, 0x01, # .Report Size (1)
240 0x95, 0x03, # .Report Count (3)
241 0x91, 0x02, # .Output (Data,Var,Abs)
242 0x95, 0x01, # .Report Count (1)
243 0x75, 0x05, # .Report Size (5)
244 0x91, 0x01, # .Output (Constant)
245 0x15, 0x00, # .Logical Minimum (0)
246 0x26, 0xFF, 0x00, # .Logical Maximum (255)
247 0x19, 0x00, # .Usage Minimum (0)
248 0x2A, 0xFF, 0x00, # .Usage Maximum (255)
249 0x05, 0x07, # .Usage Page (Keyboard)
250 0x75, 0x08, # .Report Size (8)
251 0x95, 0x06, # .Report Count (6)
252 0x81, 0x00, # .Input (Data,Arr,Abs)
253 0xC0, # End Collection
257 def __init__(self
, rdesc
=report_descriptor
, name
=None, input_info
=None):
258 super().__init
__(rdesc
, name
, input_info
)
262 class TestKeyboard(base
.BaseTestCase
.TestUhid
):
263 def test_single_key(self
):
264 """check for key reliability."""
266 evdev
= uhdev
.get_evdev()
267 syn_event
= self
.syn_event
269 r
= uhdev
.event(["a and A"])
270 expected
= [syn_event
]
271 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_A
, 1))
272 events
= uhdev
.next_sync_events()
273 self
.debug_reports(r
, uhdev
, events
)
274 self
.assertInputEventsIn(expected
, events
)
275 assert evdev
.value
[libevdev
.EV_KEY
.KEY_A
] == 1
278 expected
= [syn_event
]
279 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_A
, 0))
280 events
= uhdev
.next_sync_events()
281 self
.debug_reports(r
, uhdev
, events
)
282 self
.assertInputEventsIn(expected
, events
)
283 assert evdev
.value
[libevdev
.EV_KEY
.KEY_A
] == 0
285 def test_two_keys(self
):
287 evdev
= uhdev
.get_evdev()
288 syn_event
= self
.syn_event
290 r
= uhdev
.event(["a and A", "q and Q"])
291 expected
= [syn_event
]
292 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_A
, 1))
293 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_Q
, 1))
294 events
= uhdev
.next_sync_events()
295 self
.debug_reports(r
, uhdev
, events
)
296 self
.assertInputEventsIn(expected
, events
)
297 assert evdev
.value
[libevdev
.EV_KEY
.KEY_A
] == 1
300 expected
= [syn_event
]
301 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_A
, 0))
302 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_Q
, 0))
303 events
= uhdev
.next_sync_events()
304 self
.debug_reports(r
, uhdev
, events
)
305 self
.assertInputEventsIn(expected
, events
)
306 assert evdev
.value
[libevdev
.EV_KEY
.KEY_A
] == 0
307 assert evdev
.value
[libevdev
.EV_KEY
.KEY_Q
] == 0
309 r
= uhdev
.event(["c and C"])
310 expected
= [syn_event
]
311 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_C
, 1))
312 events
= uhdev
.next_sync_events()
313 self
.debug_reports(r
, uhdev
, events
)
314 self
.assertInputEventsIn(expected
, events
)
315 assert evdev
.value
[libevdev
.EV_KEY
.KEY_C
] == 1
317 r
= uhdev
.event(["c and C", "Spacebar"])
318 expected
= [syn_event
]
319 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_SPACE
, 1))
320 events
= uhdev
.next_sync_events()
321 self
.debug_reports(r
, uhdev
, events
)
322 assert libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_C
) not in events
323 self
.assertInputEventsIn(expected
, events
)
324 assert evdev
.value
[libevdev
.EV_KEY
.KEY_C
] == 1
325 assert evdev
.value
[libevdev
.EV_KEY
.KEY_SPACE
] == 1
327 r
= uhdev
.event(["Spacebar"])
328 expected
= [syn_event
]
329 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_C
, 0))
330 events
= uhdev
.next_sync_events()
331 self
.debug_reports(r
, uhdev
, events
)
332 assert libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_SPACE
) not in events
333 self
.assertInputEventsIn(expected
, events
)
334 assert evdev
.value
[libevdev
.EV_KEY
.KEY_C
] == 0
335 assert evdev
.value
[libevdev
.EV_KEY
.KEY_SPACE
] == 1
338 expected
= [syn_event
]
339 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_SPACE
, 0))
340 events
= uhdev
.next_sync_events()
341 self
.debug_reports(r
, uhdev
, events
)
342 self
.assertInputEventsIn(expected
, events
)
343 assert evdev
.value
[libevdev
.EV_KEY
.KEY_SPACE
] == 0
345 def test_modifiers(self
):
346 # ctrl-alt-del would be very nice :)
348 syn_event
= self
.syn_event
350 r
= uhdev
.event(["LeftControl", "LeftShift", "= and +"])
351 expected
= [syn_event
]
352 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_LEFTCTRL
, 1))
353 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_LEFTSHIFT
, 1))
354 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_EQUAL
, 1))
355 events
= uhdev
.next_sync_events()
356 self
.debug_reports(r
, uhdev
, events
)
357 self
.assertInputEventsIn(expected
, events
)
360 class TestPlainKeyboard(BaseTest
.TestKeyboard
):
361 def create_device(self
):
362 return PlainKeyboard()
364 def test_10_keys(self
):
366 syn_event
= self
.syn_event
382 expected
= [syn_event
]
383 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_0
, 1))
384 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_1
, 1))
385 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_2
, 1))
386 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_3
, 1))
387 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_4
, 1))
388 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_5
, 1))
389 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_6
, 1))
390 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_7
, 1))
391 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_8
, 1))
392 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_9
, 1))
393 events
= uhdev
.next_sync_events()
394 self
.debug_reports(r
, uhdev
, events
)
395 self
.assertInputEventsIn(expected
, events
)
398 expected
= [syn_event
]
399 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_0
, 0))
400 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_1
, 0))
401 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_2
, 0))
402 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_3
, 0))
403 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_4
, 0))
404 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_5
, 0))
405 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_6
, 0))
406 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_7
, 0))
407 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_8
, 0))
408 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_9
, 0))
409 events
= uhdev
.next_sync_events()
410 self
.debug_reports(r
, uhdev
, events
)
411 self
.assertInputEventsIn(expected
, events
)
414 class TestArrayKeyboard(BaseTest
.TestKeyboard
):
415 def create_device(self
):
416 return ArrayKeyboard()
418 def test_10_keys(self
):
420 syn_event
= self
.syn_event
432 expected
= [syn_event
]
433 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_1
, 1))
434 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_2
, 1))
435 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_3
, 1))
436 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_4
, 1))
437 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_5
, 1))
438 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_6
, 1))
439 events
= uhdev
.next_sync_events()
441 self
.debug_reports(r
, uhdev
, events
)
442 self
.assertInputEventsIn(expected
, events
)
459 events
= uhdev
.next_sync_events()
461 self
.debug_reports(r
, uhdev
, events
)
463 assert len(events
) == 0
466 expected
= [syn_event
]
467 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_1
, 0))
468 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_2
, 0))
469 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_3
, 0))
470 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_4
, 0))
471 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_5
, 0))
472 expected
.append(libevdev
.InputEvent(libevdev
.EV_KEY
.KEY_6
, 0))
473 events
= uhdev
.next_sync_events()
474 self
.debug_reports(r
, uhdev
, events
)
475 self
.assertInputEventsIn(expected
, events
)
478 class TestLEDKeyboard(BaseTest
.TestKeyboard
):
479 def create_device(self
):
483 class TestPrimaxKeyboard(BaseTest
.TestKeyboard
):
484 def create_device(self
):
485 return PrimaxKeyboard()