Added emoji support, refactored, internalized pyautogui

This commit is contained in:
itdominator 2023-03-03 21:19:39 -06:00
parent 9eea74f841
commit a3496263b9
23 changed files with 26934 additions and 1660 deletions

View File

@ -1,6 +1,10 @@
# Mouse_Keyboard # Mouse-Keyboard
An onscreen keyboard for the mouse. An onscreen keyboard for the mouse.
### Requirements
* PyGObject
* python-xlib
# TODO # TODO
<li>Get save and execute of custom commands working.</li> <li>Get save and execute of custom commands working.</li>

View File

@ -1,5 +1,7 @@
# Python imports # Python imports
import builtins, threading import os
import builtins
import threading
# Lib imports # Lib imports
@ -24,6 +26,9 @@ def daemon_threaded_wrapper(fn):
class MissingConfigError(Exception):
pass
class Pyautogui_Controller(ControlMixin): class Pyautogui_Controller(ControlMixin):
def __init__(self): def __init__(self):
@ -38,39 +43,69 @@ keys_json = {
"keys": { "keys": {
"row1": { "row1": {
"pKeys": ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'], "pKeys": ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],
"sKeys": ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'], "sKeys": ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
"eKeys": ['🤩', '\U0001F600', '', '', '', '', '', '', '', '']
}, },
"row2": { "row2": {
"pKeys": ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'], "pKeys": ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'],
"sKeys": ['\\', '^', '#', '$', '%', '&', '-', '_', '"', '*'], "sKeys": ['\\', '^', '#', '$', '%', '&', '-', '_', '"', '*']
"eKeys": ['', '', '', '', '', '', '', '', '', '']
}, },
"row3": { "row3": {
"pKeys": ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', "'"], "pKeys": ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', "'"],
"sKeys": ['/', '|', ':', '=', '+', '', '', '', ';', '!'], "sKeys": ['/', '|', ':', '=', '+', '', '', '', ';', '!']
"eKeys": ['', '', '', '', '', '', '', '', '', '']
}, },
"row4": { "row4": {
"pKeys": ['z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '?'], "pKeys": ['z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '?'],
"sKeys": ['', '', '<', '>', '[', ']', '(', ')', '{', '}'], "sKeys": ['', '', '<', '>', '[', ']', '(', ')', '{', '}']
"eKeys": ['', '', '', '', '', '', '', '', '', '']
}, },
} }
} }
# NOTE: Just reminding myself we can add to builtins two different ways... # NOTE: Just reminding myself we can add to builtins two different ways...
# __builtins__.update({"event_system": Builtins()}) # __builtins__.update({"event_system": Builtins()})
builtins.app_name = "Mouse Keyboard" builtins.app_name = "Mouse-Keyboard"
builtins.threaded = threaded_wrapper builtins.threaded = threaded_wrapper
builtins.daemon_threaded = daemon_threaded_wrapper builtins.daemon_threaded = daemon_threaded_wrapper
builtins.keys_set = keys_json builtins.keys_set = keys_json
builtins.trace_debug = False builtins.trace_debug = False
builtins.debug = False builtins.debug = False
builtins.app_settings = None builtins.app_settings = None
builtins.get_clipboard = ['xclip','-selection', 'clipboard', '-o']
builtins.set_clipboard = ['xclip','-selection','clipboard']
builtins.endpoint_registry = EndpointRegistry() builtins.endpoint_registry = EndpointRegistry()
builtins.event_system = EventSystem() builtins.event_system = EventSystem()
builtins.typwriter = Pyautogui_Controller() builtins.typwriter = Pyautogui_Controller()
_USER_HOME = os.path.expanduser('~')
_USR_PATH = f"/usr/share/{app_name.lower()}"
_CONFIG_PATH = f"{_USER_HOME}/.config/{app_name.lower()}"
_ICON_FILE = f"{_CONFIG_PATH}/icons/{app_name.lower()}.png"
_CSS_FILE = f"{_CONFIG_PATH}/stylesheet.css"
_EMOJI_FILE = f"{_CONFIG_PATH}/emoji.json"
if not os.path.exists(_ICON_FILE):
_ICON_FILE = f"{_USR_PATH}/icons/{app_name.lower()}.png"
if not os.path.exists(_ICON_FILE):
print(_ICON_FILE)
raise MissingConfigError("Unable to find the application icon.")
if not os.path.exists(_CSS_FILE):
_CSS_FILE = f"{_USR_PATH}/stylesheet.css"
if not os.path.exists(_CSS_FILE):
raise MissingConfigError("Unable to find the stylesheet.")
if not os.path.exists(_EMOJI_FILE):
_EMOJI_FILE = f"{_USR_PATH}/emoji.json"
if not os.path.exists(_EMOJI_FILE):
raise MissingConfigError("Unable to find the stylesheet.")
builtins.CONFIG_PATH = _CONFIG_PATH
builtins.ICON_FILE = _ICON_FILE
builtins.CSS_FILE = _CSS_FILE
builtins.EMOJI_FILE = _EMOJI_FILE

View File

@ -6,7 +6,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
# Application imports # Application imports
from ..widgets.defined_keys import Tab_Key from ..widgets.defined_keys import Symbols_Key
from ..widgets.defined_keys import Del_Key from ..widgets.defined_keys import Del_Key
from ..widgets.defined_keys import Ctrl_Key from ..widgets.defined_keys import Ctrl_Key
from ..widgets.defined_keys import Shift_Key from ..widgets.defined_keys import Shift_Key
@ -26,7 +26,7 @@ class Button_Box(Gtk.ButtonBox):
def __init__(self): def __init__(self):
super(Button_Box, self).__init__() super(Button_Box, self).__init__()
for key in [Tab_Key(), Del_Key(), Ctrl_Key(), Shift_Key(), Alt_Key(), PrtSc_Key()]: for key in [Symbols_Key(), Del_Key(), Ctrl_Key(), Shift_Key(), Alt_Key(), PrtSc_Key()]:
self.add(key) self.add(key)

View File

@ -38,17 +38,15 @@ class Keys_Column(Gtk.Box):
for child in children: for child in children:
pKeys = keys[child]["pKeys"] pKeys = keys[child]["pKeys"]
sKeys = keys[child]["sKeys"] sKeys = keys[child]["sKeys"]
eKeys = keys[child]["eKeys"]
row_box = self.add_row() row_box = self.add_row()
if len(pKeys) == len(sKeys) and len(pKeys) == len(eKeys): if len(pKeys) == len(sKeys):
for i in range(10): for i in range(10):
pkey = pKeys[i] pkey = pKeys[i]
sKey = sKeys[i] sKey = sKeys[i]
eKey = eKeys[i] row_box.add(Key(pkey, sKey))
row_box.add(Key(pkey, sKey, eKey))
else: else:
raise KeyboardRowMatchError("A row in keys_json has missmatched pKeys, sKeys, or eKeys lengths.") raise KeyboardRowMatchError("A row in keys_json has missmatched pKeys to sKeys lengths.")
self.add(Bottom_Key_Row()) self.add(Bottom_Key_Row())

View File

@ -7,7 +7,7 @@ from gi.repository import Gtk
# Application imports # Application imports
from ..widgets.defined_keys import Esc_Key from ..widgets.defined_keys import Esc_Key
from ..widgets.defined_keys import Symbols_Key from ..widgets.defined_keys import Tab_Key
from ..widgets.defined_keys import CAPS_Key from ..widgets.defined_keys import CAPS_Key
@ -21,7 +21,7 @@ class Left_Column(Gtk.Box):
self.setup_styling() self.setup_styling()
for key in [Symbols_Key(), Esc_Key(), CAPS_Key()]: for key in [Tab_Key(), Esc_Key(), CAPS_Key()]:
self.add(key) self.add(key)
self.show_all() self.show_all()

View File

@ -6,12 +6,14 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
# Application imports # Application imports
from ..widgets.defined_keys import Emoji_Keys from ..widgets.emoji_popover import Emoji_Popover
from ..widgets.defined_keys import Emoji_Key
from ..widgets.defined_keys import Backspace_Key from ..widgets.defined_keys import Backspace_Key
from ..widgets.defined_keys import Enter_Key from ..widgets.defined_keys import Enter_Key
class Right_Column(Gtk.Box): class Right_Column(Gtk.Box):
"""docstring for Right_Column.""" """docstring for Right_Column."""
@ -20,7 +22,14 @@ class Right_Column(Gtk.Box):
self.setup_styling() self.setup_styling()
for key in [Emoji_Keys(), Backspace_Key(), Enter_Key()]: emoji_popover = Emoji_Popover()
emoji_key = Emoji_Key(emoji_popover)
emoji_popover.set_parent_key(emoji_key)
emoji_popover.set_relative_to(emoji_key)
emoji_popover.set_constrain_to(0) # LEFT = 0, RIGHT = 1, TOP = 2, BOTTOM = 3
for key in [emoji_key, Backspace_Key(), Enter_Key()]:
self.add(key) self.add(key)
self.show_all() self.show_all()

View File

@ -63,21 +63,25 @@ class Backspace_Key(Key):
def _clicked(self, widget = None): def _clicked(self, widget = None):
typwriter.press_special_keys(self.get_label()) typwriter.press_special_keys(self.get_label())
class Emoji_Keys(Key): class Emoji_Key(Key):
def __init__(self): def __init__(self, emoji_popover):
super(Emoji_Keys, self).__init__("Emoji", "Emoji", iscontrol=True) super(Emoji_Key, self).__init__("Emoji", "Emoji", iscontrol=True)
self._ctx = self.get_style_context()
self._emoji_popover = emoji_popover
def setup_signals(self): def setup_signals(self):
self.connect("released", self._clicked) self.connect("released", self._clicked)
def _clicked(self, widget = None): def _clicked(self, widget = None):
ctx = widget.get_style_context() self._ctx.add_class("toggled_bttn")
ctx.remove_class("toggled_bttn") if ctx.has_class("toggled_bttn") else ctx.add_class("toggled_bttn") self._emoji_popover.popup()
def unset_selected(self, widget = None):
self._ctx.remove_class("toggled_bttn")
key_columns = self.get_parent().get_parent().get_children()[1]
for row in key_columns.get_children():
for key in row:
key.emit("toggle-emoji-keys", ())
class Enter_Key(Key): class Enter_Key(Key):
def __init__(self): def __init__(self):

View File

@ -0,0 +1,101 @@
# Python imports
from collections import defaultdict
import json
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from .key import Key
class Emoji_Notebook(Gtk.Notebook):
"""docstring for Emoji_Notebook."""
def __init__(self):
super(Emoji_Notebook, self).__init__()
self.load_ui( self.get_data(EMOJI_FILE) )
self.setup_styling()
self.show_all()
def setup_styling(self):
self.set_current_page(0)
self.set_scrollable(True)
def get_data(self, file):
emoji_grouping = defaultdict(list)
with open(file, 'r') as f:
emoji_data = json.load(f)
for emoji in emoji_data:
category = emoji['category']
del emoji['category']
del emoji['unicode_version']
del emoji['ios_version']
emoji_grouping[category].append(emoji)
return emoji_grouping
def load_ui(self, emoji_grouping):
width = 1
height = 1
for group in emoji_grouping:
tab_widget = Gtk.Label(label=group)
scroll, grid = self.create_scroll_and_grid()
top = 0
left = 0
for emoji in emoji_grouping[group]:
key = Key(emoji["emoji"], emoji["emoji"])
key._is_emoji = True
grid.attach(key, left, top, width, height)
left += 1
if left > 8:
left = 0
top += 1
self.append_page(scroll, tab_widget)
self.set_tab_reorderable(scroll, False)
self.set_tab_detachable(scroll, False)
def create_scroll_and_grid(self):
scroll = Gtk.ScrolledWindow()
grid = Gtk.Grid()
scroll.add(grid)
return scroll, grid
class Emoji_Popover(Gtk.Popover):
"""docstring for Emoji_Popover."""
def __init__(self):
super(Emoji_Popover, self).__init__()
emoji_notebook = Emoji_Notebook()
self.add(emoji_notebook)
self.set_default_widget(emoji_notebook)
self.setup_styling()
self._emoji_key = None
def setup_styling(self):
self.set_vexpand(True)
self.set_size_request(480, 280)
def setup_signals(self):
self.connect("closed", self._emoji_key.unset_selected)
def set_parent_key(self, emoji_key):
self._emoji_key = emoji_key
self.setup_signals()

View File

@ -9,13 +9,12 @@ from gi.repository import Gtk
class Key(Gtk.Button or Gtk.ToggleButton): class Key(Gtk.Button or Gtk.ToggleButton):
def __init__(self, primary = "NULL", secondary = "NULL", emoji = "NULL", iscontrol=False): def __init__(self, primary = "NULL", secondary = "NULL", iscontrol=False):
super(Key, self).__init__() super(Key, self).__init__()
self.iscontrol = iscontrol self.iscontrol = iscontrol
self._primary_symbol = primary self._primary_symbol = primary
self._secondary_symbol = secondary self._secondary_symbol = secondary
self._emoji_symbol = emoji
self._is_upper = False self._is_upper = False
self._is_symbol = False self._is_symbol = False
self._is_emoji = False self._is_emoji = False
@ -30,13 +29,17 @@ class Key(Gtk.Button or Gtk.ToggleButton):
def setup_signals(self): def setup_signals(self):
self.connect("released", self._do_type) self.connect("released", self._do_type)
# self.connect("toggle-caps", self.toggle_caps)
# self.connect("toggle-symbol-keys", self.toggle_symbol_keys)
self.connect("toggle-emoji-keys", self.toggle_emoji_keys) self.connect("toggle-emoji-keys", self.toggle_emoji_keys)
def _do_type(self, widget = None): def _do_type(self, widget = None):
key = self.get_label().strip() key = self.get_label().strip()
typwriter.type(key) if not self._is_emoji:
typwriter.type(key)
else:
typwriter.set_clipboard_data(key, "utf-16")
typwriter.isCtrlOn = True
typwriter.type('v')
typwriter.isCtrlOn = False
def _do_press_special_key(self, widget = None): def _do_press_special_key(self, widget = None):
key = self.get_label() key = self.get_label()
@ -51,18 +54,13 @@ class Key(Gtk.Button or Gtk.ToggleButton):
self._is_symbol = not self._is_symbol self._is_symbol = not self._is_symbol
if self._is_symbol: if self._is_symbol:
self.set_label(self._secondary_symbol) self.set_label(self._secondary_symbol)
elif self._is_emoji:
self.set_label(self._emoji_symbol)
else: else:
self.set_label(self._primary_symbol.upper()) if self._is_upper else self.set_label(self._primary_symbol.lower()) self.set_label(self._primary_symbol.upper()) if self._is_upper else self.set_label(self._primary_symbol.lower())
# NOTE: Might use name attrib on widgets and de-duplicate this and the above logic. # NOTE: Might use name attrib on widgets and de-duplicate this and the above logic.
def toggle_emoji_keys(self, widget = None, eve = None): def toggle_emoji_keys(self, widget = None, eve = None):
if not self.iscontrol: if not self.iscontrol:
self._is_emoji = not self._is_emoji if self._is_symbol:
if self._is_emoji:
self.set_label(self._emoji_symbol)
elif self._is_symbol:
self.set_label(self._secondary_symbol) self.set_label(self._secondary_symbol)
else: else:
self.set_label(self._primary_symbol.upper()) if self._is_upper else self.set_label(self._primary_symbol.lower()) self.set_label(self._primary_symbol.upper()) if self._is_upper else self.set_label(self._primary_symbol.lower())

View File

@ -16,9 +16,6 @@ from .container import Container
class MissingConfigError(Exception):
pass
class Window(SignalsMixin, Gtk.ApplicationWindow): class Window(SignalsMixin, Gtk.ApplicationWindow):
"""docstring for Window.""" """docstring for Window."""
@ -26,22 +23,6 @@ class Window(SignalsMixin, Gtk.ApplicationWindow):
def __init__(self, args, unknownargs): def __init__(self, args, unknownargs):
super(Window, self).__init__() super(Window, self).__init__()
self._USER_HOME = os.path.expanduser('~')
self._USR_PATH = f"/usr/share/{app_name.lower()}"
self._CONFIG_PATH = f"{self._USER_HOME}/.config/{app_name.lower()}"
self._ICON_FILE = f"{self._CONFIG_PATH}/icon.png"
self._CSS_FILE = f"{self._CONFIG_PATH}/stylesheet.css"
if not os.path.exists(self._ICON_FILE):
self._ICON_FILE = f"{self._USR_PATH}/icon.png"
if not os.path.exists(self._ICON_FILE):
raise MissingConfigError("Unable to find the application icon.")
if not os.path.exists(self._ICON_FILE):
self._CSS_FILE = f"{self._USR_PATH}/stylesheet.css"
if not os.path.exists(self._ICON_FILE):
raise MissingConfigError("Unable to find the stylesheet.")
self.setup_win_settings() self.setup_win_settings()
self.setup_styling() self.setup_styling()
self.setup_signals() self.setup_signals()
@ -50,12 +31,11 @@ class Window(SignalsMixin, Gtk.ApplicationWindow):
self.show_all() self.show_all()
def setup_signals(self): def setup_signals(self):
self.connect("delete-event", Gtk.main_quit) self.connect("delete-event", Gtk.main_quit)
def setup_win_settings(self): def setup_win_settings(self):
self.set_icon_from_file(self._ICON_FILE) self.set_icon_from_file(ICON_FILE)
self.set_title(app_name) self.set_title(app_name)
self.set_default_size(800, 200) self.set_default_size(800, 200)
self.set_keep_above(True) self.set_keep_above(True)
@ -77,7 +57,7 @@ class Window(SignalsMixin, Gtk.ApplicationWindow):
self.connect("draw", self._area_draw) self.connect("draw", self._area_draw)
css_provider = Gtk.CssProvider() css_provider = Gtk.CssProvider()
css_provider.load_from_path(self._CSS_FILE) css_provider.load_from_path(CSS_FILE)
screen = Gdk.Screen.get_default() screen = Gdk.Screen.get_default()
style_context = Gtk.StyleContext() style_context = Gtk.StyleContext()
style_context.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER) style_context.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
from . import displayMousePosition
displayMousePosition()

View File

View File

@ -0,0 +1,434 @@
import time
import sys
try:
import Quartz
except:
assert False, "You must first install pyobjc-core and pyobjc: https://pyautogui.readthedocs.io/en/latest/install.html"
import AppKit
import pyautogui
from pyautogui import LEFT, MIDDLE, RIGHT
if sys.platform != 'darwin':
raise Exception('The pyautogui_osx module should only be loaded on an OS X system.')
""" Taken from events.h
/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
The *KB dictionaries in pyautogui map a string that can be passed to keyDown(),
keyUp(), or press() into the code used for the OS-specific keyboard function.
They should always be lowercase, and the same keys should be used across all OSes."""
keyboardMapping = dict([(key, None) for key in pyautogui.KEY_NAMES])
keyboardMapping.update({
'a': 0x00, # kVK_ANSI_A
's': 0x01, # kVK_ANSI_S
'd': 0x02, # kVK_ANSI_D
'f': 0x03, # kVK_ANSI_F
'h': 0x04, # kVK_ANSI_H
'g': 0x05, # kVK_ANSI_G
'z': 0x06, # kVK_ANSI_Z
'x': 0x07, # kVK_ANSI_X
'c': 0x08, # kVK_ANSI_C
'v': 0x09, # kVK_ANSI_V
'b': 0x0b, # kVK_ANSI_B
'q': 0x0c, # kVK_ANSI_Q
'w': 0x0d, # kVK_ANSI_W
'e': 0x0e, # kVK_ANSI_E
'r': 0x0f, # kVK_ANSI_R
'y': 0x10, # kVK_ANSI_Y
't': 0x11, # kVK_ANSI_T
'1': 0x12, # kVK_ANSI_1
'!': 0x12, # kVK_ANSI_1
'2': 0x13, # kVK_ANSI_2
'@': 0x13, # kVK_ANSI_2
'3': 0x14, # kVK_ANSI_3
'#': 0x14, # kVK_ANSI_3
'4': 0x15, # kVK_ANSI_4
'$': 0x15, # kVK_ANSI_4
'6': 0x16, # kVK_ANSI_6
'^': 0x16, # kVK_ANSI_6
'5': 0x17, # kVK_ANSI_5
'%': 0x17, # kVK_ANSI_5
'=': 0x18, # kVK_ANSI_Equal
'+': 0x18, # kVK_ANSI_Equal
'9': 0x19, # kVK_ANSI_9
'(': 0x19, # kVK_ANSI_9
'7': 0x1a, # kVK_ANSI_7
'&': 0x1a, # kVK_ANSI_7
'-': 0x1b, # kVK_ANSI_Minus
'_': 0x1b, # kVK_ANSI_Minus
'8': 0x1c, # kVK_ANSI_8
'*': 0x1c, # kVK_ANSI_8
'0': 0x1d, # kVK_ANSI_0
')': 0x1d, # kVK_ANSI_0
']': 0x1e, # kVK_ANSI_RightBracket
'}': 0x1e, # kVK_ANSI_RightBracket
'o': 0x1f, # kVK_ANSI_O
'u': 0x20, # kVK_ANSI_U
'[': 0x21, # kVK_ANSI_LeftBracket
'{': 0x21, # kVK_ANSI_LeftBracket
'i': 0x22, # kVK_ANSI_I
'p': 0x23, # kVK_ANSI_P
'l': 0x25, # kVK_ANSI_L
'j': 0x26, # kVK_ANSI_J
"'": 0x27, # kVK_ANSI_Quote
'"': 0x27, # kVK_ANSI_Quote
'k': 0x28, # kVK_ANSI_K
';': 0x29, # kVK_ANSI_Semicolon
':': 0x29, # kVK_ANSI_Semicolon
'\\': 0x2a, # kVK_ANSI_Backslash
'|': 0x2a, # kVK_ANSI_Backslash
',': 0x2b, # kVK_ANSI_Comma
'<': 0x2b, # kVK_ANSI_Comma
'/': 0x2c, # kVK_ANSI_Slash
'?': 0x2c, # kVK_ANSI_Slash
'n': 0x2d, # kVK_ANSI_N
'm': 0x2e, # kVK_ANSI_M
'.': 0x2f, # kVK_ANSI_Period
'>': 0x2f, # kVK_ANSI_Period
'`': 0x32, # kVK_ANSI_Grave
'~': 0x32, # kVK_ANSI_Grave
' ': 0x31, # kVK_Space
'space': 0x31,
'\r': 0x24, # kVK_Return
'\n': 0x24, # kVK_Return
'enter': 0x24, # kVK_Return
'return': 0x24, # kVK_Return
'\t': 0x30, # kVK_Tab
'tab': 0x30, # kVK_Tab
'backspace': 0x33, # kVK_Delete, which is "Backspace" on OS X.
'\b': 0x33, # kVK_Delete, which is "Backspace" on OS X.
'esc': 0x35, # kVK_Escape
'escape': 0x35, # kVK_Escape
'command': 0x37, # kVK_Command
'shift': 0x38, # kVK_Shift
'shiftleft': 0x38, # kVK_Shift
'capslock': 0x39, # kVK_CapsLock
'option': 0x3a, # kVK_Option
'optionleft': 0x3a, # kVK_Option
'alt': 0x3a, # kVK_Option
'altleft': 0x3a, # kVK_Option
'ctrl': 0x3b, # kVK_Control
'ctrlleft': 0x3b, # kVK_Control
'shiftright': 0x3c, # kVK_RightShift
'optionright': 0x3d, # kVK_RightOption
'ctrlright': 0x3e, # kVK_RightControl
'fn': 0x3f, # kVK_Function
'f17': 0x40, # kVK_F17
'volumeup': 0x48, # kVK_VolumeUp
'volumedown': 0x49, # kVK_VolumeDown
'volumemute': 0x4a, # kVK_Mute
'f18': 0x4f, # kVK_F18
'f19': 0x50, # kVK_F19
'f20': 0x5a, # kVK_F20
'f5': 0x60, # kVK_F5
'f6': 0x61, # kVK_F6
'f7': 0x62, # kVK_F7
'f3': 0x63, # kVK_F3
'f8': 0x64, # kVK_F8
'f9': 0x65, # kVK_F9
'f11': 0x67, # kVK_F11
'f13': 0x69, # kVK_F13
'f16': 0x6a, # kVK_F16
'f14': 0x6b, # kVK_F14
'f10': 0x6d, # kVK_F10
'f12': 0x6f, # kVK_F12
'f15': 0x71, # kVK_F15
'help': 0x72, # kVK_Help
'home': 0x73, # kVK_Home
'pageup': 0x74, # kVK_PageUp
'pgup': 0x74, # kVK_PageUp
'del': 0x75, # kVK_ForwardDelete
'delete': 0x75, # kVK_ForwardDelete
'f4': 0x76, # kVK_F4
'end': 0x77, # kVK_End
'f2': 0x78, # kVK_F2
'pagedown': 0x79, # kVK_PageDown
'pgdn': 0x79, # kVK_PageDown
'f1': 0x7a, # kVK_F1
'left': 0x7b, # kVK_LeftArrow
'right': 0x7c, # kVK_RightArrow
'down': 0x7d, # kVK_DownArrow
'up': 0x7e, # kVK_UpArrow
'yen': 0x5d, # kVK_JIS_Yen
#'underscore' : 0x5e, # kVK_JIS_Underscore (only applies to Japanese keyboards)
#'comma': 0x5f, # kVK_JIS_KeypadComma (only applies to Japanese keyboards)
'eisu': 0x66, # kVK_JIS_Eisu
'kana': 0x68, # kVK_JIS_Kana
})
"""
# TODO - additional key codes to add
kVK_ANSI_KeypadDecimal = 0x41,
kVK_ANSI_KeypadMultiply = 0x43,
kVK_ANSI_KeypadPlus = 0x45,
kVK_ANSI_KeypadClear = 0x47,
kVK_ANSI_KeypadDivide = 0x4B,
kVK_ANSI_KeypadEnter = 0x4C,
kVK_ANSI_KeypadMinus = 0x4E,
kVK_ANSI_KeypadEquals = 0x51,
kVK_ANSI_Keypad0 = 0x52,
kVK_ANSI_Keypad1 = 0x53,
kVK_ANSI_Keypad2 = 0x54,
kVK_ANSI_Keypad3 = 0x55,
kVK_ANSI_Keypad4 = 0x56,
kVK_ANSI_Keypad5 = 0x57,
kVK_ANSI_Keypad6 = 0x58,
kVK_ANSI_Keypad7 = 0x59,
kVK_ANSI_Keypad8 = 0x5B,
kVK_ANSI_Keypad9 = 0x5C,
"""
# add mappings for uppercase letters
for c in 'abcdefghijklmnopqrstuvwxyz':
keyboardMapping[c.upper()] = keyboardMapping[c]
# 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
}
def _keyDown(key):
if key not in keyboardMapping or keyboardMapping[key] is None:
return
if key in special_key_translate_table:
_specialKeyEvent(key, 'down')
else:
_normalKeyEvent(key, 'down')
def _keyUp(key):
if key not in keyboardMapping or keyboardMapping[key] is None:
return
if key in special_key_translate_table:
_specialKeyEvent(key, 'up')
else:
_normalKeyEvent(key, 'up')
def _normalKeyEvent(key, upDown):
assert upDown in ('up', 'down'), "upDown argument must be 'up' or 'down'"
try:
if pyautogui.isShiftCharacter(key):
key_code = keyboardMapping[key.lower()]
event = Quartz.CGEventCreateKeyboardEvent(None,
keyboardMapping['shift'], upDown == 'down')
Quartz.CGEventPost(Quartz.kCGHIDEventTap, event)
# Tiny sleep to let OS X catch up on us pressing shift
time.sleep(pyautogui.DARWIN_CATCH_UP_TIME)
else:
key_code = keyboardMapping[key]
event = Quartz.CGEventCreateKeyboardEvent(None, key_code, upDown == 'down')
Quartz.CGEventPost(Quartz.kCGHIDEventTap, event)
time.sleep(pyautogui.DARWIN_CATCH_UP_TIME)
# TODO - wait, is the shift key's keyup not done?
# TODO - get rid of this try-except.
except KeyError:
raise RuntimeError("Key %s not implemented." % (key))
def _specialKeyEvent(key, upDown):
""" Helper method for special keys.
Source: http://stackoverflow.com/questions/11045814/emulate-media-key-press-on-mac
"""
assert upDown in ('up', 'down'), "upDown argument must be 'up' or 'down'"
key_code = special_key_translate_table[key]
ev = AppKit.NSEvent.otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
Quartz.NSSystemDefined, # type
(0,0), # location
0xa00 if upDown == 'down' else 0xb00, # flags
0, # timestamp
0, # window
0, # ctx
8, # subtype
(key_code << 16) | ((0xa if upDown == 'down' else 0xb) << 8), # data1
-1 # data2
)
Quartz.CGEventPost(0, ev.CGEvent())
def _position():
loc = AppKit.NSEvent.mouseLocation()
return int(loc.x), int(Quartz.CGDisplayPixelsHigh(0) - loc.y)
def _size():
return Quartz.CGDisplayPixelsWide(Quartz.CGMainDisplayID()), Quartz.CGDisplayPixelsHigh(Quartz.CGMainDisplayID())
def _scroll(clicks, x=None, y=None):
_vscroll(clicks, x, y)
"""
According to https://developer.apple.com/library/mac/documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html#//apple_ref/c/func/Quartz.CGEventCreateScrollWheelEvent
"Scrolling movement is generally represented by small signed integer values, typically in a range from -10 to +10. Large values may have unexpected results, depending on the application that processes the event."
The scrolling functions will create multiple events that scroll 10 each, and then scroll the remainder.
"""
def _vscroll(clicks, x=None, y=None):
_moveTo(x, y)
clicks = int(clicks)
for _ in range(abs(clicks) // 10):
scrollWheelEvent = Quartz.CGEventCreateScrollWheelEvent(
None, # no source
Quartz.kCGScrollEventUnitLine, # units
1, # wheelCount (number of dimensions)
10 if clicks >= 0 else -10) # vertical movement
Quartz.CGEventPost(Quartz.kCGHIDEventTap, scrollWheelEvent)
scrollWheelEvent = Quartz.CGEventCreateScrollWheelEvent(
None, # no source
Quartz.kCGScrollEventUnitLine, # units
1, # wheelCount (number of dimensions)
clicks % 10 if clicks >= 0 else -1 * (-clicks % 10)) # vertical movement
Quartz.CGEventPost(Quartz.kCGHIDEventTap, scrollWheelEvent)
def _hscroll(clicks, x=None, y=None):
_moveTo(x, y)
clicks = int(clicks)
for _ in range(abs(clicks) // 10):
scrollWheelEvent = Quartz.CGEventCreateScrollWheelEvent(
None, # no source
Quartz.kCGScrollEventUnitLine, # units
2, # wheelCount (number of dimensions)
0, # vertical movement
10 if clicks >= 0 else -10) # horizontal movement
Quartz.CGEventPost(Quartz.kCGHIDEventTap, scrollWheelEvent)
scrollWheelEvent = Quartz.CGEventCreateScrollWheelEvent(
None, # no source
Quartz.kCGScrollEventUnitLine, # units
2, # wheelCount (number of dimensions)
0, # vertical movement
(clicks % 10) if clicks >= 0 else (-1 * clicks % 10)) # horizontal movement
Quartz.CGEventPost(Quartz.kCGHIDEventTap, scrollWheelEvent)
def _mouseDown(x, y, button):
if button == LEFT:
_sendMouseEvent(Quartz.kCGEventLeftMouseDown, x, y, Quartz.kCGMouseButtonLeft)
elif button == MIDDLE:
_sendMouseEvent(Quartz.kCGEventOtherMouseDown, x, y, Quartz.kCGMouseButtonCenter)
elif button == RIGHT:
_sendMouseEvent(Quartz.kCGEventRightMouseDown, x, y, Quartz.kCGMouseButtonRight)
else:
assert False, "button argument not in ('left', 'middle', 'right')"
def _mouseUp(x, y, button):
if button == LEFT:
_sendMouseEvent(Quartz.kCGEventLeftMouseUp, x, y, Quartz.kCGMouseButtonLeft)
elif button == MIDDLE:
_sendMouseEvent(Quartz.kCGEventOtherMouseUp, x, y, Quartz.kCGMouseButtonCenter)
elif button == RIGHT:
_sendMouseEvent(Quartz.kCGEventRightMouseUp, x, y, Quartz.kCGMouseButtonRight)
else:
assert False, "button argument not in ('left', 'middle', 'right')"
def _click(x, y, button):
if button == LEFT:
_sendMouseEvent(Quartz.kCGEventLeftMouseDown, x, y, Quartz.kCGMouseButtonLeft)
_sendMouseEvent(Quartz.kCGEventLeftMouseUp, x, y, Quartz.kCGMouseButtonLeft)
elif button == MIDDLE:
_sendMouseEvent(Quartz.kCGEventOtherMouseDown, x, y, Quartz.kCGMouseButtonCenter)
_sendMouseEvent(Quartz.kCGEventOtherMouseUp, x, y, Quartz.kCGMouseButtonCenter)
elif button == RIGHT:
_sendMouseEvent(Quartz.kCGEventRightMouseDown, x, y, Quartz.kCGMouseButtonRight)
_sendMouseEvent(Quartz.kCGEventRightMouseUp, x, y, Quartz.kCGMouseButtonRight)
else:
assert False, "button argument not in ('left', 'middle', 'right')"
def _multiClick(x, y, button, num, interval=0.0):
btn = None
down = None
up = None
if button == LEFT:
btn = Quartz.kCGMouseButtonLeft
down = Quartz.kCGEventLeftMouseDown
up = Quartz.kCGEventLeftMouseUp
elif button == MIDDLE:
btn = Quartz.kCGMouseButtonCenter
down = Quartz.kCGEventOtherMouseDown
up = Quartz.kCGEventOtherMouseUp
elif button == RIGHT:
btn = Quartz.kCGMouseButtonRight
down = Quartz.kCGEventRightMouseDown
up = Quartz.kCGEventRightMouseUp
else:
assert False, "button argument not in ('left', 'middle', 'right')"
return
for i in range(num):
_click(x, y, button)
time.sleep(interval)
def _sendMouseEvent(ev, x, y, button):
mouseEvent = Quartz.CGEventCreateMouseEvent(None, ev, (x, y), button)
Quartz.CGEventPost(Quartz.kCGHIDEventTap, mouseEvent)
def _dragTo(x, y, button):
if button == LEFT:
_sendMouseEvent(Quartz.kCGEventLeftMouseDragged , x, y, Quartz.kCGMouseButtonLeft)
elif button == MIDDLE:
_sendMouseEvent(Quartz.kCGEventOtherMouseDragged , x, y, Quartz.kCGMouseButtonCenter)
elif button == RIGHT:
_sendMouseEvent(Quartz.kCGEventRightMouseDragged , x, y, Quartz.kCGMouseButtonRight)
else:
assert False, "button argument not in ('left', 'middle', 'right')"
time.sleep(pyautogui.DARWIN_CATCH_UP_TIME) # needed to allow OS time to catch up.
def _moveTo(x, y):
_sendMouseEvent(Quartz.kCGEventMouseMoved, x, y, 0)
time.sleep(pyautogui.DARWIN_CATCH_UP_TIME) # needed to allow OS time to catch up.

View File

@ -0,0 +1,568 @@
# Windows implementation of PyAutoGUI functions.
# BSD license
# Al Sweigart al@inventwithpython.com
import ctypes
import ctypes.wintypes
import pyautogui
from pyautogui import LEFT, MIDDLE, RIGHT
import sys
if sys.platform != 'win32':
raise Exception('The pyautogui_win module should only be loaded on a Windows system.')
# Fixes the scaling issues where PyAutoGUI was reporting the wrong resolution:
try:
ctypes.windll.user32.SetProcessDPIAware()
except AttributeError:
pass # Windows XP doesn't support this, so just do nothing.
"""
A lot of this code is probably repeated from win32 extensions module, but I didn't want to have that dependency.
Note: According to http://msdn.microsoft.com/en-us/library/windows/desktop/ms646260(v=vs.85).aspx
the ctypes.windll.user32.mouse_event() function has been superseded by SendInput.
SendInput() is documented here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646310(v=vs.85).aspx
UPDATE: SendInput() doesn't seem to be working for me. I've switched back to mouse_event()."""
# Event codes to be passed to the mouse_event() win32 function.
# Documented here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646273(v=vs.85).aspx
MOUSEEVENTF_MOVE = 0x0001
MOUSEEVENTF_LEFTDOWN = 0x0002
MOUSEEVENTF_LEFTUP = 0x0004
MOUSEEVENTF_LEFTCLICK = MOUSEEVENTF_LEFTDOWN + MOUSEEVENTF_LEFTUP
MOUSEEVENTF_RIGHTDOWN = 0x0008
MOUSEEVENTF_RIGHTUP = 0x0010
MOUSEEVENTF_RIGHTCLICK = MOUSEEVENTF_RIGHTDOWN + MOUSEEVENTF_RIGHTUP
MOUSEEVENTF_MIDDLEDOWN = 0x0020
MOUSEEVENTF_MIDDLEUP = 0x0040
MOUSEEVENTF_MIDDLECLICK = MOUSEEVENTF_MIDDLEDOWN + MOUSEEVENTF_MIDDLEUP
MOUSEEVENTF_ABSOLUTE = 0x8000
MOUSEEVENTF_WHEEL = 0x0800
MOUSEEVENTF_HWHEEL = 0x01000
# Documented here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646304(v=vs.85).aspx
KEYEVENTF_KEYDOWN = 0x0000 # Technically this constant doesn't exist in the MS documentation. It's the lack of KEYEVENTF_KEYUP that means pressing the key down.
KEYEVENTF_KEYUP = 0x0002
# Documented here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646270(v=vs.85).aspx
INPUT_MOUSE = 0
INPUT_KEYBOARD = 1
# These ctypes structures are for Win32 INPUT, MOUSEINPUT, KEYBDINPUT, and HARDWAREINPUT structures,
# used by SendInput and documented here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646270(v=vs.85).aspx
# Thanks to BSH for this StackOverflow answer: https://stackoverflow.com/questions/18566289/how-would-you-recreate-this-windows-api-structure-with-ctypes
class MOUSEINPUT(ctypes.Structure):
_fields_ = [
('dx', ctypes.wintypes.LONG),
('dy', ctypes.wintypes.LONG),
('mouseData', ctypes.wintypes.DWORD),
('dwFlags', ctypes.wintypes.DWORD),
('time', ctypes.wintypes.DWORD),
('dwExtraInfo', ctypes.POINTER(ctypes.wintypes.ULONG)),
]
class KEYBDINPUT(ctypes.Structure):
_fields_ = [
('wVk', ctypes.wintypes.WORD),
('wScan', ctypes.wintypes.WORD),
('dwFlags', ctypes.wintypes.DWORD),
('time', ctypes.wintypes.DWORD),
('dwExtraInfo', ctypes.POINTER(ctypes.wintypes.ULONG)),
]
class HARDWAREINPUT(ctypes.Structure):
_fields_ = [
('uMsg', ctypes.wintypes.DWORD),
('wParamL', ctypes.wintypes.WORD),
('wParamH', ctypes.wintypes.DWORD)
]
class INPUT(ctypes.Structure):
class _I(ctypes.Union):
_fields_ = [
('mi', MOUSEINPUT),
('ki', KEYBDINPUT),
('hi', HARDWAREINPUT),
]
_anonymous_ = ('i', )
_fields_ = [
('type', ctypes.wintypes.DWORD),
('i', _I),
]
# End of the SendInput win32 data structures.
""" Keyboard key mapping for pyautogui:
Documented at http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
The *KB dictionaries in pyautogui map a string that can be passed to keyDown(),
keyUp(), or press() into the code used for the OS-specific keyboard function.
They should always be lowercase, and the same keys should be used across all OSes."""
keyboardMapping = dict([(key, None) for key in pyautogui.KEY_NAMES])
keyboardMapping.update({
'backspace': 0x08, # VK_BACK
'\b': 0x08, # VK_BACK
'super': 0x5B, #VK_LWIN
'tab': 0x09, # VK_TAB
'\t': 0x09, # VK_TAB
'clear': 0x0c, # VK_CLEAR
'enter': 0x0d, # VK_RETURN
'\n': 0x0d, # VK_RETURN
'return': 0x0d, # VK_RETURN
'shift': 0x10, # VK_SHIFT
'ctrl': 0x11, # VK_CONTROL
'alt': 0x12, # VK_MENU
'pause': 0x13, # VK_PAUSE
'capslock': 0x14, # VK_CAPITAL
'kana': 0x15, # VK_KANA
'hanguel': 0x15, # VK_HANGUEL
'hangul': 0x15, # VK_HANGUL
'junja': 0x17, # VK_JUNJA
'final': 0x18, # VK_FINAL
'hanja': 0x19, # VK_HANJA
'kanji': 0x19, # VK_KANJI
'esc': 0x1b, # VK_ESCAPE
'escape': 0x1b, # VK_ESCAPE
'convert': 0x1c, # VK_CONVERT
'nonconvert': 0x1d, # VK_NONCONVERT
'accept': 0x1e, # VK_ACCEPT
'modechange': 0x1f, # VK_MODECHANGE
' ': 0x20, # VK_SPACE
'space': 0x20, # VK_SPACE
'pgup': 0x21, # VK_PRIOR
'pgdn': 0x22, # VK_NEXT
'pageup': 0x21, # VK_PRIOR
'pagedown': 0x22, # VK_NEXT
'end': 0x23, # VK_END
'home': 0x24, # VK_HOME
'left': 0x25, # VK_LEFT
'up': 0x26, # VK_UP
'right': 0x27, # VK_RIGHT
'down': 0x28, # VK_DOWN
'select': 0x29, # VK_SELECT
'print': 0x2a, # VK_PRINT
'execute': 0x2b, # VK_EXECUTE
'prtsc': 0x2c, # VK_SNAPSHOT
'prtscr': 0x2c, # VK_SNAPSHOT
'prntscrn': 0x2c, # VK_SNAPSHOT
'printscreen': 0x2c, # VK_SNAPSHOT
'insert': 0x2d, # VK_INSERT
'del': 0x2e, # VK_DELETE
'delete': 0x2e, # VK_DELETE
'help': 0x2f, # VK_HELP
'win': 0x5b, # VK_LWIN
'winleft': 0x5b, # VK_LWIN
'winright': 0x5c, # VK_RWIN
'apps': 0x5d, # VK_APPS
'sleep': 0x5f, # VK_SLEEP
'num0': 0x60, # VK_NUMPAD0
'num1': 0x61, # VK_NUMPAD1
'num2': 0x62, # VK_NUMPAD2
'num3': 0x63, # VK_NUMPAD3
'num4': 0x64, # VK_NUMPAD4
'num5': 0x65, # VK_NUMPAD5
'num6': 0x66, # VK_NUMPAD6
'num7': 0x67, # VK_NUMPAD7
'num8': 0x68, # VK_NUMPAD8
'num9': 0x69, # VK_NUMPAD9
'multiply': 0x6a, # VK_MULTIPLY ??? Is this the numpad *?
'add': 0x6b, # VK_ADD ??? Is this the numpad +?
'separator': 0x6c, # VK_SEPARATOR ??? Is this the numpad enter?
'subtract': 0x6d, # VK_SUBTRACT ??? Is this the numpad -?
'decimal': 0x6e, # VK_DECIMAL
'divide': 0x6f, # VK_DIVIDE
'f1': 0x70, # VK_F1
'f2': 0x71, # VK_F2
'f3': 0x72, # VK_F3
'f4': 0x73, # VK_F4
'f5': 0x74, # VK_F5
'f6': 0x75, # VK_F6
'f7': 0x76, # VK_F7
'f8': 0x77, # VK_F8
'f9': 0x78, # VK_F9
'f10': 0x79, # VK_F10
'f11': 0x7a, # VK_F11
'f12': 0x7b, # VK_F12
'f13': 0x7c, # VK_F13
'f14': 0x7d, # VK_F14
'f15': 0x7e, # VK_F15
'f16': 0x7f, # VK_F16
'f17': 0x80, # VK_F17
'f18': 0x81, # VK_F18
'f19': 0x82, # VK_F19
'f20': 0x83, # VK_F20
'f21': 0x84, # VK_F21
'f22': 0x85, # VK_F22
'f23': 0x86, # VK_F23
'f24': 0x87, # VK_F24
'numlock': 0x90, # VK_NUMLOCK
'scrolllock': 0x91, # VK_SCROLL
'shiftleft': 0xa0, # VK_LSHIFT
'shiftright': 0xa1, # VK_RSHIFT
'ctrlleft': 0xa2, # VK_LCONTROL
'ctrlright': 0xa3, # VK_RCONTROL
'altleft': 0xa4, # VK_LMENU
'altright': 0xa5, # VK_RMENU
'browserback': 0xa6, # VK_BROWSER_BACK
'browserforward': 0xa7, # VK_BROWSER_FORWARD
'browserrefresh': 0xa8, # VK_BROWSER_REFRESH
'browserstop': 0xa9, # VK_BROWSER_STOP
'browsersearch': 0xaa, # VK_BROWSER_SEARCH
'browserfavorites': 0xab, # VK_BROWSER_FAVORITES
'browserhome': 0xac, # VK_BROWSER_HOME
'volumemute': 0xad, # VK_VOLUME_MUTE
'volumedown': 0xae, # VK_VOLUME_DOWN
'volumeup': 0xaf, # VK_VOLUME_UP
'nexttrack': 0xb0, # VK_MEDIA_NEXT_TRACK
'prevtrack': 0xb1, # VK_MEDIA_PREV_TRACK
'stop': 0xb2, # VK_MEDIA_STOP
'playpause': 0xb3, # VK_MEDIA_PLAY_PAUSE
'launchmail': 0xb4, # VK_LAUNCH_MAIL
'launchmediaselect': 0xb5, # VK_LAUNCH_MEDIA_SELECT
'launchapp1': 0xb6, # VK_LAUNCH_APP1
'launchapp2': 0xb7, # VK_LAUNCH_APP2
})
# There are other virtual key constants that are not used here because the printable ascii keys are
# handled in the following `for` loop.
# The virtual key constants that aren't used are:
# VK_OEM_1, VK_OEM_PLUS, VK_OEM_COMMA, VK_OEM_MINUS, VK_OEM_PERIOD, VK_OEM_2, VK_OEM_3, VK_OEM_4,
# VK_OEM_5, VK_OEM_6, VK_OEM_7, VK_OEM_8, VK_PACKET, VK_ATTN, VK_CRSEL, VK_EXSEL, VK_EREOF,
# VK_PLAY, VK_ZOOM, VK_NONAME, VK_PA1, VK_OEM_CLEAR
# Populate the basic printable ascii characters.
# https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscana
for c in range(32, 128):
keyboardMapping[chr(c)] = ctypes.windll.user32.VkKeyScanA(ctypes.wintypes.WCHAR(chr(c)))
def _keyDown(key):
"""Performs a keyboard key press without the release. This will put that
key in a held down state.
NOTE: For some reason, this does not seem to cause key repeats like would
happen if a keyboard key was held down on a text field.
Args:
key (str): The key to be pressed down. The valid names are listed in
pyautogui.KEY_NAMES.
Returns:
None
"""
if key not in keyboardMapping or keyboardMapping[key] is None:
return
needsShift = pyautogui.isShiftCharacter(key)
"""
# OLD CODE: The new code relies on having all keys be loaded in keyboardMapping from the start.
if key in keyboardMapping.keys():
vkCode = keyboardMapping[key]
elif len(key) == 1:
# note: I could use this case to update keyboardMapping to cache the VkKeyScan results, but I've decided not to just to make any possible bugs easier to reproduce.
vkCode = ctypes.windll.user32.VkKeyScanW(ctypes.wintypes.WCHAR(key))
if vkCode == -1:
raise ValueError('There is no VK code for key "%s"' % (key))
if vkCode > 0x100: # the vk code will be > 0x100 if it needs shift
vkCode -= 0x100
needsShift = True
"""
mods, vkCode = divmod(keyboardMapping[key], 0x100)
for apply_mod, vk_mod in [(mods & 4, 0x12), (mods & 2, 0x11),
(mods & 1 or needsShift, 0x10)]: #HANKAKU not supported! mods & 8
if apply_mod:
ctypes.windll.user32.keybd_event(vk_mod, 0, KEYEVENTF_KEYDOWN, 0) #
ctypes.windll.user32.keybd_event(vkCode, 0, KEYEVENTF_KEYDOWN, 0)
for apply_mod, vk_mod in [(mods & 1 or needsShift, 0x10), (mods & 2, 0x11),
(mods & 4, 0x12)]: #HANKAKU not supported! mods & 8
if apply_mod:
ctypes.windll.user32.keybd_event(vk_mod, 0, KEYEVENTF_KEYUP, 0) #
def _keyUp(key):
"""Performs a keyboard key release (without the press down beforehand).
Args:
key (str): The key to be released up. The valid names are listed in
pyautogui.KEY_NAMES.
Returns:
None
"""
if key not in keyboardMapping or keyboardMapping[key] is None:
return
needsShift = pyautogui.isShiftCharacter(key)
"""
# OLD CODE: The new code relies on having all keys be loaded in keyboardMapping from the start.
if key in keyboardMapping.keys():
vkCode = keyboardMapping[key]
elif len(key) == 1:
# note: I could use this case to update keyboardMapping to cache the VkKeyScan results, but I've decided not to just to make any possible bugs easier to reproduce.
vkCode = ctypes.windll.user32.VkKeyScanW(ctypes.wintypes.WCHAR(key))
if vkCode == -1:
raise ValueError('There is no VK code for key "%s"' % (key))
if vkCode > 0x100: # the vk code will be > 0x100 if it needs shift
vkCode -= 0x100
needsShift = True
"""
mods, vkCode = divmod(keyboardMapping[key], 0x100)
for apply_mod, vk_mod in [(mods & 4, 0x12), (mods & 2, 0x11),
(mods & 1 or needsShift, 0x10)]: #HANKAKU not supported! mods & 8
if apply_mod:
ctypes.windll.user32.keybd_event(vk_mod, 0, 0, 0) #
ctypes.windll.user32.keybd_event(vkCode, 0, KEYEVENTF_KEYUP, 0)
for apply_mod, vk_mod in [(mods & 1 or needsShift, 0x10), (mods & 2, 0x11),
(mods & 4, 0x12)]: #HANKAKU not supported! mods & 8
if apply_mod:
ctypes.windll.user32.keybd_event(vk_mod, 0, KEYEVENTF_KEYUP, 0) #
def _position():
"""Returns the current xy coordinates of the mouse cursor as a two-integer
tuple by calling the GetCursorPos() win32 function.
Returns:
(x, y) tuple of the current xy coordinates of the mouse cursor.
"""
cursor = ctypes.wintypes.POINT()
ctypes.windll.user32.GetCursorPos(ctypes.byref(cursor))
return (cursor.x, cursor.y)
def _size():
"""Returns the width and height of the screen as a two-integer tuple.
Returns:
(width, height) tuple of the screen size, in pixels.
"""
return (ctypes.windll.user32.GetSystemMetrics(0), ctypes.windll.user32.GetSystemMetrics(1))
def _moveTo(x, y):
"""Send the mouse move event to Windows by calling SetCursorPos() win32
function.
Args:
button (str): The mouse button, either 'left', 'middle', or 'right'
x (int): The x position of the mouse event.
y (int): The y position of the mouse event.
Returns:
None
"""
ctypes.windll.user32.SetCursorPos(x, y)
# This was a possible solution to issue #314 https://github.com/asweigart/pyautogui/issues/314
# but I'd like to hang on to SetCursorPos because mouse_event() has been superseded.
#_sendMouseEvent(MOUSEEVENTF_MOVE + MOUSEEVENTF_ABSOLUTE, x, y)
def _mouseDown(x, y, button):
"""Send the mouse down event to Windows by calling the mouse_event() win32
function.
Args:
x (int): The x position of the mouse event.
y (int): The y position of the mouse event.
button (str): The mouse button, either 'left', 'middle', or 'right'
Returns:
None
"""
if button not in (LEFT, MIDDLE, RIGHT):
raise ValueError('button arg to _click() must be one of "left", "middle", or "right", not %s' % button)
if button == LEFT:
EV = MOUSEEVENTF_LEFTDOWN
elif button == MIDDLE:
EV = MOUSEEVENTF_MIDDLEDOWN
elif button == RIGHT:
EV = MOUSEEVENTF_RIGHTDOWN
try:
_sendMouseEvent(EV, x, y)
except (PermissionError, OSError):
# TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
pass
def _mouseUp(x, y, button):
"""Send the mouse up event to Windows by calling the mouse_event() win32
function.
Args:
x (int): The x position of the mouse event.
y (int): The y position of the mouse event.
button (str): The mouse button, either 'left', 'middle', or 'right'
Returns:
None
"""
if button not in (LEFT, MIDDLE, RIGHT):
raise ValueError('button arg to _click() must be one of "left", "middle", or "right", not %s' % button)
if button == LEFT:
EV = MOUSEEVENTF_LEFTUP
elif button == MIDDLE:
EV = MOUSEEVENTF_MIDDLEUP
elif button == RIGHT:
EV = MOUSEEVENTF_RIGHTUP
try:
_sendMouseEvent(EV, x, y)
except (PermissionError, OSError): # TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
pass
def _click(x, y, button):
"""Send the mouse click event to Windows by calling the mouse_event() win32
function.
Args:
button (str): The mouse button, either 'left', 'middle', or 'right'
x (int): The x position of the mouse event.
y (int): The y position of the mouse event.
Returns:
None
"""
if button not in (LEFT, MIDDLE, RIGHT):
raise ValueError('button arg to _click() must be one of "left", "middle", or "right", not %s' % button)
if button == LEFT:
EV = MOUSEEVENTF_LEFTCLICK
elif button == MIDDLE:
EV = MOUSEEVENTF_MIDDLECLICK
elif button ==RIGHT:
EV = MOUSEEVENTF_RIGHTCLICK
try:
_sendMouseEvent(EV, x, y)
except (PermissionError, OSError):
# TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
pass
def _sendMouseEvent(ev, x, y, dwData=0):
"""The helper function that actually makes the call to the mouse_event()
win32 function.
Args:
ev (int): The win32 code for the mouse event. Use one of the MOUSEEVENTF_*
constants for this argument.
x (int): The x position of the mouse event.
y (int): The y position of the mouse event.
dwData (int): The argument for mouse_event()'s dwData parameter. So far
this is only used by mouse scrolling.
Returns:
None
"""
assert x != None and y != None, 'x and y cannot be set to None'
# TODO: ARG! For some reason, SendInput isn't working for mouse events. I'm switching to using the older mouse_event win32 function.
#mouseStruct = MOUSEINPUT()
#mouseStruct.dx = x
#mouseStruct.dy = y
#mouseStruct.mouseData = ev
#mouseStruct.time = 0
#mouseStruct.dwExtraInfo = ctypes.pointer(ctypes.c_ulong(0)) # according to https://stackoverflow.com/questions/13564851/generate-keyboard-events I can just set this. I don't really care about this value.
#inputStruct = INPUT()
#inputStruct.mi = mouseStruct
#inputStruct.type = INPUT_MOUSE
#ctypes.windll.user32.SendInput(1, ctypes.pointer(inputStruct), ctypes.sizeof(inputStruct))
# TODO Note: We need to handle additional buttons, which I believe is documented here:
# https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-mouse_event
width, height = _size()
convertedX = 65536 * x // width + 1
convertedY = 65536 * y // height + 1
ctypes.windll.user32.mouse_event(ev, ctypes.c_long(convertedX), ctypes.c_long(convertedY), dwData, 0)
# TODO: Too many false positives with this code: See: https://github.com/asweigart/pyautogui/issues/108
#if ctypes.windll.kernel32.GetLastError() != 0:
# raise ctypes.WinError()
def _scroll(clicks, x=None, y=None):
"""Send the mouse vertical scroll event to Windows by calling the
mouse_event() win32 function.
Args:
clicks (int): The amount of scrolling to do. A positive value is the mouse
wheel moving forward (scrolling up), a negative value is backwards (down).
x (int): The x position of the mouse event.
y (int): The y position of the mouse event.
Returns:
None
"""
startx, starty = _position()
width, height = _size()
if x is None:
x = startx
else:
if x < 0:
x = 0
elif x >= width:
x = width - 1
if y is None:
y = starty
else:
if y < 0:
y = 0
elif y >= height:
y = height - 1
try:
_sendMouseEvent(MOUSEEVENTF_WHEEL, x, y, dwData=clicks)
except (PermissionError, OSError): # TODO: We need to figure out how to prevent these errors, see https://github.com/asweigart/pyautogui/issues/60
pass
def _hscroll(clicks, x, y):
"""Send the mouse horizontal scroll event to Windows by calling the
mouse_event() win32 function.
Args:
clicks (int): The amount of scrolling to do. A positive value is the mouse
wheel moving right, a negative value is moving left.
x (int): The x position of the mouse event.
y (int): The y position of the mouse event.
Returns:
None
"""
return _scroll(clicks, x, y)
def _vscroll(clicks, x, y):
"""A wrapper for _scroll(), which does vertical scrolling.
Args:
clicks (int): The amount of scrolling to do. A positive value is the mouse
wheel moving forward (scrolling up), a negative value is backwards (down).
x (int): The x position of the mouse event.
y (int): The y position of the mouse event.
Returns:
None
"""
return _scroll(clicks, x, y)

View File

@ -0,0 +1,301 @@
# NOTE - It is a known issue that the keyboard-related functions don't work on Ubuntu VMs in Virtualbox.
# import pyautogui
import sys
import os
from . import LEFT, MIDDLE, RIGHT, KEY_NAMES, isShiftCharacter
# from pyautogui import LEFT, MIDDLE, RIGHT
from Xlib.display import Display
from Xlib import X
from Xlib.ext.xtest import fake_input
import Xlib.XK
BUTTON_NAME_MAPPING = {LEFT: 1, MIDDLE: 2, RIGHT: 3, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7}
if sys.platform in ('java', 'darwin', 'win32'):
raise Exception('The pyautogui_x11 module should only be loaded on a Unix system that supports X11.')
#from pyautogui import *
"""
Much of this code is based on information gleaned from Paul Barton's PyKeyboard in PyUserInput from 2013, itself derived from Akkana Peck's pykey in 2008 ( http://www.shallowsky.com/software/crikey/pykey-0.1 ), itself derived from her "Crikey" lib.
"""
def _position():
"""Returns the current xy coordinates of the mouse cursor as a two-integer
tuple.
Returns:
(x, y) tuple of the current xy coordinates of the mouse cursor.
"""
coord = _display.screen().root.query_pointer()._data
return coord["root_x"], coord["root_y"]
def _size():
return _display.screen().width_in_pixels, _display.screen().height_in_pixels
def _vscroll(clicks, x=None, y=None):
clicks = int(clicks)
if clicks == 0:
return
elif clicks > 0:
button = 4 # scroll up
else:
button = 5 # scroll down
for i in range(abs(clicks)):
_click(x, y, button=button)
def _hscroll(clicks, x=None, y=None):
clicks = int(clicks)
if clicks == 0:
return
elif clicks > 0:
button = 7 # scroll right
else:
button = 6 # scroll left
for i in range(abs(clicks)):
_click(x, y, button=button)
def _scroll(clicks, x=None, y=None):
return _vscroll(clicks, x, y)
def _click(x, y, button):
assert button in BUTTON_NAME_MAPPING.keys(), "button argument not in ('left', 'middle', 'right', 4, 5, 6, 7)"
button = BUTTON_NAME_MAPPING[button]
_mouseDown(x, y, button)
_mouseUp(x, y, button)
def _moveTo(x, y):
fake_input(_display, X.MotionNotify, x=x, y=y)
_display.sync()
def _mouseDown(x, y, button):
_moveTo(x, y)
assert button in BUTTON_NAME_MAPPING.keys(), "button argument not in ('left', 'middle', 'right', 4, 5, 6, 7)"
button = BUTTON_NAME_MAPPING[button]
fake_input(_display, X.ButtonPress, button)
_display.sync()
def _mouseUp(x, y, button):
_moveTo(x, y)
assert button in BUTTON_NAME_MAPPING.keys(), "button argument not in ('left', 'middle', 'right', 4, 5, 6, 7)"
button = BUTTON_NAME_MAPPING[button]
fake_input(_display, X.ButtonRelease, button)
_display.sync()
def _keyDown(key):
"""Performs a keyboard key press without the release. This will put that
key in a held down state.
NOTE: For some reason, this does not seem to cause key repeats like would
happen if a keyboard key was held down on a text field.
Args:
key (str): The key to be pressed down. The valid names are listed in
KEY_NAMES.
Returns:
None
"""
if key not in keyboardMapping or keyboardMapping[key] is None:
return
if type(key) == int:
fake_input(_display, X.KeyPress, key)
_display.sync()
return
needsShift = isShiftCharacter(key)
if needsShift:
fake_input(_display, X.KeyPress, keyboardMapping['shift'])
fake_input(_display, X.KeyPress, keyboardMapping[key])
if needsShift:
fake_input(_display, X.KeyRelease, keyboardMapping['shift'])
_display.sync()
def _keyUp(key):
"""Performs a keyboard key release (without the press down beforehand).
Args:
key (str): The key to be released up. The valid names are listed in
KEY_NAMES.
Returns:
None
"""
"""
Release a given character key. Also works with character keycodes as
integers, but not keysyms.
"""
if key not in keyboardMapping or keyboardMapping[key] is None:
return
if type(key) == int:
keycode = key
else:
keycode = keyboardMapping[key]
fake_input(_display, X.KeyRelease, keycode)
_display.sync()
# Taken from PyKeyboard's ctor function.
_display = Display(os.environ['DISPLAY'])
""" Information for keyboardMapping derived from PyKeyboard's special_key_assignment() function.
The *KB dictionaries in pyautogui map a string that can be passed to keyDown(),
keyUp(), or press() into the code used for the OS-specific keyboard function.
They should always be lowercase, and the same keys should be used across all OSes."""
keyboardMapping = dict([(key, None) for key in KEY_NAMES])
keyboardMapping.update({
'backspace': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('BackSpace')),
'\b': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('BackSpace')),
'tab': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Tab')),
'enter': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Return')),
'return': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Return')),
'shift': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Shift_L')),
'ctrl': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Control_L')),
'alt': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Alt_L')),
'pause': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Pause')),
'capslock': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Caps_Lock')),
'esc': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Escape')),
'escape': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Escape')),
'pgup': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Page_Up')),
'pgdn': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Page_Down')),
'pageup': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Page_Up')),
'pagedown': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Page_Down')),
'end': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('End')),
'home': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Home')),
'left': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Left')),
'up': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Up')),
'right': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Right')),
'down': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Down')),
'select': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Select')),
'print': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Print')),
'execute': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Execute')),
'prtsc': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Print')),
'prtscr': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Print')),
'prntscrn': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Print')),
'printscreen': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Print')),
'insert': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Insert')),
'del': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Delete')),
'delete': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Delete')),
'help': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Help')),
'win': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Super_L')),
'winleft': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Super_L')),
'winright': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Super_R')),
'apps': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Menu')),
'num0': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_0')),
'num1': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_1')),
'num2': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_2')),
'num3': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_3')),
'num4': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_4')),
'num5': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_5')),
'num6': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_6')),
'num7': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_7')),
'num8': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_8')),
'num9': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_9')),
'multiply': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_Multiply')),
'add': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_Add')),
'separator': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_Separator')),
'subtract': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_Subtract')),
'decimal': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_Decimal')),
'divide': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('KP_Divide')),
'f1': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F1')),
'f2': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F2')),
'f3': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F3')),
'f4': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F4')),
'f5': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F5')),
'f6': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F6')),
'f7': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F7')),
'f8': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F8')),
'f9': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F9')),
'f10': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F10')),
'f11': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F11')),
'f12': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F12')),
'f13': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F13')),
'f14': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F14')),
'f15': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F15')),
'f16': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F16')),
'f17': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F17')),
'f18': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F18')),
'f19': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F19')),
'f20': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F20')),
'f21': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F21')),
'f22': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F22')),
'f23': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F23')),
'f24': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('F24')),
'numlock': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Num_Lock')),
'scrolllock': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Scroll_Lock')),
'shiftleft': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Shift_L')),
'shiftright': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Shift_R')),
'ctrlleft': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Control_L')),
'ctrlright': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Control_R')),
'altleft': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Alt_L')),
'altright': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Alt_R')),
# These are added because unlike a-zA-Z0-9, the single characters do not have a
' ': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('space')),
'space': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('space')),
'\t': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Tab')),
'\n': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Return')), # for some reason this needs to be cr, not lf
'\r': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Return')),
'\e': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('Escape')),
'!': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('exclam')),
'#': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('numbersign')),
'%': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('percent')),
'$': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('dollar')),
'&': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('ampersand')),
'"': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('quotedbl')),
"'": _display.keysym_to_keycode(Xlib.XK.string_to_keysym('apostrophe')),
'(': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('parenleft')),
')': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('parenright')),
'*': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('asterisk')),
'=': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('equal')),
'+': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('plus')),
',': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('comma')),
'-': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('minus')),
'.': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('period')),
'/': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('slash')),
':': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('colon')),
';': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('semicolon')),
'<': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('less')),
'>': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('greater')),
'?': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('question')),
'@': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('at')),
'[': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('bracketleft')),
']': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('bracketright')),
'\\': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('backslash')),
'^': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('asciicircum')),
'_': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('underscore')),
'`': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('grave')),
'{': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('braceleft')),
'|': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('bar')),
'}': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('braceright')),
'~': _display.keysym_to_keycode(Xlib.XK.string_to_keysym('asciitilde')),
})
# Trading memory for time" populate winKB so we don't have to call VkKeyScanA each time.
for c in """abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890""":
keyboardMapping[c] = _display.keysym_to_keycode(Xlib.XK.string_to_keysym(c))

View File

@ -1,7 +1,16 @@
# Python imports # Python imports
import pyautogui import subprocess
# Gtk imports # Lib imports
# NOTE: Source: https://github.com/asweigart/pyautogui
# Gunna try importing an env pyautogui; but, If none exist we will use the internal one
# modified to import itself properly for Linux systems.
try:
import pyautogui
print("Found system/env pyautogui instance...")
except Exception as e:
print("Defering to internal pyautogui instance...")
from . import pyautogui
# Application imports # Application imports
@ -14,6 +23,18 @@ pyautogui.PAUSE = 0
class ControlMixin: class ControlMixin:
def get_clipboard_data(self, encoding="utf-8") -> str:
proc = subprocess.Popen(get_clipboard, stdout=subprocess.PIPE)
retcode = proc.wait()
data = proc.stdout.read()
return data.decode(encoding).strip()
def set_clipboard_data(self, data: type, encoding="utf-8") -> None:
proc = subprocess.Popen(set_clipboard, stdin=subprocess.PIPE)
proc.stdin.write(data.encode(encoding))
proc.stdin.close()
retcode = proc.wait()
def type(self, key): def type(self, key):
if self.isCtrlOn or self.isShiftOn or self.isAltOn: if self.isCtrlOn or self.isShiftOn or self.isAltOn:
self.set_hotkeys() self.set_hotkeys()

23
user_config/bin/mouse-keyboard Executable file
View File

@ -0,0 +1,23 @@
#!/bin/bash
# . CONFIG.sh
# set -o xtrace ## To debug scripts
# set -o errexit ## To exit on error
# set -o errunset ## To exit if a variable is referenced but not set
function main() {
call_path=`pwd`
path=""
if [[ ! "${1::1}" == /* ]]; then
path="${call_path}/${1}"
else
path="${1}"
fi
cd "/opt/"
python /opt/mouse-keyboard.zip "$@"
}
main "$@";

View File

@ -0,0 +1,11 @@
[Desktop Entry]
Name=Mouse-Board
GenericName=Mouse Keyboard
Comment=A Python and Gtk+ mouse based keyboard.
Exec=/bin/mouse-keyboard %F
Icon=/usr/share/mouse-keyboard/icons/mouse-keyboard.png
Type=Application
StartupNotify=true
Categories=System;FileTools;Utility;Core
MimeType=
Terminal=false

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB