Added support for multimedia keyboard button simulation for controlling the host.
parent
2b3d81d007
commit
866a3e852e
|
@ -17,7 +17,7 @@ import dateutil.parser
|
|||
from phue import Bridge
|
||||
import requests
|
||||
|
||||
from .packages.pychromecast import pychromecast
|
||||
from .packages import pychromecast, pykeyboard
|
||||
|
||||
from . import track_state_change
|
||||
from .util import sanitize_filename
|
||||
|
@ -35,7 +35,10 @@ EVENT_BROWSE_URL = "browse_url"
|
|||
EVENT_CHROMECAST_YOUTUBE_VIDEO = "chromecast.play_youtube_video"
|
||||
EVENT_TURN_LIGHT_ON = "turn_light_on"
|
||||
EVENT_TURN_LIGHT_OFF = "turn_light_off"
|
||||
|
||||
EVENT_KEYBOARD_VOLUME_UP = "keyboard.volume_up"
|
||||
EVENT_KEYBOARD_VOLUME_DOWN = "keyboard.volume_down"
|
||||
EVENT_KEYBOARD_VOLUME_MUTE = "keyboard.volume_mute"
|
||||
EVENT_KEYBOARD_MEDIA_PLAY_PAUSE = "keyboard.media_play_pause"
|
||||
|
||||
def _hue_process_transition_time(transition_seconds):
|
||||
""" Transition time is in 1/10th seconds
|
||||
|
@ -313,3 +316,20 @@ def setup_chromecast(eventbus, host):
|
|||
|
||||
eventbus.listen(EVENT_CHROMECAST_YOUTUBE_VIDEO,
|
||||
lambda event: pychromecast.play_youtube_video(host, event.data['video']))
|
||||
|
||||
def setup_media_buttons(eventbus):
|
||||
""" Listen for keyboard events. """
|
||||
keyboard = pykeyboard.PyKeyboard()
|
||||
keyboard.special_key_assignment()
|
||||
|
||||
eventbus.listen(EVENT_KEYBOARD_VOLUME_UP,
|
||||
lambda event: keyboard.tap_key(keyboard.volume_up_key))
|
||||
|
||||
eventbus.listen(EVENT_KEYBOARD_VOLUME_DOWN,
|
||||
lambda event: keyboard.tap_key(keyboard.volume_down_key))
|
||||
|
||||
eventbus.listen(EVENT_KEYBOARD_VOLUME_MUTE,
|
||||
lambda event: keyboard.tap_key(keyboard.volume_mute_key))
|
||||
|
||||
eventbus.listen(EVENT_KEYBOARD_MEDIA_PLAY_PAUSE,
|
||||
lambda event: keyboard.tap_key(keyboard.media_play_pause_key))
|
||||
|
|
|
@ -2,4 +2,15 @@
|
|||
Not all external Git repositories that we depend on are
|
||||
available as a package for pip. That is why we include
|
||||
them here.
|
||||
|
||||
PyChromecast
|
||||
------------
|
||||
https://github.com/balloob/pychromecast
|
||||
|
||||
PyKeyboard
|
||||
----------
|
||||
Forked from https://github.com/SavinaRoja/PyUserInput
|
||||
Patched with some code to get it working on OS X:
|
||||
https://github.com/balloob/PyUserInput
|
||||
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
#Copyright 2013 Paul Barton
|
||||
#
|
||||
#This program is free software: you can redistribute it and/or modify
|
||||
#it under the terms of the GNU General Public License as published by
|
||||
#the Free Software Foundation, either version 3 of the License, or
|
||||
#(at your option) any later version.
|
||||
#
|
||||
#This program is distributed in the hope that it will be useful,
|
||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
#GNU General Public License for more details.
|
||||
#
|
||||
#You should have received a copy of the GNU General Public License
|
||||
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
The goal of PyMouse is to have a cross-platform way to control the mouse.
|
||||
PyMouse should work on Windows, Mac and any Unix that has xlib.
|
||||
|
||||
PyKeyboard is a part of PyUserInput, along with PyMouse, for more information
|
||||
about this project, see:
|
||||
http://github.com/SavinaRoja/PyUserInput
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
if sys.platform.startswith('java'):
|
||||
from .java_ import PyKeyboard
|
||||
|
||||
elif sys.platform == 'darwin':
|
||||
from .mac import PyKeyboard, PyKeyboardEvent
|
||||
|
||||
elif sys.platform == 'win32':
|
||||
from .windows import PyKeyboard, PyKeyboardEvent
|
||||
|
||||
else:
|
||||
from .x11 import PyKeyboard, PyKeyboardEvent
|
|
@ -0,0 +1,102 @@
|
|||
#Copyright 2013 Paul Barton
|
||||
#
|
||||
#This program is free software: you can redistribute it and/or modify
|
||||
#it under the terms of the GNU General Public License as published by
|
||||
#the Free Software Foundation, either version 3 of the License, or
|
||||
#(at your option) any later version.
|
||||
#
|
||||
#This program is distributed in the hope that it will be useful,
|
||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
#GNU General Public License for more details.
|
||||
#
|
||||
#You should have received a copy of the GNU General Public License
|
||||
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
As the base file, this provides a rough operational model along with the
|
||||
framework to be extended by each platform.
|
||||
"""
|
||||
|
||||
import time
|
||||
from threading import Thread
|
||||
|
||||
class PyKeyboardMeta(object):
|
||||
"""
|
||||
The base class for PyKeyboard. Represents basic operational model.
|
||||
"""
|
||||
|
||||
def press_key(self, character=''):
|
||||
"""Press a given character key."""
|
||||
raise NotImplementedError
|
||||
|
||||
def release_key(self, character=''):
|
||||
"""Release a given character key."""
|
||||
raise NotImplementedError
|
||||
|
||||
def tap_key(self, character='', n=1, interval=0):
|
||||
"""Press and release a given character key n times."""
|
||||
for i in xrange(n):
|
||||
self.press_key(character)
|
||||
self.release_key(character)
|
||||
time.sleep(interval)
|
||||
|
||||
def type_string(self, char_string, interval=0):
|
||||
"""A convenience method for typing longer strings of characters."""
|
||||
for i in char_string:
|
||||
time.sleep(interval)
|
||||
self.tap_key(i)
|
||||
|
||||
def special_key_assignment(self):
|
||||
"""Makes special keys more accessible."""
|
||||
raise NotImplementedError
|
||||
|
||||
def lookup_character_value(self, character):
|
||||
"""
|
||||
If necessary, lookup a valid API value for the key press from the
|
||||
character.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_char_shifted(self, character):
|
||||
"""Returns True if the key character is uppercase or shifted."""
|
||||
if character.isupper():
|
||||
return True
|
||||
if character in '<>?:"{}|~!@#$%^&*()_+':
|
||||
return True
|
||||
return False
|
||||
|
||||
class PyKeyboardEventMeta(Thread):
|
||||
"""
|
||||
The base class for PyKeyboard. Represents basic operational model.
|
||||
"""
|
||||
def __init__(self, capture=False):
|
||||
Thread.__init__(self)
|
||||
self.daemon = True
|
||||
self.capture = capture
|
||||
self.state = True
|
||||
|
||||
def run(self):
|
||||
self.state = True
|
||||
|
||||
def stop(self):
|
||||
self.state = False
|
||||
|
||||
def handler(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def key_press(self, key):
|
||||
"""Subclass this method with your key press event handler."""
|
||||
pass
|
||||
|
||||
def key_release(self, key):
|
||||
"""Subclass this method with your key release event handler."""
|
||||
pass
|
||||
|
||||
def escape_code(self):
|
||||
"""
|
||||
Defines a means to signal a stop to listening. Subclass this with your
|
||||
escape behavior.
|
||||
"""
|
||||
escape = None
|
||||
return escape
|
|
@ -0,0 +1,14 @@
|
|||
#Copyright 2013 Paul Barton
|
||||
#
|
||||
#This program is free software: you can redistribute it and/or modify
|
||||
#it under the terms of the GNU General Public License as published by
|
||||
#the Free Software Foundation, either version 3 of the License, or
|
||||
#(at your option) any later version.
|
||||
#
|
||||
#This program is distributed in the hope that it will be useful,
|
||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
#GNU General Public License for more details.
|
||||
#
|
||||
#You should have received a copy of the GNU General Public License
|
||||
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,201 @@
|
|||
#Copyright 2013 Paul Barton
|
||||
#
|
||||
#This program is free software: you can redistribute it and/or modify
|
||||
#it under the terms of the GNU General Public License as published by
|
||||
#the Free Software Foundation, either version 3 of the License, or
|
||||
#(at your option) any later version.
|
||||
#
|
||||
#This program is distributed in the hope that it will be useful,
|
||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
#GNU General Public License for more details.
|
||||
#
|
||||
#You should have received a copy of the GNU General Public License
|
||||
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import time
|
||||
from Quartz import *
|
||||
from AppKit import NSEvent
|
||||
from .base import PyKeyboardMeta, PyKeyboardEventMeta
|
||||
|
||||
# Taken from events.h
|
||||
# /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
|
||||
character_translate_table = {
|
||||
'a': 0x00,
|
||||
's': 0x01,
|
||||
'd': 0x02,
|
||||
'f': 0x03,
|
||||
'h': 0x04,
|
||||
'g': 0x05,
|
||||
'z': 0x06,
|
||||
'x': 0x07,
|
||||
'c': 0x08,
|
||||
'v': 0x09,
|
||||
'b': 0x0b,
|
||||
'q': 0x0c,
|
||||
'w': 0x0d,
|
||||
'e': 0x0e,
|
||||
'r': 0x0f,
|
||||
'y': 0x10,
|
||||
't': 0x11,
|
||||
'1': 0x12,
|
||||
'2': 0x13,
|
||||
'3': 0x14,
|
||||
'4': 0x15,
|
||||
'6': 0x16,
|
||||
'5': 0x17,
|
||||
'=': 0x18,
|
||||
'9': 0x19,
|
||||
'7': 0x1a,
|
||||
'-': 0x1b,
|
||||
'8': 0x1c,
|
||||
'0': 0x1d,
|
||||
']': 0x1e,
|
||||
'o': 0x1f,
|
||||
'u': 0x20,
|
||||
'[': 0x21,
|
||||
'i': 0x22,
|
||||
'p': 0x23,
|
||||
'l': 0x25,
|
||||
'j': 0x26,
|
||||
'\'': 0x27,
|
||||
'k': 0x28,
|
||||
';': 0x29,
|
||||
'\\': 0x2a,
|
||||
',': 0x2b,
|
||||
'/': 0x2c,
|
||||
'n': 0x2d,
|
||||
'm': 0x2e,
|
||||
'.': 0x2f,
|
||||
'`': 0x32,
|
||||
' ': 0x31,
|
||||
'\r': 0x24,
|
||||
'\t': 0x30,
|
||||
'shift': 0x38
|
||||
}
|
||||
|
||||
# Taken from ev_keymap.h
|
||||
# http://www.opensource.apple.com/source/IOHIDFamily/IOHIDFamily-86.1/IOHIDSystem/IOKit/hidsystem/ev_keymap.h
|
||||
special_key_translate_table = {
|
||||
'KEYTYPE_SOUND_UP': 0,
|
||||
'KEYTYPE_SOUND_DOWN': 1,
|
||||
'KEYTYPE_BRIGHTNESS_UP': 2,
|
||||
'KEYTYPE_BRIGHTNESS_DOWN': 3,
|
||||
'KEYTYPE_CAPS_LOCK': 4,
|
||||
'KEYTYPE_HELP': 5,
|
||||
'POWER_KEY': 6,
|
||||
'KEYTYPE_MUTE': 7,
|
||||
'UP_ARROW_KEY': 8,
|
||||
'DOWN_ARROW_KEY': 9,
|
||||
'KEYTYPE_NUM_LOCK': 10,
|
||||
'KEYTYPE_CONTRAST_UP': 11,
|
||||
'KEYTYPE_CONTRAST_DOWN': 12,
|
||||
'KEYTYPE_LAUNCH_PANEL': 13,
|
||||
'KEYTYPE_EJECT': 14,
|
||||
'KEYTYPE_VIDMIRROR': 15,
|
||||
'KEYTYPE_PLAY': 16,
|
||||
'KEYTYPE_NEXT': 17,
|
||||
'KEYTYPE_PREVIOUS': 18,
|
||||
'KEYTYPE_FAST': 19,
|
||||
'KEYTYPE_REWIND': 20,
|
||||
'KEYTYPE_ILLUMINATION_UP': 21,
|
||||
'KEYTYPE_ILLUMINATION_DOWN': 22,
|
||||
'KEYTYPE_ILLUMINATION_TOGGLE': 23
|
||||
}
|
||||
|
||||
class PyKeyboard(PyKeyboardMeta):
|
||||
def press_key(self, key):
|
||||
if key in special_key_translate_table:
|
||||
self._press_special_key(key, True)
|
||||
else:
|
||||
self._press_normal_key(key, True)
|
||||
|
||||
def release_key(self, key):
|
||||
if key in special_key_translate_table:
|
||||
self._press_special_key(key, False)
|
||||
else:
|
||||
self._press_normal_key(key, False)
|
||||
|
||||
def special_key_assignment(self):
|
||||
self.volume_mute_key = 'KEYTYPE_MUTE'
|
||||
self.volume_down_key = 'KEYTYPE_SOUND_DOWN'
|
||||
self.volume_up_key = 'KEYTYPE_SOUND_UP'
|
||||
self.media_play_pause_key = 'KEYTYPE_PLAY'
|
||||
|
||||
# Doesn't work :(
|
||||
# self.media_next_track_key = 'KEYTYPE_NEXT'
|
||||
# self.media_prev_track_key = 'KEYTYPE_PREVIOUS'
|
||||
|
||||
def _press_normal_key(self, key, down):
|
||||
try:
|
||||
if self.is_char_shifted(key):
|
||||
key_code = character_translate_table[key.lower()]
|
||||
|
||||
event = CGEventCreateKeyboardEvent(None,
|
||||
character_translate_table['shift'], down)
|
||||
CGEventPost(kCGHIDEventTap, event)
|
||||
# Tiny sleep to let OS X catch up on us pressing shift
|
||||
time.sleep(.01)
|
||||
|
||||
else:
|
||||
key_code = character_translate_table[key]
|
||||
|
||||
|
||||
event = CGEventCreateKeyboardEvent(None, key_code, down)
|
||||
CGEventPost(kCGHIDEventTap, event)
|
||||
|
||||
|
||||
except KeyError:
|
||||
raise RuntimeError("Key {} not implemented.".format(key))
|
||||
|
||||
def _press_special_key(self, key, down):
|
||||
""" Helper method for special keys.
|
||||
|
||||
Source: http://stackoverflow.com/questions/11045814/emulate-media-key-press-on-mac
|
||||
"""
|
||||
key_code = special_key_translate_table[key]
|
||||
|
||||
ev = NSEvent.otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
|
||||
NSSystemDefined, # type
|
||||
(0,0), # location
|
||||
0xa00 if down else 0xb00, # flags
|
||||
0, # timestamp
|
||||
0, # window
|
||||
0, # ctx
|
||||
8, # subtype
|
||||
(key_code << 16) | ((0xa if down else 0xb) << 8), # data1
|
||||
-1 # data2
|
||||
)
|
||||
|
||||
CGEventPost(0, ev.CGEvent())
|
||||
|
||||
class PyKeyboardEvent(PyKeyboardEventMeta):
|
||||
def run(self):
|
||||
tap = CGEventTapCreate(
|
||||
kCGSessionEventTap,
|
||||
kCGHeadInsertEventTap,
|
||||
kCGEventTapOptionDefault,
|
||||
CGEventMaskBit(kCGEventKeyDown) |
|
||||
CGEventMaskBit(kCGEventKeyUp),
|
||||
self.handler,
|
||||
None)
|
||||
|
||||
loopsource = CFMachPortCreateRunLoopSource(None, tap, 0)
|
||||
loop = CFRunLoopGetCurrent()
|
||||
CFRunLoopAddSource(loop, loopsource, kCFRunLoopDefaultMode)
|
||||
CGEventTapEnable(tap, True)
|
||||
|
||||
while self.state:
|
||||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 5, False)
|
||||
|
||||
def handler(self, proxy, type, event, refcon):
|
||||
key = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode)
|
||||
if type == kCGEventKeyDown:
|
||||
self.key_press(key)
|
||||
elif type == kCGEventKeyUp:
|
||||
self.key_release(key)
|
||||
|
||||
if self.capture:
|
||||
CGEventSetType(event, kCGEventNull)
|
||||
|
||||
return event
|
|
@ -0,0 +1,14 @@
|
|||
#Copyright 2013 Paul Barton
|
||||
#
|
||||
#This program is free software: you can redistribute it and/or modify
|
||||
#it under the terms of the GNU General Public License as published by
|
||||
#the Free Software Foundation, either version 3 of the License, or
|
||||
#(at your option) any later version.
|
||||
#
|
||||
#This program is distributed in the hope that it will be useful,
|
||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
#GNU General Public License for more details.
|
||||
#
|
||||
#You should have received a copy of the GNU General Public License
|
||||
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,14 @@
|
|||
#Copyright 2013 Paul Barton
|
||||
#
|
||||
#This program is free software: you can redistribute it and/or modify
|
||||
#it under the terms of the GNU General Public License as published by
|
||||
#the Free Software Foundation, either version 3 of the License, or
|
||||
#(at your option) any later version.
|
||||
#
|
||||
#This program is distributed in the hope that it will be useful,
|
||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
#GNU General Public License for more details.
|
||||
#
|
||||
#You should have received a copy of the GNU General Public License
|
||||
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,316 @@
|
|||
#Copyright 2013 Paul Barton
|
||||
#
|
||||
#This program is free software: you can redistribute it and/or modify
|
||||
#it under the terms of the GNU General Public License as published by
|
||||
#the Free Software Foundation, either version 3 of the License, or
|
||||
#(at your option) any later version.
|
||||
#
|
||||
#This program is distributed in the hope that it will be useful,
|
||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
#GNU General Public License for more details.
|
||||
#
|
||||
#You should have received a copy of the GNU General Public License
|
||||
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from ctypes import *
|
||||
import win32api
|
||||
from win32con import *
|
||||
import pythoncom, pyHook
|
||||
|
||||
from .base import PyKeyboardMeta, PyKeyboardEventMeta
|
||||
|
||||
import time
|
||||
|
||||
class SupportError(Exception):
|
||||
"""For keys not supported on this system"""
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return('The {0} key is not supported in Windows'.format(self.value))
|
||||
|
||||
class PyKeyboard(PyKeyboardMeta):
|
||||
"""
|
||||
The PyKeyboard implementation for Windows systems. This allows one to
|
||||
simulate keyboard input.
|
||||
"""
|
||||
def __init__(self):
|
||||
PyKeyboardMeta.__init__(self)
|
||||
self.special_key_assignment()
|
||||
|
||||
def press_key(self, character=''):
|
||||
"""
|
||||
Press a given character key.
|
||||
"""
|
||||
try:
|
||||
shifted = self.is_char_shifted(character)
|
||||
except AttributeError:
|
||||
win32api.keybd_event(character, 0, 0, 0)
|
||||
else:
|
||||
if shifted:
|
||||
win32api.keybd_event(self.shift_key, 0, 0, 0)
|
||||
char_vk = win32api.VkKeyScan(character)
|
||||
win32api.keybd_event(char_vk, 0, 0, 0)
|
||||
|
||||
def release_key(self, character=''):
|
||||
"""
|
||||
Release a given character key.
|
||||
"""
|
||||
try:
|
||||
shifted = self.is_char_shifted(character)
|
||||
except AttributeError:
|
||||
win32api.keybd_event(character, 0, KEYEVENTF_KEYUP, 0)
|
||||
else:
|
||||
if shifted:
|
||||
win32api.keybd_event(self.shift_key, 0, KEYEVENTF_KEYUP, 0)
|
||||
char_vk = win32api.VkKeyScan(character)
|
||||
win32api.keybd_event(char_vk, 0, KEYEVENTF_KEYUP, 0)
|
||||
|
||||
def special_key_assignment(self):
|
||||
"""
|
||||
Special Key assignment for windows
|
||||
"""
|
||||
#As defined by Microsoft, refer to:
|
||||
#http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
|
||||
self.backspace_key = VK_BACK
|
||||
self.tab_key = VK_TAB
|
||||
self.clear_key = VK_CLEAR
|
||||
self.return_key = VK_RETURN
|
||||
self.enter_key = self.return_key # Because many keyboards call it "Enter"
|
||||
self.shift_key = VK_SHIFT
|
||||
self.shift_l_key = VK_LSHIFT
|
||||
self.shift_r_key = VK_RSHIFT
|
||||
self.control_key = VK_CONTROL
|
||||
self.control_l_key = VK_LCONTROL
|
||||
self.control_r_key = VK_RCONTROL
|
||||
#Windows uses "menu" to refer to Alt...
|
||||
self.menu_key = VK_MENU
|
||||
self.alt_l_key = VK_LMENU
|
||||
self.alt_r_key = VK_RMENU
|
||||
self.alt_key = self.alt_l_key
|
||||
self.pause_key = VK_PAUSE
|
||||
self.caps_lock_key = VK_CAPITAL
|
||||
self.capital_key = self.caps_lock_key
|
||||
self.num_lock_key = VK_NUMLOCK
|
||||
self.scroll_lock_key = VK_SCROLL
|
||||
#Windows Language Keys,
|
||||
self.kana_key = VK_KANA
|
||||
self.hangeul_key = VK_HANGEUL # old name - should be here for compatibility
|
||||
self.hangul_key = VK_HANGUL
|
||||
self.junjua_key = VK_JUNJA
|
||||
self.final_key = VK_FINAL
|
||||
self.hanja_key = VK_HANJA
|
||||
self.kanji_key = VK_KANJI
|
||||
self.convert_key = VK_CONVERT
|
||||
self.nonconvert_key = VK_NONCONVERT
|
||||
self.accept_key = VK_ACCEPT
|
||||
self.modechange_key = VK_MODECHANGE
|
||||
#More Keys
|
||||
self.escape_key = VK_ESCAPE
|
||||
self.space_key = VK_SPACE
|
||||
self.prior_key = VK_PRIOR
|
||||
self.next_key = VK_NEXT
|
||||
self.page_up_key = self.prior_key
|
||||
self.page_down_key = self.next_key
|
||||
self.home_key = VK_HOME
|
||||
self.up_key = VK_UP
|
||||
self.down_key = VK_DOWN
|
||||
self.left_key = VK_LEFT
|
||||
self.right_key = VK_RIGHT
|
||||
self.end_key = VK_END
|
||||
self.select_key = VK_SELECT
|
||||
self.print_key = VK_PRINT
|
||||
self.snapshot_key = VK_SNAPSHOT
|
||||
self.print_screen_key = self.snapshot_key
|
||||
self.execute_key = VK_EXECUTE
|
||||
self.insert_key = VK_INSERT
|
||||
self.delete_key = VK_DELETE
|
||||
self.help_key = VK_HELP
|
||||
self.windows_l_key = VK_LWIN
|
||||
self.super_l_key = self.windows_l_key
|
||||
self.windows_r_key = VK_RWIN
|
||||
self.super_r_key = self.windows_r_key
|
||||
self.apps_key = VK_APPS
|
||||
#Numpad
|
||||
self.keypad_keys = {'Space': None,
|
||||
'Tab': None,
|
||||
'Enter': None, # Needs Fixing
|
||||
'F1': None,
|
||||
'F2': None,
|
||||
'F3': None,
|
||||
'F4': None,
|
||||
'Home': VK_NUMPAD7,
|
||||
'Left': VK_NUMPAD4,
|
||||
'Up': VK_NUMPAD8,
|
||||
'Right': VK_NUMPAD6,
|
||||
'Down': VK_NUMPAD2,
|
||||
'Prior': None,
|
||||
'Page_Up': VK_NUMPAD9,
|
||||
'Next': None,
|
||||
'Page_Down': VK_NUMPAD3,
|
||||
'End': VK_NUMPAD1,
|
||||
'Begin': None,
|
||||
'Insert': VK_NUMPAD0,
|
||||
'Delete': VK_DECIMAL,
|
||||
'Equal': None, # Needs Fixing
|
||||
'Multiply': VK_MULTIPLY,
|
||||
'Add': VK_ADD,
|
||||
'Separator': VK_SEPARATOR,
|
||||
'Subtract': VK_SUBTRACT,
|
||||
'Decimal': VK_DECIMAL,
|
||||
'Divide': VK_DIVIDE,
|
||||
0: VK_NUMPAD0,
|
||||
1: VK_NUMPAD1,
|
||||
2: VK_NUMPAD2,
|
||||
3: VK_NUMPAD3,
|
||||
4: VK_NUMPAD4,
|
||||
5: VK_NUMPAD5,
|
||||
6: VK_NUMPAD6,
|
||||
7: VK_NUMPAD7,
|
||||
8: VK_NUMPAD8,
|
||||
9: VK_NUMPAD9}
|
||||
self.numpad_keys = self.keypad_keys
|
||||
#FKeys
|
||||
self.function_keys = [None, VK_F1, VK_F2, VK_F3, VK_F4, VK_F5, VK_F6,
|
||||
VK_F7, VK_F8, VK_F9, VK_F10, VK_F11, VK_F12,
|
||||
VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18,
|
||||
VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24,
|
||||
None, None, None, None, None, None, None, None,
|
||||
None, None, None] #Up to 36 as in x11
|
||||
#Miscellaneous
|
||||
self.cancel_key = VK_CANCEL
|
||||
self.break_key = self.cancel_key
|
||||
self.mode_switch_key = VK_MODECHANGE
|
||||
self.browser_back_key = VK_BROWSER_BACK
|
||||
self.browser_forward_key = VK_BROWSER_FORWARD
|
||||
self.processkey_key = VK_PROCESSKEY
|
||||
self.attn_key = VK_ATTN
|
||||
self.crsel_key = VK_CRSEL
|
||||
self.exsel_key = VK_EXSEL
|
||||
self.ereof_key = VK_EREOF
|
||||
self.play_key = VK_PLAY
|
||||
self.zoom_key = VK_ZOOM
|
||||
self.noname_key = VK_NONAME
|
||||
self.pa1_key = VK_PA1
|
||||
self.oem_clear_key = VK_OEM_CLEAR
|
||||
self.volume_mute_key = VK_VOLUME_MUTE
|
||||
self.volume_down_key = VK_VOLUME_DOWN
|
||||
self.volume_up_key = VK_VOLUME_UP
|
||||
self.media_next_track_key = VK_MEDIA_NEXT_TRACK
|
||||
self.media_prev_track_key = VK_MEDIA_PREV_TRACK
|
||||
self.media_play_pause_key = VK_MEDIA_PLAY_PAUSE
|
||||
self.begin_key = self.home_key
|
||||
#LKeys - Unsupported
|
||||
self.l_keys = [None] * 11
|
||||
#RKeys - Unsupported
|
||||
self.r_keys = [None] * 16
|
||||
|
||||
#Other unsupported Keys from X11
|
||||
self.linefeed_key = None
|
||||
self.find_key = None
|
||||
self.meta_l_key = None
|
||||
self.meta_r_key = None
|
||||
self.sys_req_key = None
|
||||
self.hyper_l_key = None
|
||||
self.hyper_r_key = None
|
||||
self.undo_key = None
|
||||
self.redo_key = None
|
||||
self.script_switch_key = None
|
||||
|
||||
class PyKeyboardEvent(PyKeyboardEventMeta):
|
||||
"""
|
||||
The PyKeyboardEvent implementation for Windows Systems. This allows one
|
||||
to listen for keyboard input.
|
||||
"""
|
||||
def __init__(self):
|
||||
PyKeyboardEventMeta.__init__(self)
|
||||
self.hm = pyHook.HookManager()
|
||||
self.shift_state = 0 # 0 is off, 1 is on
|
||||
self.alt_state = 0 # 0 is off, 2 is on
|
||||
|
||||
def run(self):
|
||||
"""Begin listening for keyboard input events."""
|
||||
self.state = True
|
||||
self.hm.KeyAll = self.handler
|
||||
self.hm.HookKeyboard()
|
||||
while self.state:
|
||||
time.sleep(0.01)
|
||||
pythoncom.PumpWaitingMessages()
|
||||
|
||||
def stop(self):
|
||||
"""Stop listening for keyboard input events."""
|
||||
self.hm.UnhookKeyboard()
|
||||
self.state = False
|
||||
|
||||
def handler(self, reply):
|
||||
"""Upper level handler of keyboard events."""
|
||||
if reply.Message == pyHook.HookConstants.WM_KEYDOWN:
|
||||
self._key_press(reply)
|
||||
elif reply.Message == pyHook.HookConstants.WM_KEYUP:
|
||||
self._key_release(reply)
|
||||
elif reply.Message == pyHook.HookConstants.WM_SYSKEYDOWN:
|
||||
self._key_press(reply)
|
||||
elif reply.Message == pyHook.HookConstants.WM.SYSKEYUP:
|
||||
self._key_release(reply)
|
||||
else:
|
||||
print('Keyboard event message unhandled: {0}'.format(reply.Message))
|
||||
return not self.capture
|
||||
|
||||
def _key_press(self, event):
|
||||
if self.escape_code(event): #Quit if this returns True
|
||||
self.stop()
|
||||
if event.GetKey() in ['Shift', 'Lshift', 'Rshift', 'Capital']:
|
||||
self.toggle_shift_state()
|
||||
if event.GetKey() in ['Menu', 'Lmenu', 'Rmenu']:
|
||||
self.toggle_alt_state()
|
||||
#print('Key Pressed!')
|
||||
#print('GetKey: {0}'.format(event.GetKey())) # Name of the virtual keycode, str
|
||||
#print('IsAlt: {0}'.format(event.IsAlt())) # Was the alt key depressed?, bool
|
||||
#print('IsExtended: {0}'.format(event.IsExtended())) # Is this an extended key?, bool
|
||||
#print('IsInjected: {0}'.format(event.IsInjected())) # Was this event generated programmatically?, bool
|
||||
#print('IsTransition: {0}'.format(event.IsTransition())) #Is this a transition from up to down or vice versa?, bool
|
||||
#print('ASCII: {0}'.format(event.Ascii)) # ASCII value, if one exists, str
|
||||
#print('KeyID: {0}'.format(event.KeyID)) # Virtual key code, int
|
||||
#print('ScanCode: {0}'.format(event.ScanCode)) # Scan code, int
|
||||
|
||||
def _key_release(self, event):
|
||||
if event.GetKey() in ['Shift', 'Lshift', 'Rshift', 'Capital']:
|
||||
self.toggle_shift_state()
|
||||
if event.GetKey() in ['Menu', 'Lmenu', 'Rmenu']:
|
||||
self.toggle_alt_state()
|
||||
self.key_release()
|
||||
#print('Key Released!')
|
||||
#print('GetKey: {0}'.format(event.GetKey())) # Name of the virtual keycode, str
|
||||
#print('IsAlt: {0}'.format(event.IsAlt())) # Was the alt key depressed?, bool
|
||||
#print('IsExtended: {0}'.format(event.IsExtended())) # Is this an extended key?, bool
|
||||
#print('IsInjected: {0}'.format(event.IsInjected())) # Was this event generated programmatically?, bool
|
||||
#print('IsTransition: {0}'.format(event.IsTransition())) #Is this a transition from up to down or vice versa?, bool
|
||||
#print('ASCII: {0}'.format(event.Ascii)) # ASCII value, if one exists, str
|
||||
#print('KeyID: {0}'.format(event.KeyID)) # Virtual key code, int
|
||||
#print('ScanCode: {0}'.format(event.ScanCode)) # Scan code, int
|
||||
|
||||
def escape_code(self, event):
|
||||
if event.KeyID == VK_ESCAPE:
|
||||
return True
|
||||
return False
|
||||
|
||||
def toggle_shift_state(self):
|
||||
'''Does toggling for the shift state.'''
|
||||
if self.shift_state == 0:
|
||||
self.shift_state = 1
|
||||
elif self.shift_state == 1:
|
||||
self.shift_state = 0
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
def toggle_alt_state(self):
|
||||
'''Does toggling for the alt state.'''
|
||||
if self.alt_state == 0:
|
||||
self.alt_state = 2
|
||||
elif self.alt_state == 2:
|
||||
self.alt_state = 0
|
||||
else:
|
||||
return False
|
||||
return True
|
|
@ -0,0 +1,363 @@
|
|||
#Copyright 2013 Paul Barton
|
||||
#
|
||||
#This program is free software: you can redistribute it and/or modify
|
||||
#it under the terms of the GNU General Public License as published by
|
||||
#the Free Software Foundation, either version 3 of the License, or
|
||||
#(at your option) any later version.
|
||||
#
|
||||
#This program is distributed in the hope that it will be useful,
|
||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
#GNU General Public License for more details.
|
||||
#
|
||||
#You should have received a copy of the GNU General Public License
|
||||
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from Xlib.display import Display
|
||||
from Xlib import X
|
||||
from Xlib.ext.xtest import fake_input
|
||||
from Xlib.XK import string_to_keysym, keysym_to_string
|
||||
from Xlib.ext import record
|
||||
from Xlib.protocol import rq
|
||||
|
||||
from .base import PyKeyboardMeta, PyKeyboardEventMeta
|
||||
|
||||
import time
|
||||
|
||||
special_X_keysyms = {
|
||||
' ': "space",
|
||||
'\t': "Tab",
|
||||
'\n': "Return", # for some reason this needs to be cr, not lf
|
||||
'\r': "Return",
|
||||
'\e': "Escape",
|
||||
'!': "exclam",
|
||||
'#': "numbersign",
|
||||
'%': "percent",
|
||||
'$': "dollar",
|
||||
'&': "ampersand",
|
||||
'"': "quotedbl",
|
||||
'\'': "apostrophe",
|
||||
'(': "parenleft",
|
||||
')': "parenright",
|
||||
'*': "asterisk",
|
||||
'=': "equal",
|
||||
'+': "plus",
|
||||
',': "comma",
|
||||
'-': "minus",
|
||||
'.': "period",
|
||||
'/': "slash",
|
||||
':': "colon",
|
||||
';': "semicolon",
|
||||
'<': "less",
|
||||
'>': "greater",
|
||||
'?': "question",
|
||||
'@': "at",
|
||||
'[': "bracketleft",
|
||||
']': "bracketright",
|
||||
'\\': "backslash",
|
||||
'^': "asciicircum",
|
||||
'_': "underscore",
|
||||
'`': "grave",
|
||||
'{': "braceleft",
|
||||
'|': "bar",
|
||||
'}': "braceright",
|
||||
'~': "asciitilde"
|
||||
}
|
||||
|
||||
class PyKeyboard(PyKeyboardMeta):
|
||||
"""
|
||||
The PyKeyboard implementation for X11 systems (mostly linux). This
|
||||
allows one to simulate keyboard input.
|
||||
"""
|
||||
def __init__(self, display=None):
|
||||
PyKeyboardMeta.__init__(self)
|
||||
self.display = Display(display)
|
||||
self.display2 = Display(display)
|
||||
self.special_key_assignment()
|
||||
|
||||
def press_key(self, character=''):
|
||||
"""
|
||||
Press a given character key. Also works with character keycodes as
|
||||
integers, but not keysyms.
|
||||
"""
|
||||
try: # Detect uppercase or shifted character
|
||||
shifted = self.is_char_shifted(character)
|
||||
except AttributeError: # Handle the case of integer keycode argument
|
||||
fake_input(self.display, X.KeyPress, character)
|
||||
self.display.sync()
|
||||
else:
|
||||
if shifted:
|
||||
fake_input(self.display, X.KeyPress, self.shift_key)
|
||||
char_val = self.lookup_character_value(character)
|
||||
fake_input(self.display, X.KeyPress, char_val)
|
||||
self.display.sync()
|
||||
|
||||
def release_key(self, character=''):
|
||||
"""
|
||||
Release a given character key. Also works with character keycodes as
|
||||
integers, but not keysyms.
|
||||
"""
|
||||
try: # Detect uppercase or shifted character
|
||||
shifted = self.is_char_shifted(character)
|
||||
except AttributeError: # Handle the case of integer keycode argument
|
||||
fake_input(self.display, X.KeyRelease, character)
|
||||
self.display.sync()
|
||||
else:
|
||||
if shifted:
|
||||
fake_input(self.display, X.KeyRelease, self.shift_key)
|
||||
char_val = self.lookup_character_value(character)
|
||||
fake_input(self.display, X.KeyRelease, char_val)
|
||||
self.display.sync()
|
||||
|
||||
def special_key_assignment(self):
|
||||
"""
|
||||
Determines the keycodes for common special keys on the keyboard. These
|
||||
are integer values and can be passed to the other key methods.
|
||||
Generally speaking, these are non-printable codes.
|
||||
"""
|
||||
#This set of keys compiled using the X11 keysymdef.h file as reference
|
||||
#They comprise a relatively universal set of keys, though there may be
|
||||
#exceptions which may come up for other OSes and vendors. Countless
|
||||
#special cases exist which are not handled here, but may be extended.
|
||||
#TTY Function Keys
|
||||
self.backspace_key = self.lookup_character_value('BackSpace')
|
||||
self.tab_key = self.lookup_character_value('Tab')
|
||||
self.linefeed_key = self.lookup_character_value('Linefeed')
|
||||
self.clear_key = self.lookup_character_value('Clear')
|
||||
self.return_key = self.lookup_character_value('Return')
|
||||
self.enter_key = self.return_key # Because many keyboards call it "Enter"
|
||||
self.pause_key = self.lookup_character_value('Pause')
|
||||
self.scroll_lock_key = self.lookup_character_value('Scroll_Lock')
|
||||
self.sys_req_key = self.lookup_character_value('Sys_Req')
|
||||
self.escape_key = self.lookup_character_value('Escape')
|
||||
self.delete_key = self.lookup_character_value('Delete')
|
||||
#Modifier Keys
|
||||
self.shift_l_key = self.lookup_character_value('Shift_L')
|
||||
self.shift_r_key = self.lookup_character_value('Shift_R')
|
||||
self.shift_key = self.shift_l_key # Default Shift is left Shift
|
||||
self.alt_l_key = self.lookup_character_value('Alt_L')
|
||||
self.alt_r_key = self.lookup_character_value('Alt_R')
|
||||
self.alt_key = self.alt_l_key # Default Alt is left Alt
|
||||
self.control_l_key = self.lookup_character_value('Control_L')
|
||||
self.control_r_key = self.lookup_character_value('Control_R')
|
||||
self.control_key = self.control_l_key # Default Ctrl is left Ctrl
|
||||
self.caps_lock_key = self.lookup_character_value('Caps_Lock')
|
||||
self.capital_key = self.caps_lock_key # Some may know it as Capital
|
||||
self.shift_lock_key = self.lookup_character_value('Shift_Lock')
|
||||
self.meta_l_key = self.lookup_character_value('Meta_L')
|
||||
self.meta_r_key = self.lookup_character_value('Meta_R')
|
||||
self.super_l_key = self.lookup_character_value('Super_L')
|
||||
self.windows_l_key = self.super_l_key # Cross-support; also it's printed there
|
||||
self.super_r_key = self.lookup_character_value('Super_R')
|
||||
self.windows_r_key = self.super_r_key # Cross-support; also it's printed there
|
||||
self.hyper_l_key = self.lookup_character_value('Hyper_L')
|
||||
self.hyper_r_key = self.lookup_character_value('Hyper_R')
|
||||
#Cursor Control and Motion
|
||||
self.home_key = self.lookup_character_value('Home')
|
||||
self.up_key = self.lookup_character_value('Up')
|
||||
self.down_key = self.lookup_character_value('Down')
|
||||
self.left_key = self.lookup_character_value('Left')
|
||||
self.right_key = self.lookup_character_value('Right')
|
||||
self.end_key = self.lookup_character_value('End')
|
||||
self.begin_key = self.lookup_character_value('Begin')
|
||||
self.page_up_key = self.lookup_character_value('Page_Up')
|
||||
self.page_down_key = self.lookup_character_value('Page_Down')
|
||||
self.prior_key = self.lookup_character_value('Prior')
|
||||
self.next_key = self.lookup_character_value('Next')
|
||||
#Misc Functions
|
||||
self.select_key = self.lookup_character_value('Select')
|
||||
self.print_key = self.lookup_character_value('Print')
|
||||
self.print_screen_key = self.print_key # Seems to be the same thing
|
||||
self.snapshot_key = self.print_key # Another name for printscreen
|
||||
self.execute_key = self.lookup_character_value('Execute')
|
||||
self.insert_key = self.lookup_character_value('Insert')
|
||||
self.undo_key = self.lookup_character_value('Undo')
|
||||
self.redo_key = self.lookup_character_value('Redo')
|
||||
self.menu_key = self.lookup_character_value('Menu')
|
||||
self.apps_key = self.menu_key # Windows...
|
||||
self.find_key = self.lookup_character_value('Find')
|
||||
self.cancel_key = self.lookup_character_value('Cancel')
|
||||
self.help_key = self.lookup_character_value('Help')
|
||||
self.break_key = self.lookup_character_value('Break')
|
||||
self.mode_switch_key = self.lookup_character_value('Mode_switch')
|
||||
self.script_switch_key = self.lookup_character_value('script_switch')
|
||||
self.num_lock_key = self.lookup_character_value('Num_Lock')
|
||||
#Keypad Keys: Dictionary structure
|
||||
keypad = ['Space', 'Tab', 'Enter', 'F1', 'F2', 'F3', 'F4', 'Home',
|
||||
'Left', 'Up', 'Right', 'Down', 'Prior', 'Page_Up', 'Next',
|
||||
'Page_Down', 'End', 'Begin', 'Insert', 'Delete', 'Equal',
|
||||
'Multiply', 'Add', 'Separator', 'Subtract', 'Decimal',
|
||||
'Divide', 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
self.keypad_keys = {k: self.lookup_character_value('KP_'+str(k)) for k in keypad}
|
||||
self.numpad_keys = self.keypad_keys
|
||||
#Function Keys/ Auxilliary Keys
|
||||
#FKeys
|
||||
self.function_keys = [None] + [self.lookup_character_value('F'+str(i)) for i in xrange(1,36)]
|
||||
#LKeys
|
||||
self.l_keys = [None] + [self.lookup_character_value('L'+str(i)) for i in xrange(1,11)]
|
||||
#RKeys
|
||||
self.r_keys = [None] + [self.lookup_character_value('R'+str(i)) for i in xrange(1,16)]
|
||||
|
||||
#Unsupported keys from windows
|
||||
self.kana_key = None
|
||||
self.hangeul_key = None # old name - should be here for compatibility
|
||||
self.hangul_key = None
|
||||
self.junjua_key = None
|
||||
self.final_key = None
|
||||
self.hanja_key = None
|
||||
self.kanji_key = None
|
||||
self.convert_key = None
|
||||
self.nonconvert_key = None
|
||||
self.accept_key = None
|
||||
self.modechange_key = None
|
||||
self.sleep_key = None
|
||||
|
||||
def lookup_character_value(self, character):
|
||||
"""
|
||||
Looks up the keysym for the character then returns the keycode mapping
|
||||
for that keysym.
|
||||
"""
|
||||
ch_keysym = string_to_keysym(character)
|
||||
if ch_keysym == 0:
|
||||
ch_keysym = string_to_keysym(special_X_keysyms[character])
|
||||
return self.display.keysym_to_keycode(ch_keysym)
|
||||
|
||||
class PyKeyboardEvent(PyKeyboardEventMeta):
|
||||
"""
|
||||
The PyKeyboardEvent implementation for X11 systems (mostly linux). This
|
||||
allows one to listen for keyboard input.
|
||||
"""
|
||||
def __init__(self, display=None):
|
||||
PyKeyboardEventMeta.__init__(self)
|
||||
self.display = Display(display)
|
||||
self.display2 = Display(display)
|
||||
self.ctx = self.display2.record_create_context(
|
||||
0,
|
||||
[record.AllClients],
|
||||
[{
|
||||
'core_requests': (0, 0),
|
||||
'core_replies': (0, 0),
|
||||
'ext_requests': (0, 0, 0, 0),
|
||||
'ext_replies': (0, 0, 0, 0),
|
||||
'delivered_events': (0, 0),
|
||||
'device_events': (X.KeyPress, X.KeyRelease),
|
||||
'errors': (0, 0),
|
||||
'client_started': False,
|
||||
'client_died': False,
|
||||
}])
|
||||
self.shift_state = 0 # 0 is off, 1 is on
|
||||
self.alt_state = 0 # 0 is off, 2 is on
|
||||
self.mod_keycodes = self.get_mod_keycodes()
|
||||
|
||||
def run(self):
|
||||
"""Begin listening for keyboard input events."""
|
||||
self.state = True
|
||||
if self.capture:
|
||||
self.display2.screen().root.grab_keyboard(True, X.KeyPressMask | X.KeyReleaseMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime)
|
||||
|
||||
self.display2.record_enable_context(self.ctx, self.handler)
|
||||
self.display2.record_free_context(self.ctx)
|
||||
|
||||
def stop(self):
|
||||
"""Stop listening for keyboard input events."""
|
||||
self.state = False
|
||||
self.display.record_disable_context(self.ctx)
|
||||
self.display.ungrab_keyboard(X.CurrentTime)
|
||||
self.display.flush()
|
||||
self.display2.record_disable_context(self.ctx)
|
||||
self.display2.ungrab_keyboard(X.CurrentTime)
|
||||
self.display2.flush()
|
||||
|
||||
def handler(self, reply):
|
||||
"""Upper level handler of keyboard events."""
|
||||
data = reply.data
|
||||
while len(data):
|
||||
event, data = rq.EventField(None).parse_binary_value(data, self.display.display, None, None)
|
||||
if event.type == X.KeyPress:
|
||||
if self.escape_code(event): # Quit if this returns True
|
||||
self.stop()
|
||||
else:
|
||||
self._key_press(event.detail)
|
||||
elif event.type == X.KeyRelease:
|
||||
self._key_release(event.detail)
|
||||
else:
|
||||
print('WTF: {0}'.format(event.type))
|
||||
|
||||
def _key_press(self, keycode):
|
||||
"""A key has been pressed, do stuff."""
|
||||
#Alter modification states
|
||||
if keycode in self.mod_keycodes['Shift'] or keycode in self.mod_keycodes['Lock']:
|
||||
self.toggle_shift_state()
|
||||
elif keycode in self.mod_keycodes['Alt']:
|
||||
self.toggle_alt_state()
|
||||
else:
|
||||
self.key_press(keycode)
|
||||
|
||||
def _key_release(self, keycode):
|
||||
"""A key has been released, do stuff."""
|
||||
#Alter modification states
|
||||
if keycode in self.mod_keycodes['Shift']:
|
||||
self.toggle_shift_state()
|
||||
elif keycode in self.mod_keycodes['Alt']:
|
||||
self.toggle_alt_state()
|
||||
else:
|
||||
self.key_release(keycode)
|
||||
|
||||
def escape_code(self, event):
|
||||
if event.detail == self.lookup_character_value('Escape'):
|
||||
return True
|
||||
return False
|
||||
|
||||
def lookup_char_from_keycode(self, keycode):
|
||||
keysym =self.display.keycode_to_keysym(keycode, self.shift_state + self.alt_state)
|
||||
if keysym:
|
||||
char = self.display.lookup_string(keysym)
|
||||
return char
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_mod_keycodes(self):
|
||||
"""
|
||||
Detects keycodes for modifiers and parses them into a dictionary
|
||||
for easy access.
|
||||
"""
|
||||
modifier_mapping = self.display.get_modifier_mapping()
|
||||
modifier_dict = {}
|
||||
nti = [('Shift', X.ShiftMapIndex),
|
||||
('Control', X.ControlMapIndex), ('Mod1', X.Mod1MapIndex),
|
||||
('Alt', X.Mod1MapIndex), ('Mod2', X.Mod2MapIndex),
|
||||
('Mod3', X.Mod3MapIndex), ('Mod4', X.Mod4MapIndex),
|
||||
('Mod5', X.Mod5MapIndex), ('Lock', X.LockMapIndex)]
|
||||
for n, i in nti:
|
||||
modifier_dict[n] = list(modifier_mapping[i])
|
||||
return modifier_dict
|
||||
|
||||
def lookup_character_value(self, character):
|
||||
"""
|
||||
Looks up the keysym for the character then returns the keycode mapping
|
||||
for that keysym.
|
||||
"""
|
||||
ch_keysym = string_to_keysym(character)
|
||||
if ch_keysym == 0:
|
||||
ch_keysym = string_to_keysym(special_X_keysyms[character])
|
||||
return self.display.keysym_to_keycode(ch_keysym)
|
||||
|
||||
def toggle_shift_state(self):
|
||||
'''Does toggling for the shift state.'''
|
||||
if self.shift_state == 0:
|
||||
self.shift_state = 1
|
||||
elif self.shift_state == 1:
|
||||
self.shift_state = 0
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
def toggle_alt_state(self):
|
||||
'''Does toggling for the alt state.'''
|
||||
if self.alt_state == 0:
|
||||
self.alt_state = 2
|
||||
elif self.alt_state == 2:
|
||||
self.alt_state = 0
|
||||
else:
|
||||
return False
|
||||
return True
|
1
start.py
1
start.py
|
@ -35,6 +35,7 @@ actors.LightTrigger(eventbus, statemachine,
|
|||
actors.setup_chromecast(eventbus, config.get("chromecast", "host"))
|
||||
actors.setup_file_downloader(eventbus, config.get("downloader", "download_dir"))
|
||||
actors.setup_webbrowser(eventbus)
|
||||
actors.setup_media_buttons(eventbus)
|
||||
|
||||
# Init HTTP interface
|
||||
HTTPInterface(eventbus, statemachine, config.get("common","api_password"))
|
||||
|
|
Loading…
Reference in New Issue