| 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 # DESCRIPTION : | |
| 8 # | |
| 9 # Intended for use during manufacturing to validate that all touchpad | |
| 10 # features function properly. Run normally, this program will display a | |
| 11 # touchpad image and buttons will be highlighted as they are pressed and | |
| 12 # released. Once all functions have been used, a brief 'PASS' message will | |
| 13 # be displayed and the test will terminate. After the first button is | |
| 14 # hit, a countdown will begin. If not all buttons are used in time, the | |
| 15 # test will fail with an 'ERROR' message that is displayed forever. | |
| 16 | |
| 17 import cairo | |
| 18 import gobject | |
| 19 import gtk | |
| 20 import os | |
| 21 import sys | |
| 22 import time | |
| 23 | |
| 24 class Button: | |
| 25 def __init__(self, coords): | |
| 26 self._pressed = False | |
| 27 self._released = False | |
| 28 self._coords = coords | |
| 29 | |
| 30 class TouchpadTest: | |
| 31 # Coordinates of buttons we need to highlight. | |
| 32 left_button_coords = (65, 321, 255, 93) | |
| 33 right_button_coords = (321, 321, 254, 93) | |
| 34 | |
| 35 # How long TouchpadTest allows in seconds from the first move or button | |
| 36 # pressed until defaulting to the failure condition. | |
| 37 timeout = 50 | |
| 38 | |
| 39 # How long to display the success message in seconds before exit. | |
| 40 pass_msg_timeout = 2 | |
| 41 | |
| 42 # Background color and alpha for the final result message. | |
| 43 bg_rgba_error = (0.7, 0, 0, 0.9) | |
| 44 bg_rgba_pass = ( 0, 0.7, 0, 0.9) | |
| 45 | |
| 46 # Highlight color and alpha to indicate activated keys. | |
| 47 rgba_press_and_release = ( 0, 0.5, 0, 0.6) | |
| 48 rgba_press_only = (0.6, 0.6, 0, 0.6) | |
| 49 | |
| 50 def __init__(self, image, exit_on_error=False): | |
| 51 self._image = image | |
| 52 self._exit_on_error = exit_on_error | |
| 53 self._deadline = None | |
| 54 self._success = None | |
| 55 self._buttons = {} | |
| 56 self._buttons[1] = Button(TouchpadTest.left_button_coords) | |
| 57 self._buttons[3] = Button(TouchpadTest.right_button_coords) | |
| 58 | |
| 59 def has_test_passed(self): | |
| 60 for key in self._buttons.keys(): | |
| 61 if (not self._buttons[key]._pressed or | |
| 62 not self._buttons[key]._released): | |
| 63 return False | |
| 64 return True | |
| 65 | |
| 66 def show_result(self, widget, context, text, bg_rgba): | |
| 67 widget_width, widget_height = widget.get_size_request() | |
| 68 context.save() | |
| 69 context.scale(widget_width / 1.0, widget_height / 1.0) | |
| 70 context.rectangle(0.05, 0.05, 0.9, 0.9) | |
| 71 context.set_source_rgba(*bg_rgba) | |
| 72 context.fill() | |
| 73 context.set_source_rgba(0.1, 0.1, 0.1, 0.95) | |
| 74 context.select_font_face( | |
| 75 'Verdana', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) | |
| 76 context.set_font_size(0.2) | |
| 77 x_bearing, y_bearing, width, height = context.text_extents(text)[:4] | |
| 78 context.move_to(0.5 - (width / 2) - x_bearing, | |
| 79 0.5 - (height / 2) - y_bearing) | |
| 80 context.show_text(text) | |
| 81 context.restore() | |
| 82 | |
| 83 def start_countdown(self, duration): | |
| 84 self._deadline = int(time.time()) + duration | |
| 85 | |
| 86 def show_countdown(self, widget, context): | |
| 87 countdown = self._deadline - int(time.time()) | |
| 88 width, height = widget.get_size_request() | |
| 89 text = '%3d' % countdown | |
| 90 context.save() | |
| 91 context.translate(width - 60, height) | |
| 92 context.set_source_rgb(0.5, 0.5, 0.5) | |
| 93 context.select_font_face( | |
| 94 'Courier New', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) | |
| 95 context.set_font_size(20) | |
| 96 x_bearing, y_bearing = context.text_extents('000')[:2] | |
| 97 context.move_to(x_bearing, y_bearing) | |
| 98 context.show_text(text) | |
| 99 context.restore() | |
| 100 | |
| 101 def timer_event(self, window): | |
| 102 if not self._deadline: | |
| 103 # Ignore timer events with no countdown in progress. | |
| 104 return True | |
| 105 if self._deadline <= time.time(): | |
| 106 self._deadline = None | |
| 107 if self._success is None: | |
| 108 self._success = False | |
| 109 if self._exit_on_error: | |
| 110 sys.exit(1) | |
| 111 elif self._success: | |
| 112 sys.exit(0) | |
| 113 window.queue_draw() | |
| 114 return True | |
| 115 | |
| 116 def expose_event(self, widget, event): | |
| 117 context = widget.window.cairo_create() | |
| 118 | |
| 119 # Show touchpad image as the background. | |
| 120 context.set_source_surface(self._image, 0, 0) | |
| 121 context.paint() | |
| 122 | |
| 123 # Boolean values for success correspond with test pass or | |
| 124 # failure. None means normal operation. | |
| 125 if self._success is None: | |
| 126 for key in self._buttons.keys(): | |
| 127 color = None | |
| 128 if self._buttons[key]._released: | |
| 129 color = TouchpadTest.rgba_press_and_release | |
| 130 elif self._buttons[key]._pressed: | |
| 131 color = TouchpadTest.rgba_press_only | |
| 132 else: | |
| 133 continue | |
| 134 coords = self._buttons[key]._coords | |
| 135 context.rectangle(*coords) | |
| 136 context.set_source_rgba(*color) | |
| 137 context.fill() | |
| 138 if self._deadline: | |
| 139 self.show_countdown(widget, context) | |
| 140 elif self._success: | |
| 141 self.show_result(widget, context, 'PASS', | |
| 142 TouchpadTest.bg_rgba_pass) | |
| 143 else: | |
| 144 self.show_result(widget, context, 'ERROR', | |
| 145 TouchpadTest.bg_rgba_error) | |
| 146 return False | |
| 147 | |
| 148 def button_press_event(self, widget, event): | |
| 149 if self._success is not None: | |
| 150 return True | |
| 151 if not event.button in self._buttons.keys(): | |
| 152 return True | |
| 153 | |
| 154 if self._deadline is None: | |
| 155 self.start_countdown(TouchpadTest.timeout) | |
| 156 self._buttons[event.button]._pressed = True | |
| 157 widget.queue_draw() | |
| 158 return True | |
| 159 | |
| 160 def button_release_event(self, widget, event): | |
| 161 if self._success is not None: | |
| 162 return True | |
| 163 if not event.button in self._buttons.keys(): | |
| 164 return True | |
| 165 | |
| 166 self._buttons[event.button]._released = True | |
| 167 if self.has_test_passed(): | |
| 168 self._success = True | |
| 169 self.start_countdown(TouchpadTest.pass_msg_timeout) | |
| 170 widget.queue_draw() | |
| 171 return True | |
| 172 | |
| 173 def main(): | |
| 174 window = gtk.Window(gtk.WINDOW_TOPLEVEL) | |
| 175 window.set_name('Touchpad Test') | |
| 176 window.connect('destroy', lambda w: gtk.main_quit()) | |
| 177 | |
| 178 bg_color = gtk.gdk.color_parse('midnight blue') | |
| 179 window.modify_bg(gtk.STATE_NORMAL, bg_color) | |
| 180 | |
| 181 touchpad_image = cairo.ImageSurface.create_from_png('touchpad.png') | |
| 182 touchpad_image_size = (touchpad_image.get_width(), | |
| 183 touchpad_image.get_height()) | |
| 184 | |
| 185 drawing_area = gtk.DrawingArea() | |
| 186 drawing_area.set_size_request(*touchpad_image_size) | |
| 187 | |
| 188 exit_on_error = False | |
| 189 if '--exit-on-error' in sys.argv: | |
| 190 exit_on_error = True | |
| 191 tt = TouchpadTest(touchpad_image, exit_on_error=exit_on_error) | |
| 192 screen = window.get_screen() | |
| 193 screen_size = (screen.get_width(), screen.get_height()) | |
| 194 window.set_default_size(*screen_size) | |
| 195 window.connect('button_press_event', tt.button_press_event) | |
| 196 window.connect('button_release_event', tt.button_release_event) | |
| 197 gobject.timeout_add(1000, tt.timer_event, window) | |
| 198 | |
| 199 drawing_area.connect('expose_event', tt.expose_event) | |
| 200 | |
| 201 drawing_area.show() | |
| 202 | |
| 203 align = gtk.Alignment(xalign=0.5, yalign=0.5) | |
| 204 align.add(drawing_area) | |
| 205 align.show() | |
| 206 | |
| 207 drawing_area.set_events(gtk.gdk.EXPOSURE_MASK | | |
| 208 gtk.gdk.BUTTON_PRESS_MASK | | |
| 209 gtk.gdk.BUTTON_RELEASE_MASK) | |
| 210 | |
| 211 window.add(align) | |
| 212 window.show() | |
| 213 | |
| 214 gtk.main() | |
| 215 | |
| 216 return (tt._success != True) | |
| 217 | |
| 218 if __name__ == '__main__': | |
| 219 main() | |
| OLD | NEW |