| 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 |