Added emoji support, refactored, internalized pyautogui
This commit is contained in:
		| @@ -1,6 +1,10 @@ | ||||
| # Mouse_Keyboard | ||||
| # Mouse-Keyboard | ||||
| An onscreen keyboard for the mouse. | ||||
|  | ||||
| ### Requirements | ||||
| * PyGObject | ||||
| * python-xlib | ||||
|  | ||||
| # TODO | ||||
| <li>Get save and execute of custom commands working.</li> | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| # Python imports | ||||
| import builtins, threading | ||||
| import os | ||||
| import builtins | ||||
| import threading | ||||
|  | ||||
| # Lib imports | ||||
|  | ||||
| @@ -24,6 +26,9 @@ def daemon_threaded_wrapper(fn): | ||||
|  | ||||
|  | ||||
|  | ||||
| class MissingConfigError(Exception): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class Pyautogui_Controller(ControlMixin): | ||||
|     def __init__(self): | ||||
| @@ -38,39 +43,69 @@ keys_json = { | ||||
|     "keys": { | ||||
|         "row1": { | ||||
|             "pKeys": ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'], | ||||
|             "sKeys": ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'], | ||||
|             "eKeys": ['🤩', '\U0001F600', '', '', '', '', '', '', '', ''] | ||||
|             "sKeys": ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'] | ||||
|         }, | ||||
|         "row2": { | ||||
|             "pKeys": ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'], | ||||
|             "sKeys": ['\\', '^', '#', '$', '%', '&', '-', '_', '"', '*'], | ||||
|             "eKeys": ['', '', '', '', '', '', '', '', '', ''] | ||||
|             "sKeys": ['\\', '^', '#', '$', '%', '&', '-', '_', '"', '*'] | ||||
|         }, | ||||
|         "row3": { | ||||
|             "pKeys": ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', "'"], | ||||
|             "sKeys": ['/', '|', ':', '=', '+', '', '', '', ';', '!'], | ||||
|             "eKeys": ['', '', '', '', '', '', '', '', '', ''] | ||||
|             "sKeys": ['/', '|', ':', '=', '+', '', '', '', ';', '!'] | ||||
|         }, | ||||
|         "row4": { | ||||
|             "pKeys": ['z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '?'], | ||||
|             "sKeys": ['', '', '<', '>', '[', ']', '(', ')', '{', '}'], | ||||
|             "eKeys": ['', '', '', '', '', '', '', '', '', ''] | ||||
|             "sKeys": ['', '', '<', '>', '[', ']', '(', ')', '{', '}'] | ||||
|         }, | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| # NOTE: Just reminding myself we can add to builtins two different ways... | ||||
| # __builtins__.update({"event_system": Builtins()}) | ||||
| builtins.app_name          = "Mouse Keyboard" | ||||
| builtins.app_name          = "Mouse-Keyboard" | ||||
| builtins.threaded          = threaded_wrapper | ||||
| builtins.daemon_threaded   = daemon_threaded_wrapper | ||||
| builtins.keys_set          = keys_json | ||||
| builtins.trace_debug       = False | ||||
| builtins.debug             = False | ||||
| builtins.app_settings      = None | ||||
| builtins.get_clipboard     = ['xclip','-selection', 'clipboard', '-o'] | ||||
| builtins.set_clipboard     = ['xclip','-selection','clipboard'] | ||||
| builtins.endpoint_registry = EndpointRegistry() | ||||
| builtins.event_system      = EventSystem() | ||||
| 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 | ||||
|   | ||||
| @@ -6,7 +6,7 @@ gi.require_version('Gtk', '3.0') | ||||
| from gi.repository import Gtk | ||||
|  | ||||
| # 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 Ctrl_Key | ||||
| from ..widgets.defined_keys import Shift_Key | ||||
| @@ -26,7 +26,7 @@ class Button_Box(Gtk.ButtonBox): | ||||
|     def __init__(self): | ||||
|         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) | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -38,17 +38,15 @@ class Keys_Column(Gtk.Box): | ||||
|         for child in children: | ||||
|             pKeys = keys[child]["pKeys"] | ||||
|             sKeys = keys[child]["sKeys"] | ||||
|             eKeys = keys[child]["eKeys"] | ||||
|  | ||||
|             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): | ||||
|                     pkey = pKeys[i] | ||||
|                     sKey = sKeys[i] | ||||
|                     eKey = eKeys[i] | ||||
|                     row_box.add(Key(pkey, sKey, eKey)) | ||||
|                     row_box.add(Key(pkey, sKey)) | ||||
|             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()) | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,7 @@ from gi.repository import Gtk | ||||
|  | ||||
| # Application imports | ||||
| 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 | ||||
|  | ||||
|  | ||||
| @@ -21,7 +21,7 @@ class Left_Column(Gtk.Box): | ||||
|  | ||||
|         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.show_all() | ||||
|   | ||||
| @@ -6,12 +6,14 @@ gi.require_version('Gtk', '3.0') | ||||
| from gi.repository import Gtk | ||||
|  | ||||
| # 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 Enter_Key | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class Right_Column(Gtk.Box): | ||||
|     """docstring for Right_Column.""" | ||||
|  | ||||
| @@ -20,7 +22,14 @@ class Right_Column(Gtk.Box): | ||||
|  | ||||
|         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.show_all() | ||||
|   | ||||
| @@ -63,21 +63,25 @@ class Backspace_Key(Key): | ||||
|     def _clicked(self, widget = None): | ||||
|         typwriter.press_special_keys(self.get_label()) | ||||
|  | ||||
| class Emoji_Keys(Key): | ||||
|     def __init__(self): | ||||
|         super(Emoji_Keys, self).__init__("Emoji", "Emoji", iscontrol=True) | ||||
| class Emoji_Key(Key): | ||||
|     def __init__(self, emoji_popover): | ||||
|         super(Emoji_Key, self).__init__("Emoji", "Emoji", iscontrol=True) | ||||
|  | ||||
|         self._ctx           = self.get_style_context() | ||||
|         self._emoji_popover = emoji_popover | ||||
|  | ||||
|     def setup_signals(self): | ||||
|         self.connect("released", self._clicked) | ||||
|  | ||||
|     def _clicked(self, widget = None): | ||||
|         ctx = widget.get_style_context() | ||||
|         ctx.remove_class("toggled_bttn") if ctx.has_class("toggled_bttn") else ctx.add_class("toggled_bttn") | ||||
|         self._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): | ||||
|     def __init__(self): | ||||
|   | ||||
							
								
								
									
										101
									
								
								src/core/widgets/emoji_popover.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/core/widgets/emoji_popover.py
									
									
									
									
									
										Normal 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() | ||||
| @@ -9,13 +9,12 @@ from gi.repository import Gtk | ||||
|  | ||||
|  | ||||
| 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__() | ||||
|  | ||||
|         self.iscontrol         = iscontrol | ||||
|         self._primary_symbol   = primary | ||||
|         self._secondary_symbol = secondary | ||||
|         self._emoji_symbol     = emoji | ||||
|         self._is_upper         = False | ||||
|         self._is_symbol        = False | ||||
|         self._is_emoji         = False | ||||
| @@ -30,13 +29,17 @@ class Key(Gtk.Button or Gtk.ToggleButton): | ||||
|  | ||||
|     def setup_signals(self): | ||||
|         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) | ||||
|  | ||||
|     def _do_type(self, widget = None): | ||||
|         key = self.get_label().strip() | ||||
|         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): | ||||
|         key = self.get_label() | ||||
| @@ -51,18 +54,13 @@ class Key(Gtk.Button or Gtk.ToggleButton): | ||||
|             self._is_symbol = not self._is_symbol | ||||
|             if self._is_symbol: | ||||
|                 self.set_label(self._secondary_symbol) | ||||
|             elif self._is_emoji: | ||||
|                 self.set_label(self._emoji_symbol) | ||||
|             else: | ||||
|                 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. | ||||
|     def toggle_emoji_keys(self, widget = None, eve = None): | ||||
|         if not self.iscontrol: | ||||
|             self._is_emoji = not self._is_emoji | ||||
|             if self._is_emoji: | ||||
|                 self.set_label(self._emoji_symbol) | ||||
|             elif self._is_symbol: | ||||
|             if self._is_symbol: | ||||
|                 self.set_label(self._secondary_symbol) | ||||
|             else: | ||||
|                 self.set_label(self._primary_symbol.upper()) if self._is_upper else self.set_label(self._primary_symbol.lower()) | ||||
|   | ||||
| @@ -16,9 +16,6 @@ from .container import Container | ||||
|  | ||||
|  | ||||
|  | ||||
| class MissingConfigError(Exception): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class Window(SignalsMixin, Gtk.ApplicationWindow): | ||||
|     """docstring for Window.""" | ||||
| @@ -26,22 +23,6 @@ class Window(SignalsMixin, Gtk.ApplicationWindow): | ||||
|     def __init__(self, args, unknownargs): | ||||
|         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_styling() | ||||
|         self.setup_signals() | ||||
| @@ -50,12 +31,11 @@ class Window(SignalsMixin, Gtk.ApplicationWindow): | ||||
|  | ||||
|         self.show_all() | ||||
|  | ||||
|  | ||||
|     def setup_signals(self): | ||||
|         self.connect("delete-event", Gtk.main_quit) | ||||
|  | ||||
|     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_default_size(800, 200) | ||||
|         self.set_keep_above(True) | ||||
| @@ -77,7 +57,7 @@ class Window(SignalsMixin, Gtk.ApplicationWindow): | ||||
|             self.connect("draw", self._area_draw) | ||||
|  | ||||
|         css_provider  = Gtk.CssProvider() | ||||
|         css_provider.load_from_path(self._CSS_FILE) | ||||
|         css_provider.load_from_path(CSS_FILE) | ||||
|         screen        = Gdk.Screen.get_default() | ||||
|         style_context = Gtk.StyleContext() | ||||
|         style_context.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER) | ||||
|   | ||||
							
								
								
									
										2163
									
								
								src/utils/pyautogui/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2163
									
								
								src/utils/pyautogui/__init__.py
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2
									
								
								src/utils/pyautogui/__main__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/utils/pyautogui/__main__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| from . import displayMousePosition | ||||
| displayMousePosition() | ||||
							
								
								
									
										0
									
								
								src/utils/pyautogui/_pyautogui_java.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/utils/pyautogui/_pyautogui_java.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										434
									
								
								src/utils/pyautogui/_pyautogui_osx.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										434
									
								
								src/utils/pyautogui/_pyautogui_osx.py
									
									
									
									
									
										Normal 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. | ||||
							
								
								
									
										568
									
								
								src/utils/pyautogui/_pyautogui_win.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										568
									
								
								src/utils/pyautogui/_pyautogui_win.py
									
									
									
									
									
										Normal 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) | ||||
|  | ||||
							
								
								
									
										301
									
								
								src/utils/pyautogui/_pyautogui_x11.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								src/utils/pyautogui/_pyautogui_x11.py
									
									
									
									
									
										Normal 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)) | ||||
| @@ -1,7 +1,16 @@ | ||||
| # 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 | ||||
|  | ||||
| @@ -14,6 +23,18 @@ pyautogui.PAUSE            = 0 | ||||
|  | ||||
| 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): | ||||
|         if self.isCtrlOn or self.isShiftOn or self.isAltOn: | ||||
|             self.set_hotkeys() | ||||
|   | ||||
							
								
								
									
										23
									
								
								user_config/bin/mouse-keyboard
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										23
									
								
								user_config/bin/mouse-keyboard
									
									
									
									
									
										Executable 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 "$@"; | ||||
							
								
								
									
										11
									
								
								user_config/usr/applications/mouse-keyboard.desktop
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										11
									
								
								user_config/usr/applications/mouse-keyboard.desktop
									
									
									
									
									
										Executable 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
											
										
									
								
							
							
								
								
									
										23215
									
								
								user_config/usr/share/mouse-keyboard/emoji.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23215
									
								
								user_config/usr/share/mouse-keyboard/emoji.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB | 
		Reference in New Issue
	
	Block a user