OLD | NEW |
| (Empty) |
1 #!/usr/bin/python | |
2 # | |
3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | |
4 # Use of this source code is governed by a BSD-style license that can be | |
5 # found in the LICENSE file. | |
6 | |
7 | |
8 # DESCRIPTION : | |
9 # | |
10 # Intended for use during manufacturing to validate that all keyboard | |
11 # keys function properly. This program will display a keyboard image | |
12 # and keys will be highlighted as they are pressed and released. Once | |
13 # all keys have been used, a brief 'PASS' message will be displayed | |
14 # and the test will terminate. After the first key is hit, a | |
15 # countdown will begin. If not all keys are used in time, the test | |
16 # will fail with an 'ERROR' message that is displayed forever. | |
17 | |
18 | |
19 from autotest_lib.client.common_lib import factory_test | |
20 | |
21 import cairo | |
22 import gobject | |
23 import gtk | |
24 import logging | |
25 import pango | |
26 import time | |
27 import os | |
28 import sys | |
29 | |
30 | |
31 def XXX_log(s): | |
32 print >> sys.stderr, '--- XXX : ' + s | |
33 | |
34 | |
35 # How long keyboard_test allows in seconds from the first keypress | |
36 # until defaulting to the failure condition. | |
37 _TIMEOUT = 50 | |
38 _PASS_TIMEOUT = 0.4 | |
39 | |
40 # Highlight color and alpha to indicate activated keys. | |
41 _RGBA_PRESS_AND_RELEASE = ( 0, 0.5, 0, 0.6) | |
42 _RGBA_PRESS_ONLY = (0.6, 0.6, 0, 0.6) | |
43 | |
44 | |
45 class keyboard_test: | |
46 | |
47 def __init__(self, kbd_image, bindings): | |
48 self._bindings = bindings | |
49 self._kbd_image = kbd_image | |
50 self._pressed_keys = set() | |
51 self._successful_keys = set() | |
52 self._deadline = None | |
53 self._success = False | |
54 | |
55 def show_countdown(self, widget, context): | |
56 countdown = self._deadline - int(time.time()) | |
57 width, height = widget.get_size_request() | |
58 text = '%3d' % countdown | |
59 context.save() | |
60 context.translate(width - 60, height) | |
61 context.set_source_rgb(0.5, 0.5, 0.5) | |
62 context.select_font_face( | |
63 'Courier New', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) | |
64 context.set_font_size(20) | |
65 x_bearing, y_bearing = context.text_extents('000')[:2] | |
66 context.move_to(x_bearing, y_bearing) | |
67 context.show_text(text) | |
68 context.restore() | |
69 | |
70 def timer_event(self, window): | |
71 if not self._deadline: | |
72 # Ignore timer events with no countdown in progress. | |
73 return True | |
74 if self._deadline <= time.time(): | |
75 XXX_log('deadline reached') | |
76 gtk.main_quit() | |
77 window.queue_draw() | |
78 return True | |
79 | |
80 def expose_event(self, widget, event): | |
81 context = widget.window.cairo_create() | |
82 | |
83 # Show keyboard image as the background. | |
84 context.set_source_surface(self._kbd_image, 0, 0) | |
85 context.paint() | |
86 | |
87 for key in self._successful_keys: | |
88 coords = self._bindings[key] | |
89 context.rectangle(*coords) | |
90 context.set_source_rgba(*_RGBA_PRESS_AND_RELEASE) | |
91 context.fill() | |
92 for key in self._pressed_keys: | |
93 coords = self._bindings[key] | |
94 context.rectangle(*coords) | |
95 context.set_source_rgba(*_RGBA_PRESS_ONLY) | |
96 context.fill() | |
97 if self._deadline: | |
98 self.show_countdown(widget, context) | |
99 | |
100 return True | |
101 | |
102 def key_press_event(self, widget, event): | |
103 if factory_test.test_switch_on_trigger(event): | |
104 return True | |
105 if ('GDK_MOD1_MASK' in event.state.value_names | |
106 and event.keyval == gtk.keysyms.q): | |
107 # Alt-q for early exit. | |
108 gtk.main_quit() | |
109 return True | |
110 if event.keyval in self._successful_keys: | |
111 # Ignore keys already found to work successfully. | |
112 return True | |
113 if event.state != 0: | |
114 XXX_log('key (0x%x) ignored because modifier applied (state=%d)' | |
115 % (event.keyval, event.state)) | |
116 return True | |
117 if event.keyval not in self._bindings: | |
118 XXX_log('key (0x%x) ignored because not in bindings' % event.keyval) | |
119 return True | |
120 | |
121 self._pressed_keys.add(event.keyval) | |
122 widget.queue_draw() | |
123 | |
124 # The first keypress starts test countdown. | |
125 if self._deadline is None: | |
126 self._deadline = int(time.time()) + _TIMEOUT | |
127 | |
128 return True | |
129 | |
130 def key_release_event(self, widget, event): | |
131 if event.keyval not in self._pressed_keys: | |
132 # Ignore releases for keys not previously accepted as pressed. | |
133 return False | |
134 self._pressed_keys.remove(event.keyval) | |
135 self._successful_keys.add(event.keyval) | |
136 if not (set(self._bindings) - self._successful_keys): | |
137 self._success = True | |
138 self._deadline = int(time.time()) + _PASS_TIMEOUT | |
139 widget.queue_draw() | |
140 return True | |
141 | |
142 def register_callbacks(self, window): | |
143 window.connect('key-press-event', self.key_press_event) | |
144 window.connect('key-release-event', self.key_release_event) | |
145 window.add_events(gtk.gdk.KEY_PRESS_MASK | gtk.gdk.KEY_RELEASE_MASK) | |
146 | |
147 | |
148 def make_test_widget(kbd_image, bindings): | |
149 test = keyboard_test(kbd_image, bindings) | |
150 | |
151 image_size = (kbd_image.get_width(), kbd_image.get_height()) | |
152 | |
153 drawing_area = gtk.DrawingArea() | |
154 drawing_area.set_size_request(*image_size) | |
155 drawing_area.connect('expose_event', test.expose_event) | |
156 drawing_area.add_events(gtk.gdk.EXPOSURE_MASK) | |
157 gobject.timeout_add(1000, test.timer_event, drawing_area) | |
158 | |
159 return drawing_area, test.register_callbacks | |
OLD | NEW |