| #!/usr/bin/python |
| # |
| # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import cairo |
| import gobject |
| import gtk |
| import sys |
| import time |
| import os |
| |
| |
| _OUTPUT_HEADER = \ |
| ''' |
| # AUTOMATICALLY GENERATED -- This data structure is printed out by |
| # BindingsSetup during execution, and modifications can be pasted from |
| # there back to here. |
| ''' |
| |
| |
| class BindingsSetup: |
| """Facilitate generation of the binding map for the actual |
| KeyboardTest -- it takes the name of a png keyboard image file and |
| optionally a corresponding bindings file as its first and second |
| command line arguments, respectively. On each assignment of a new |
| binding, it will output the contents for a new bindings file to |
| stdout (a bit verbose, but simple). |
| |
| UI -- select key region to be highlighted with the mouse, hit |
| corresponding key, double click to active tweak mode, fine tune |
| highlighted region with the arrow keys (hold shift for neg |
| effect), double click to confirm and output current bindings |
| datastructure, repeat.""" |
| |
| # Allow tweak mode adjustment of coords using the arrow keys. |
| tweak_keys = { |
| 0xff51 : [1, 0, 0, 0], |
| 0xff52 : [0, 1, 0, 0], |
| 0xff53 : [0, 0, 1, 0], |
| 0xff54 : [0, 0, 0, 1]} |
| |
| def __init__(self, kbd_image, bindings): |
| self._kbd_image = kbd_image |
| self._press_xy = None |
| self._last_coords = None |
| self._last_key = None |
| self._tweak_mode = False |
| self._bindings = bindings |
| |
| def fmt_coords(self, coords): |
| return '%3d,%-3d %2dx%-2d' % coords |
| |
| def fmt_key(self, key): |
| return '%4x,%-4x' % key |
| |
| def fmt_binding(self, binding): |
| key, coords = binding |
| return '0x%-4x : %s' % (key, '(%3d,%3d,%3d,%3d)' % coords) |
| |
| def tweak_coords(self, d_lft, d_top, d_rgt, d_bot): |
| xmin, ymin, xdelta, ydelta = self._last_coords |
| xmin -= d_lft |
| ymin -= d_top |
| xdelta += d_lft + d_rgt |
| ydelta += d_top + d_bot |
| self._last_coords = (xmin, ymin, xdelta, ydelta) |
| |
| def confirm_binding(self): |
| kc, kv = self._last_key |
| self._bindings[kc] = self._last_coords |
| binding_list = [self.fmt_binding(b) for b in |
| sorted(self._bindings.items())] |
| print _OUTPUT_HEADER |
| print ('\n{\n%s\n}' % ',\n'.join(' %s' % b for b in binding_list)) |
| |
| def expose_event(self, widget, event): |
| context = widget.window.cairo_create() |
| context.set_source_surface(self._kbd_image, 0, 0) |
| context.paint() |
| if self._last_coords: |
| context.rectangle(*self._last_coords) |
| context.set_source_rgba(0, 0.5, 0, 0.6) |
| context.fill() |
| return False |
| |
| def button_press_event(self, widget, event): |
| if not event.button == 1: |
| return False |
| if self._tweak_mode: |
| if event.type == gtk.gdk._2BUTTON_PRESS: |
| self.confirm_binding() |
| self._tweak_mode = False |
| self._press_xy = None |
| self._last_coords = None |
| self._last_key = None |
| else: |
| if (event.type == gtk.gdk._2BUTTON_PRESS and |
| self._last_coords and self._last_key): |
| self._tweak_mode = True |
| print 'tweak mode' |
| else: |
| self._press_xy = (event.x, event.y) |
| return True |
| |
| def button_release_event(self, widget, event): |
| if not event.button == 1: |
| return False |
| if (not self._tweak_mode) and self._press_xy: |
| px, py = self._press_xy |
| xmin, xmax = sorted([px, event.x]) |
| ymin, ymax = sorted([py, event.y]) |
| xdelta = xmax - xmin |
| ydelta = ymax - ymin |
| if xdelta and ydelta: |
| self._last_coords = (xmin, ymin, xdelta, ydelta) |
| print 'coords %s' % self.fmt_coords(self._last_coords) |
| widget.queue_draw() |
| return True |
| |
| def key_press_event(self, widget, event): |
| key = (event.keyval, event.hardware_keycode) |
| if self._tweak_mode: |
| delta = BindingsSetup.tweak_keys.get(event.keyval) |
| if delta: |
| if 'GDK_SHIFT_MASK' in event.state.value_names: |
| # Reverse the effect if SHIFT is being held down. |
| delta = map((lambda x: 0 - x), delta) |
| self.tweak_coords(*delta) |
| print 'tweak %s --> %s' % (self.fmt_key(self._last_key), |
| self.fmt_coords(self._last_coords)) |
| widget.queue_draw() |
| else: |
| self._last_key = key |
| print 'key %s %s' % (self.fmt_key(key), repr(event.string)) |
| return True |
| |
| def main(): |
| window = gtk.Window(gtk.WINDOW_TOPLEVEL) |
| window.connect('destroy', lambda w: gtk.main_quit()) |
| |
| window.set_default_size(981, 450) |
| |
| bg_color = gtk.gdk.color_parse('midnight blue') |
| window.modify_bg(gtk.STATE_NORMAL, bg_color) |
| |
| kbd_image = cairo.ImageSurface.create_from_png(sys.argv[1]) |
| kbd_image_size = (kbd_image.get_width(), kbd_image.get_height()) |
| |
| bindings = {} |
| if len(sys.argv) == 3 and os.path.exists(sys.argv[2]): |
| f = open(sys.argv[2], 'r') |
| bindings = eval(f.read()) |
| f.close() |
| |
| drawing_area = gtk.DrawingArea() |
| drawing_area.set_size_request(*kbd_image_size) |
| |
| kt = BindingsSetup(kbd_image, bindings) |
| window.connect('key-press-event', kt.key_press_event) |
| drawing_area.connect('button_release_event', kt.button_release_event) |
| drawing_area.connect('button_press_event', kt.button_press_event) |
| drawing_area.connect('expose_event', kt.expose_event) |
| |
| drawing_area.show() |
| |
| align = gtk.Alignment(xalign=0.5, yalign=0.5) |
| align.add(drawing_area) |
| align.show() |
| |
| drawing_area.set_events(gtk.gdk.EXPOSURE_MASK | |
| gtk.gdk.KEY_PRESS_MASK | |
| gtk.gdk.BUTTON_PRESS_MASK | |
| gtk.gdk.BUTTON_RELEASE_MASK) |
| |
| window.add(align) |
| window.show() |
| |
| gtk.main() |
| |
| if __name__ == '__main__': |
| main() |