+from kivy.uix.relativelayout import RelativeLayout
+from kivy.properties import NumericProperty, ListProperty
+from kivy.core.window import Window
+from kivy.clock import Clock
+
import threading
-import pygame
-from .key import *
-
-class Mapping:
- WIDTH = 903
- HEIGHT = 298
- SIZE = WIDTH, HEIGHT
-
- KEYS = [
- (pygame.K_ESCAPE, 'ESC', 'first', 0, {}),
-
- (pygame.K_F1, 'F1', 'first', 100, {}),
- (pygame.K_F2, 'F2', 'first', 150, {}),
- (pygame.K_F3, 'F3', 'first', 200, {}),
- (pygame.K_F4, 'F4', 'first', 250, {}),
-
- (pygame.K_F5, 'F5', 'first', 325, {}),
- (pygame.K_F6, 'F6', 'first', 375, {}),
- (pygame.K_F7, 'F7', 'first', 425, {}),
- (pygame.K_F8, 'F8', 'first', 475, {}),
-
- (pygame.K_F9, 'F9', 'first', 550, {}),
- (pygame.K_F10, 'F10', 'first', 600, {}),
- (pygame.K_F11, 'F11', 'first', 650, {}),
- (pygame.K_F12, 'F12', 'first', 700, {}),
-
-
- (178, '²', 'second', 0, {}),
- (pygame.K_AMPERSAND, '&', 'second', 50, {}),
- (233, 'é', 'second', 100, {}),
- (pygame.K_QUOTEDBL, '"', 'second', 150, {}),
- (pygame.K_QUOTE, "'", 'second', 200, {}),
- (pygame.K_LEFTPAREN, '(', 'second', 250, {}),
- (pygame.K_MINUS, '-', 'second', 300, {}),
- (232, 'è', 'second', 350, {}),
- (pygame.K_UNDERSCORE, '_', 'second', 400, {}),
- (231, 'ç', 'second', 450, {}),
- (224, 'à', 'second', 500, {}),
- (pygame.K_RIGHTPAREN, ')', 'second', 550, {}),
- (pygame.K_EQUALS, '=', 'second', 600, {}),
-
- (pygame.K_BACKSPACE, '<-', 'second', 650, { 'width': 98 }),
-
-
- (pygame.K_TAB, 'tab', 'third', 0, { 'width' : 73 }),
- (pygame.K_a, 'a', 'third', 75, {}),
- (pygame.K_z, 'z', 'third', 125, {}),
- (pygame.K_e, 'e', 'third', 175, {}),
- (pygame.K_r, 'r', 'third', 225, {}),
- (pygame.K_t, 't', 'third', 275, {}),
- (pygame.K_y, 'y', 'third', 325, {}),
- (pygame.K_u, 'u', 'third', 375, {}),
- (pygame.K_i, 'i', 'third', 425, {}),
- (pygame.K_o, 'o', 'third', 475, {}),
- (pygame.K_p, 'p', 'third', 525, {}),
- (pygame.K_CARET, '^', 'third', 575, {}),
- (pygame.K_DOLLAR, '$', 'third', 625, {}),
-
- (pygame.K_RETURN, 'Enter', 'third', 692, { 'width': 56, 'height': 98 }),
-
- (pygame.K_CAPSLOCK, 'CAPS', 'fourth', 0, { 'width': 88, 'disabled': True }),
-
- (pygame.K_q, 'q', 'fourth', 90, {}),
- (pygame.K_s, 's', 'fourth', 140, {}),
- (pygame.K_d, 'd', 'fourth', 190, {}),
- (pygame.K_f, 'f', 'fourth', 240, {}),
- (pygame.K_g, 'g', 'fourth', 290, {}),
- (pygame.K_h, 'h', 'fourth', 340, {}),
- (pygame.K_j, 'j', 'fourth', 390, {}),
- (pygame.K_k, 'k', 'fourth', 440, {}),
- (pygame.K_l, 'l', 'fourth', 490, {}),
- (pygame.K_m, 'm', 'fourth', 540, {}),
- (249, 'ù', 'fourth', 590, {}),
- (pygame.K_ASTERISK, '*', 'fourth', 640, {}),
-
-
- (pygame.K_LSHIFT, 'LShift', 'fifth', 0, { 'width': 63, 'disabled': True }),
-
- (pygame.K_LESS, '<', 'fifth', 65, {}),
- (pygame.K_w, 'w', 'fifth', 115, {}),
- (pygame.K_x, 'x', 'fifth', 165, {}),
- (pygame.K_c, 'c', 'fifth', 215, {}),
- (pygame.K_v, 'v', 'fifth', 265, {}),
- (pygame.K_b, 'b', 'fifth', 315, {}),
- (pygame.K_n, 'n', 'fifth', 365, {}),
- (pygame.K_COMMA, ',', 'fifth', 415, {}),
- (pygame.K_SEMICOLON, ';', 'fifth', 465, {}),
- (pygame.K_COLON, ':', 'fifth', 515, {}),
- (pygame.K_EXCLAIM, '!', 'fifth', 565, {}),
-
- (pygame.K_RSHIFT, 'RShift', 'fifth', 615, { 'width': 133, 'disabled': True }),
-
- (pygame.K_LCTRL, 'LCtrl', 'sixth', 0, { 'width': 63, 'disabled': True }),
- (pygame.K_LSUPER, 'LSuper', 'sixth', 115, { 'disabled': True }),
- (pygame.K_LALT, 'LAlt', 'sixth', 165, { 'disabled': True }),
- (pygame.K_SPACE, 'Espace', 'sixth', 215, { 'width': 248 }),
- (pygame.K_MODE, 'AltGr', 'sixth', 465, { 'disabled': True }),
- (314, 'Compose', 'sixth', 515, { 'disabled': True }),
- (pygame.K_RCTRL, 'RCtrl', 'sixth', 565, { 'width': 63, 'disabled': True }),
-
-
- (pygame.K_INSERT, 'ins', 'second', 755, {}),
- (pygame.K_HOME, 'home', 'second', 805, {}),
- (pygame.K_PAGEUP, 'pg_u', 'second', 855, {}),
- (pygame.K_DELETE, 'del', 'third', 755, {}),
- (pygame.K_END, 'end', 'third', 805, {}),
- (pygame.K_PAGEDOWN, 'pg_d', 'third', 855, {}),
-
-
- (pygame.K_UP, 'up', 'fifth', 805, {}),
- (pygame.K_DOWN, 'down', 'sixth', 805, {}),
- (pygame.K_LEFT, 'left', 'sixth', 755, {}),
- (pygame.K_RIGHT, 'right', 'sixth', 855, {}),
- ]
-
- def __init__(self, screen, draw_lock):
- self.draw_lock = draw_lock
- self.screen = screen
- self.background = pygame.Surface(self.SIZE).convert()
- self.background.fill((250, 250, 250))
- self.keys = {}
+import yaml
+import sys
+
+from .music_file import *
+from . import yml_file,gain
+
+class Mapping(RelativeLayout):
+ expected_keys = NumericProperty(0)
+ master_volume = NumericProperty(100)
+ ready_color = ListProperty([1, 165/255, 0, 1])
+
+ def __init__(self, **kwargs):
+ self.key_config, self.open_files = self.parse_config()
+ super(Mapping, self).__init__(**kwargs)
+ self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
+ self._keyboard.bind(on_key_down=self._on_keyboard_down)
self.running = []
- for key in self.KEYS:
- self.keys[key[0]] = Key(self, self.draw_lock, *key[0:4], **key[4])
-
- def draw(self):
- for key_name in self.keys:
- key = self.keys[key_name]
- should_redraw_key = key.draw(self.background)
-
- if should_redraw_key:
- threading.Thread(name = "MSPollRedraw", target = key.poll_redraw, args = [self.background]).start()
- self.blit()
-
- def blit(self):
- self.draw_lock.acquire()
- self.screen.blit(self.background, (5, 5))
- pygame.display.flip()
- self.draw_lock.release()
-
- def find_by_key_num(self, key_num):
- if key_num in self.keys:
- return self.keys[key_num]
- return None
-
- def find_by_collidepoint(self, position):
- for key in self.keys:
- if self.keys[key].collidepoint(position):
- return self.keys[key]
+ Clock.schedule_interval(self.not_all_keys_ready, 1)
+
+
+ @property
+ def master_gain(self):
+ return gain(self.master_volume)
+
+ def set_master_volume(self, value, delta = False):
+ [db_gain, self.master_volume] = gain(value + int(delta) * self.master_volume, self.master_volume)
+ for music in self.open_files.values():
+ music.set_gain(db_gain)
+
+ def _keyboard_closed(self):
+ self._keyboard.unbind(on_key_down=self._on_keyboard_down)
+ self._keyboard = None
+
+ def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
+ key = self.find_by_key_code(keycode)
+ if len(modifiers) == 0 and key is not None:
+ threading.Thread(name = "MSKeyAction", target=key.do_actions).start()
+ elif 'ctrl' in modifiers and (keycode[0] == 113 or keycode[0] == '99'):
+ for thread in threading.enumerate():
+ if thread.getName()[0:2] != "MS":
+ continue
+ thread.join()
+
+ sys.exit()
+ return True
+
+ def find_by_key_code(self, key_code):
+ if "Key_" + str(key_code[0]) in self.ids:
+ return self.ids["Key_" + str(key_code[0])]
return None
- def find_by_unicode(self, key_sym):
- for key in self.keys:
- if self.keys[key].key_sym == key_sym:
- return self.keys[key]
- return None
+ def not_all_keys_ready(self, dt):
+ for key in self.children:
+ if not type(key).__name__ == "Key":
+ continue
+ if not key.is_key_ready:
+ return True
+ self.ready_color = [0, 1, 0, 1]
+ return False
def stop_all_running(self):
+ running = self.running
self.running = []
+ for (key, start_time) in running:
+ key.interrupt_action()
def start_running(self, key, start_time):
self.running.append((key, start_time))
if (key, start_time) in self.running:
self.running.remove((key, start_time))
+ def parse_config(self):
+ stream = open(yml_file(), "r")
+ config = yaml.load(stream)
+ stream.close()
+
+ aliases = config['aliases']
+ seen_files = {}
+
+ key_properties = {}
+
+ for key in config['key_properties']:
+ if key not in key_properties:
+ key_prop = config['key_properties'][key]
+ if 'include' in key_prop:
+ included = key_prop['include']
+ del(key_prop['include'])
+
+ if isinstance(included, str):
+ key_prop.update(aliases[included], **key_prop)
+ else:
+ for included_ in included:
+ key_prop.update(aliases[included_], **key_prop)
+
+ key_properties[key] = {
+ "actions": [],
+ "properties": key_prop,
+ "files": []
+ }
+
+ for mapped_key in config['keys']:
+ if mapped_key not in key_properties:
+ key_properties[mapped_key] = {
+ "actions": [],
+ "properties": {},
+ "files": []
+ }
+ for action in config['keys'][mapped_key]:
+ action_name = list(action)[0]
+ action_args = {}
+ if action[action_name] is None:
+ action[action_name] = []
+
+ if 'include' in action[action_name]:
+ included = action[action_name]['include']
+ del(action[action_name]['include'])
+
+ if isinstance(included, str):
+ action[action_name].update(aliases[included], **action[action_name])
+ else:
+ for included_ in included:
+ action[action_name].update(aliases[included_], **action[action_name])
+
+ for argument in action[action_name]:
+ if argument == 'file':
+ filename = action[action_name]['file']
+ if filename not in seen_files:
+ if filename in config['music_properties']:
+ seen_files[filename] = MusicFile(
+ filename,
+ self,
+ **config['music_properties'][filename])
+ else:
+ seen_files[filename] = MusicFile(
+ self,
+ filename)
+
+ if filename not in key_properties[mapped_key]['files']:
+ key_properties[mapped_key]['files'].append(seen_files[filename])
+
+ action_args['music'] = seen_files[filename]
+
+ else:
+ action_args[argument] = action[action_name][argument]
+
+ key_properties[mapped_key]['actions'].append([action_name, action_args])
+
+ return (key_properties, seen_files)
+
+