#!/usr/bin/python """Describes the physical layout of the keyboard""" from color import Color, Colors # Keys are described by the "pixels" they cover # Y is in rows, the 0 row has the space bar, the the 5 row has the function # keys, and the 6 row has the light, lock, and mute buttons # Column values; there are 2 columns per normal width key. This isn't exact, # but I think it would be close enough. There are 22 key widths, so 44 columns # in the grid. # FIXME: Should add a column for the empty space on either side of the cursor # keys, making it 46 columns total. def _key_row(left, row, keys): """Utility function to reduce how many hardcoded numbers are required in the key positions description. """ entries = [] for n, key in enumerate(keys): entries.append( (key, [(left+2*n, row), (left+2*n+1, row)]) ) return entries key_positions = dict( # Top row [ ("light", [(30, 6), (31, 6)]), ("lock", [(32, 6), (33, 6)]), ("mute", [(38, 6), (39, 6)]), #("volup", [(41, 6), (42, 6)]), # Volume scrollwheel does not have a backlight #("voldn", [(41, 6), (42, 6)]), # Function key row ("esc", [(0, 5), (1, 5)]), ] + _key_row(4, 5, [ "f1", "f2", "f3", "f4", ]) + [ ] + _key_row(13, 5, [ "f5", "f6", "f7", "f8", ]) + [ ] + _key_row(22, 5, [ "f9", "f10", "f11", "f12", ]) + [ ] + _key_row(30, 5, [ "prtscn", "scroll", "pause", ]) + [ ] + _key_row(36, 5, [ "stop", "prev", "play", "next", ]) + [ # Number row ] + _key_row(0, 4, [ "grave", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "minus", "equal", ]) + [ ("bspace", [(26, 4), (27, 4), (28, 4), (29, 4)]), ] + _key_row(30, 4, [ "ins", "home", "pgup", "numlock", "numslash", "numstar", "numminus", ]) + [ # QWERTY row ("tab", [(0, 3), (1, 3), (2, 3)]), ] + _key_row(3, 3, [ "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "lbrace", "rbrace", ]) + [ ("bslash", [(27, 3), (28, 3), (29, 3)]), ] + _key_row(30, 3, [ "del", "end", "pgdn", "num7", "num8", "num9", ]) + [ ("numplus", [(42, 3), (43, 3), (42, 4), (43, 4)]), # Note this extends into the row below # Home row ("caps", [(0, 2), (1, 2), (2, 2)]), ] + _key_row(3, 2, [ "a", "s", "d", "f", "g", "h", "j", "k", "l", "colon", "quote", ]) + [ ("enter", [(26, 2), (27, 2), (28, 2), (29, 2)]), ] + _key_row(36, 2, [ "num4", "num5", "num6", ]) + [ # Shift row ("lshift", [(0, 1), (1, 1), (2, 1), (3, 1)]), ] + _key_row(4, 1, [ "z", "x", "c", "v", "b", "n", "m", "comma", "dot", "slash", ]) + [ ("rshift", [(24, 1), (25, 1), (26, 1), (27, 1), (28, 1), (29, 1)]), ("up", [(32, 1), (33, 1)]), ] + _key_row(36, 1, [ "num1", "num2", "num3", ]) + [ ("numenter", [(42, 1), (43, 1), (42, 0), (43, 0)]), # Extends into row below # Bottom row ("lctrl", [(0, 0), (1, 0), (2, 0)]), ("lwin", [(3, 0), (4, 0)]), ("lalt", [(5, 0), (6, 0), (7, 0)]), ("space", [(x, 0) for x in range(8, 20)]), ("ralt", [(20, 0), (21, 0), (22, 0)]), ("rwin", [(23, 0), (24, 0)]), ("rmenu", [(25, 0), (26, 0)]), ("rctrl", [(27, 0), (28, 0), (29, 0)]), ] + _key_row(30, 0, [ "left", "down", "right", ]) + [ ("num0", [(36, 0), (37, 0), (38, 0), (39, 0)]), ("numdot", [(40, 0), (41, 0)]), # The K70 does not have the extra keys #("g1", [(x, y), (x, y)]), #("g2", [(x, y), (x, y)]), #("g3", [(x, y), (x, y)]), #("g4", [(x, y), (x, y)]), #("g5", [(x, y), (x, y)]), #("g6", [(x, y), (x, y)]), #("g7", [(x, y), (x, y)]), #("g8", [(x, y), (x, y)]), #("g9", [(x, y), (x, y)]), #("g10", [(x, y), (x, y)]), #("g11", [(x, y), (x, y)]), #("g12", [(x, y), (x, y)]), #("g13", [(x, y), (x, y)]), #("g14", [(x, y), (x, y)]), #("g15", [(x, y), (x, y)]), #("g16", [(x, y), (x, y)]), #("g17", [(x, y), (x, y)]), #("g18", [(x, y), (x, y)]), #("mr", [(x, y), (x, y)]), #("m1", [(x, y), (x, y)]), #("m2", [(x, y), (x, y)]), #("m3", [(x, y), (x, y)]), ] ) class KeyMatrix(object): """(X,Y)<->Key mapping object to allow treating the keyboard as an image grid. """ x = 44 y = 7 def __init__(self, background=Colors.BLACK): # 2-dimensional array addressible as self.data[x][y] self.data = [[background for y in range(self.y)] for x in range(self.x)] def set_pixel(self, x, y, color): """Sets a pixel to a particular color.""" try: self.data[x][y] = color except IndexError, error: print "Failed dereferencing (%s,%s)" % (x, y) def set_key(self, key, color): """Sets all pixels for a key to a particular color.""" for x, y in key_positions[key]: self.set_pixel(x, y, color) def get_key(self, key): """Returns the average color of the pixels for a key.""" colors = [self.data[x][y] for x,y in key_positions[key]] # There is also a case for taking the mode instead of the mean # However, for that to make a meaningful difference, we'd probably need to # allocate more pixels to each key return Color.average(colors) class Keys(object): """Shortcut names for logical groups of keys on a keyboard""" ALL = ','.join(key_positions.keys()) HOME = "a,s,d,f,j,k,l,colon" WASD = "w,a,s,d" FUNCTION = "f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12" NUM_PAD = \ "num0," \ "num1," \ "num2," \ "num3," \ "num4," \ "num5," \ "num6," \ "num7," \ "num8," \ "num9," \ "numdot," \ "numenter," \ "numlock," \ "numminus," \ "numplus," \ "numslash," \ "numstar" MEDIA = \ "mute," \ "stop," \ "prev," \ "play," \ "next" MACRO = \ "g1," \ "g2," \ "g3," \ "g4," \ "g5," \ "g6," \ "g7," \ "g8," \ "g9," \ "g10," \ "g11," \ "g12," \ "g13," \ "g14," \ "g15," \ "g16," \ "g17," \ "g18" ARROW = \ "up," \ "left," \ "down," \ "right" HOME_GROUP = \ "ins," \ "home," \ "pgup," \ "del," \ "end," \ "pgdn" NAV = ','.join([ARROW, HOME_GROUP]) MOD = \ "lctrl," \ "lwin," \ "lalt," \ "lshift," \ "rmenu," \ "rctrl," \ "rwin," \ "ralt," \ "rshift" # Typing keys are the keys that should count for WPM calculations TYPING = \ "grave," \ "1," \ "2," \ "3," \ "4," \ "5," \ "6," \ "7," \ "8," \ "9," \ "0," \ "minus," \ "tab," \ "q," \ "w," \ "e," \ "r," \ "t," \ "y," \ "u," \ "i," \ "o," \ "p," \ "lbrace," \ "a," \ "s," \ "d," \ "f," \ "g," \ "h," \ "j," \ "k," \ "l," \ "colon," \ "quote," \ "z," \ "x," \ "c," \ "v," \ "b," \ "n," \ "m," \ "comma," \ "dot," \ "slash," \ "space," \ "f12," \ "rbrace," \ "bslash," \ "enter," \ "equal," \ "numslash," \ "numstar," \ "numminus," \ "numplus," \ "numenter," \ "num7," \ "num8," \ "num9," \ "num4," \ "num5," \ "num6," \ "num1," \ "num2," \ "num3," \ "num0," \ "numdot"