mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 16:27:08 +00:00
Added support for multimedia keyboard button simulation for controlling the host.
This commit is contained in:
parent
2b3d81d007
commit
866a3e852e
@ -17,7 +17,7 @@ import dateutil.parser
|
|||||||
from phue import Bridge
|
from phue import Bridge
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from .packages.pychromecast import pychromecast
|
from .packages import pychromecast, pykeyboard
|
||||||
|
|
||||||
from . import track_state_change
|
from . import track_state_change
|
||||||
from .util import sanitize_filename
|
from .util import sanitize_filename
|
||||||
@ -35,7 +35,10 @@ EVENT_BROWSE_URL = "browse_url"
|
|||||||
EVENT_CHROMECAST_YOUTUBE_VIDEO = "chromecast.play_youtube_video"
|
EVENT_CHROMECAST_YOUTUBE_VIDEO = "chromecast.play_youtube_video"
|
||||||
EVENT_TURN_LIGHT_ON = "turn_light_on"
|
EVENT_TURN_LIGHT_ON = "turn_light_on"
|
||||||
EVENT_TURN_LIGHT_OFF = "turn_light_off"
|
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):
|
def _hue_process_transition_time(transition_seconds):
|
||||||
""" Transition time is in 1/10th seconds
|
""" Transition time is in 1/10th seconds
|
||||||
@ -313,3 +316,20 @@ def setup_chromecast(eventbus, host):
|
|||||||
|
|
||||||
eventbus.listen(EVENT_CHROMECAST_YOUTUBE_VIDEO,
|
eventbus.listen(EVENT_CHROMECAST_YOUTUBE_VIDEO,
|
||||||
lambda event: pychromecast.play_youtube_video(host, event.data['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
|
Not all external Git repositories that we depend on are
|
||||||
available as a package for pip. That is why we include
|
available as a package for pip. That is why we include
|
||||||
them here.
|
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
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
37
homeassistant/packages/pykeyboard/__init__.py
Normal file
37
homeassistant/packages/pykeyboard/__init__.py
Normal file
@ -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
|
102
homeassistant/packages/pykeyboard/base.py
Normal file
102
homeassistant/packages/pykeyboard/base.py
Normal file
@ -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
|
14
homeassistant/packages/pykeyboard/java_.py
Normal file
14
homeassistant/packages/pykeyboard/java_.py
Normal file
@ -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/>.
|
201
homeassistant/packages/pykeyboard/mac.py
Normal file
201
homeassistant/packages/pykeyboard/mac.py
Normal file
@ -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
|
14
homeassistant/packages/pykeyboard/mir.py
Normal file
14
homeassistant/packages/pykeyboard/mir.py
Normal file
@ -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/>.
|
14
homeassistant/packages/pykeyboard/wayland.py
Normal file
14
homeassistant/packages/pykeyboard/wayland.py
Normal file
@ -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/>.
|
316
homeassistant/packages/pykeyboard/windows.py
Normal file
316
homeassistant/packages/pykeyboard/windows.py
Normal file
@ -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
|
363
homeassistant/packages/pykeyboard/x11.py
Normal file
363
homeassistant/packages/pykeyboard/x11.py
Normal file
@ -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_chromecast(eventbus, config.get("chromecast", "host"))
|
||||||
actors.setup_file_downloader(eventbus, config.get("downloader", "download_dir"))
|
actors.setup_file_downloader(eventbus, config.get("downloader", "download_dir"))
|
||||||
actors.setup_webbrowser(eventbus)
|
actors.setup_webbrowser(eventbus)
|
||||||
|
actors.setup_media_buttons(eventbus)
|
||||||
|
|
||||||
# Init HTTP interface
|
# Init HTTP interface
|
||||||
HTTPInterface(eventbus, statemachine, config.get("common","api_password"))
|
HTTPInterface(eventbus, statemachine, config.get("common","api_password"))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user