pax_global_header00006660000000000000000000000064137711330410014512gustar00rootroot0000000000000052 comment=73f4e294c6f74b933aebf566b644500aa854725c python-xdo-0.5/000077500000000000000000000000001377113304100134675ustar00rootroot00000000000000python-xdo-0.5/.gitignore000066400000000000000000000000211377113304100154500ustar00rootroot00000000000000*~ build/* *.pyc python-xdo-0.5/COPYING000066400000000000000000000025321377113304100145240ustar00rootroot00000000000000These python bindings for libxdo are written by Daniel Kahn Gillmor , and are licensed under a BSD-style license (similar to that of libxdo itself): Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. python-xdo-0.5/NEWS000066400000000000000000000002221377113304100141620ustar00rootroot00000000000000version 0.5 -- 2020-12-24 - drop python 2 support - Add mypy-compatible type annotations - fix libc import to work with ctypes from python 3.9 python-xdo-0.5/README000066400000000000000000000023111377113304100143440ustar00rootroot00000000000000xdo module for Python ===================== python-xdo is a module for accessing libxdo from python. It is based on version 3 of the xdo API, which is a nicely-organized API. It won't compile against earlier SONAME versions of libxdo. python-xdo was created as a minimal binding use in a couple dedicated python applications -- as such, it does currently attempt to cover the full libxdo api. It's very minimal (just enough for the job at hand currently), but it doesn't need to remain that way. Patches to improve library coverage are welcome! BUG REPORTS, PATCHES, AND QUESTIONS ----------------------------------- Discussions about libxdo and python-xdo are welcome at xdotool-users@googlegroups.com (you may need to subscribe to post). CREDITS ------- libxdo itself comes from Jordan Sissel: http://www.semicomplete.com/projects/xdotool/ https://github.com/jordansissel/xdotool The python-xdo binding was initially written by Daniel Kahn Gillmor The overhaul to ctypes was inspired by discussion with (and large parts of the ctypes code originated from) Samuele Santi , and additional implementation work was done by Cyril Brulebois . python-xdo-0.5/setup.py000066400000000000000000000024621377113304100152050ustar00rootroot00000000000000from setuptools import find_packages, setup setup (name = 'xdo', version = '0.5', author = 'Daniel Kahn Gillmor', author_email = 'dkg@fifthhorseman.net', license = 'BSD', packages=find_packages(), description = 'simulate X11 keyboard/mouse input (bindings for libxdo)', classifiers=[ "License :: OSI Approved :: BSD License", # 2-clause # "Development Status :: 1 - Planning", # "Development Status :: 2 - Pre-Alpha", # "Development Status :: 3 - Alpha", "Development Status :: 4 - Beta", # "Development Status :: 5 - Production/Stable", # "Development Status :: 6 - Mature", # "Development Status :: 7 - Inactive", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", # Only CPython is supported at the moment "Programming Language :: Python :: Implementation :: CPython", # "Programming Language :: Python :: Implementation :: PyPy", ], package_data={'': ['README', 'COPYING']}) python-xdo-0.5/xdo/000077500000000000000000000000001377113304100142615ustar00rootroot00000000000000python-xdo-0.5/xdo/__init__.py000066400000000000000000000171711377113304100164010ustar00rootroot00000000000000# -*- coding: utf-8 -*- import ctypes import os from ._xdo import libxdo as _libxdo from ._xdo import libc as _libc from ._xdo import charcodemap_ptr as _charcodemap_ptr from ._xdo import window_t as _window_t from ._xdo import CURRENTWINDOW from datetime import timedelta from typing import Any, Callable, TypeVar, cast, Optional, Union import warnings F = TypeVar('F', bound=Callable[..., Any]) def deprecated(func: F) -> F: '''This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted when the function is used.''' def new_func(*args: Any, **kwargs: Any) -> Any: warnings.warn("Call to deprecated function {}.".format(func.__name__), category=DeprecationWarning) return func(*args, **kwargs) new_func.__name__ = func.__name__ new_func.__doc__ = func.__doc__ new_func.__dict__.update(func.__dict__) return cast(F, new_func) def version() -> str: return _libxdo.xdo_version().decode('utf-8') class xdo(object): def __init__(self, display: Optional[str]=None) -> None: if display is None: display = os.environ.get('DISPLAY') self._xdo = _libxdo.xdo_new(None if display is None else display.encode('utf-8')) if self._xdo is None: raise SystemError("Could not initialize libxdo") def enter_text_window(self, string:Union[str,bytes], clearmodifiers: bool=True, delay: Union[int, timedelta]=timedelta(microseconds=12000), window: int=CURRENTWINDOW) -> None: """ Type a string to the specified window. If you want to send a specific key or key sequence, such as "alt+l", you want instead the ``send_keysequence_window(...)`` function. :param string: The string to type, like "Hello world!" :param delay: The delay between keystrokes as a datetime.timedelta. default: 12 milliseconds. If passed as an int, it will be treated as microseconds :param window: The window you want to send keystrokes to or (by default) xdo.CURRENTWINDOW :param clearmodifiers: Whether to clear any current modifier keys before sending the text (defaults to True). """ if isinstance(delay, timedelta): delay_int = int(delay.total_seconds() * 1000000) elif isinstance(delay, int): delay_int = delay else: raise TypeError("delay parameter should be either a timedelta or an int") if isinstance(string, str): # FIXME: is it right to assume that we're going to encode # in UTF-8? if the sender wants to emit a bytestring, # they can just send it as a bytestring in the first # place. string = string.encode('utf-8') if clearmodifiers: active_mods_n = ctypes.c_int(0) active_mods = _charcodemap_ptr() _libxdo.xdo_get_active_modifiers(self._xdo, ctypes.byref(active_mods), ctypes.byref(active_mods_n)) _libxdo.xdo_clear_active_modifiers(self._xdo, window, active_mods, active_mods_n) ret = _libxdo.xdo_enter_text_window(self._xdo, window, string, delay_int) if clearmodifiers: _libxdo.xdo_set_active_modifiers(self._xdo, window, active_mods, active_mods_n) _libc.free(active_mods) return ret def send_keysequence_window(self, keysequence: Union[str,bytes], clearmodifiers: bool=True, delay: Union[timedelta, int]=timedelta(microseconds=12000), window: int=CURRENTWINDOW) -> None: """ Send a keysequence to the specified window. This allows you to send keysequences by symbol name. Any combination of X11 KeySym names separated by '+' are valid. Single KeySym names are valid, too. Examples: "l" "semicolon" "alt+Return" "Alt_L+Tab" If you want to type a string, such as "Hello world." you want to instead use xdo_enter_text_window. :param window: The window you want to send the keysequence to or CURRENTWINDOW :param keysequence: The string keysequence to send. :param delay: The delay between keystrokes as a datetime.timedelta. default: 12 milliseconds. If passed as an int, it will be treated as microseconds :param clearmodifiers: Whether to clear any current modifier keys before sending the keysequence (defaults to True). """ if isinstance(delay, timedelta): delay_int = int(delay.total_seconds() * 1000000) elif isinstance(delay, int): delay_int = delay else: raise TypeError("delay parameter should be either a timedelta or an int") if isinstance(keysequence, str): # FIXME: is it right to assume that we're going to encode # in UTF-8? if the sender wants to send a keysequence as # a bytestring, they can just send it as a bytestring in # the first place. keysequence = keysequence.encode('utf-8') if clearmodifiers: active_mods_n = ctypes.c_int(0) active_mods = _charcodemap_ptr() _libxdo.xdo_get_active_modifiers(self._xdo, ctypes.byref(active_mods), ctypes.byref(active_mods_n)) _libxdo.xdo_clear_active_modifiers(self._xdo, window, active_mods, active_mods_n) ret = _libxdo.xdo_send_keysequence_window(self._xdo, window, keysequence, delay_int) if clearmodifiers: _libxdo.xdo_set_active_modifiers(self._xdo, window, active_mods, active_mods_n) _libc.free(active_mods) return ret @deprecated def type(self, string: Union[str,bytes], clearmodifiers: bool=True, delay: Union[int, timedelta]=12000, window: int=CURRENTWINDOW) -> None: """ Please use enter_text_window() instead of type()! note that the delay parameter for enter_text_window expects a datetime.timedelta """ if isinstance(delay, int): delay = timedelta(microseconds=delay) return self.enter_text_window(string, clearmodifiers=clearmodifiers, delay=delay, window=window) def focus_window(self, window: int=CURRENTWINDOW) -> None: """ Focus a window. :param wid: the window to focus. """ return _libxdo.xdo_focus_window(self._xdo, window) def get_focused_window(self) -> int: """ Get the window currently having focus. :returns window: identifier for the currently-focused window """ window_ret = _window_t(0) _libxdo.xdo_get_focused_window(self._xdo, ctypes.byref(window_ret)) return window_ret.value def wait_for_window_focus(self, window: int, want_focus: bool=True) -> None: """ Wait for a window to have or lose focus. :param window: The window to wait on :param want_focus: If True, wait for focus. If False, wait for loss of focus. (default: True) """ return _libxdo.xdo_wait_for_window_focus(self._xdo, window, 1 if want_focus else 0) python-xdo-0.5/xdo/_xdo.py000066400000000000000000000171701377113304100155720ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Ctypes bindings for libxdo """ import ctypes from ctypes import ( POINTER, c_char_p, c_int, c_ulong, c_void_p) from ctypes.util import find_library libxdo = ctypes.CDLL(find_library("xdo")) libc = ctypes.CDLL(ctypes.util.find_library('c')) libc.free.argtypes = (c_void_p,) libc.free.restype = None libc.free.__doc__ = """\ free data allocated from malloc (this is useful for disposing of the results of libxdo.xdo_get_active_modifiers) """ XDO_ERROR = 1 XDO_SUCCESS = 0 # Window type is just defined as ``unsigned long`` window_t = c_ulong useconds_t = c_ulong class XdoException(Exception): pass def _errcheck(result, func, arguments): """ Error checker for functions returning an integer indicating success (0) / failure (1). Raises a XdoException in case of error, otherwise just returns ``None`` (returning the original code, 0, would be useless anyways..) """ if result != 0: raise XdoException( 'Function {0} returned error code {1}' .format(func.__name__, result)) return None # be explicit :) # CURRENTWINDOW is a special identifier for xdo input faking (mouse and # keyboard) functions like xdo_send_keysequence_window that indicate # we should target the current window, not a specific window. # # Generally, this means we will use XTEST instead of XSendEvent when sending # events. CURRENTWINDOW = 0 # all the types are opaque types: xdo_ptr = c_void_p charcodemap_ptr = c_void_p # ============================================================================ # xdo_t* xdo_new(const char *display); libxdo.xdo_new.argtypes = (c_char_p,) libxdo.xdo_new.restype = xdo_ptr libxdo.xdo_new.__doc__ = """\ Create a new xdo_ptr instance. :param display: the string display name, such as ":0". If null, uses the environment variable DISPLAY just like XOpenDisplay(NULL). :return: Pointer to a new xdo_ptr or NULL on failure """ # ============================================================================ # const char *xdo_version(void); libxdo.xdo_version.argtypes = () libxdo.xdo_version.restype = c_char_p libxdo.xdo_version.__doc__ = """\ Return a string representing the version of this library """ # ============================================================================ # void xdo_free(xdo_t *xdo); libxdo.xdo_free.argtypes = (xdo_ptr,) libxdo.xdo_free.__doc__ = """\ Free and destroy an xdo_ptr instance. If close_display_when_freed is set, then we will also close the Display. """ # ============================================================================ # int xdo_enter_text_window(const xdo_t *xdo, Window window, # const char *string, useconds_t delay); libxdo.xdo_enter_text_window.argtypes = ( xdo_ptr, window_t, c_char_p, useconds_t) libxdo.xdo_enter_text_window.restype = c_int libxdo.xdo_enter_text_window.errcheck = _errcheck libxdo.xdo_enter_text_window.__doc__ = """ Type a string to the specified window. If you want to send a specific key or key sequence, such as "alt+l", you want instead xdo_send_keysequence_window(...). :param window: The window you want to send keystrokes to or CURRENTWINDOW :param string: The string to type, like "Hello world!" :param delay: The delay between keystrokes in microseconds. 12000 is a decent choice if you don't have other plans. """ # ============================================================================ # int xdo_send_keysequence_window(const xdo_t *xdo, Window window, # const char *keysequence, useconds_t delay); libxdo.xdo_send_keysequence_window.argtypes = ( xdo_ptr, window_t, c_char_p, useconds_t) libxdo.xdo_send_keysequence_window.restype = c_int libxdo.xdo_send_keysequence_window.errcheck = _errcheck libxdo.xdo_send_keysequence_window.__doc__ = """ Send a keysequence to the specified window. This allows you to send keysequences by symbol name. Any combination of X11 KeySym names separated by '+' are valid. Single KeySym names are valid, too. Examples: "l" "semicolon" "alt+Return" "Alt_L+Tab" If you want to type a string, such as "Hello world." you want to instead use xdo_enter_text_window. :param window: The window you want to send the keysequence to or CURRENTWINDOW :param keysequence: The string keysequence to send. :param delay: The delay between keystrokes in microseconds. """ # ============================================================================ # int xdo_focus_window(const xdo_t *xdo, Window wid); libxdo.xdo_focus_window.argtypes = (xdo_ptr, window_t) libxdo.xdo_focus_window.restype = c_int libxdo.xdo_focus_window.errcheck = _errcheck libxdo.xdo_focus_window.__doc__ = """\ Focus a window. :param window: the window to focus. """ # ============================================================================ # int xdo_get_focused_window(const xdo_t *xdo, Window *window_ret); libxdo.xdo_get_focused_window.argtypes = (xdo_ptr, POINTER(window_t)) libxdo.xdo_get_focused_window.restype = c_int libxdo.xdo_get_focused_window.errcheck = _errcheck libxdo.xdo_get_focused_window.__doc__ = """\ Get the window currently having focus. :param window_ret: Pointer to a window where the currently-focused window will be stored. """ # ============================================================================ # int xdo_wait_for_window_focus(const xdo_t *xdo, Window window, # int want_focus); libxdo.xdo_wait_for_window_focus.argtypes = ( xdo_ptr, window_t, c_int) libxdo.xdo_wait_for_window_focus.restype = c_int libxdo.xdo_wait_for_window_focus.errcheck = _errcheck libxdo.xdo_wait_for_window_focus.__doc__ = """\ Wait for a window to have or lose focus. :param window: The window to wait on :param want_focus: If 1, wait for focus. If 0, wait for loss of focus. """ # ============================================================================ # int xdo_get_active_modifiers(const xdo_t *xdo, charcodemap_t **keys, # int *nkeys); libxdo.xdo_get_active_modifiers.argtypes = ( xdo_ptr, POINTER(charcodemap_ptr), POINTER(c_int)) libxdo.xdo_get_active_modifiers.restype = c_int libxdo.xdo_get_active_modifiers.errcheck = _errcheck libxdo.xdo_get_active_modifiers.__doc__ = """\ Get a list of active keys. :param keys: Pointer to the array of charcodemap_t that will be allocated by this function. :param nkeys: Pointer to integer where the number of keys will be stored. The returned object must be freed. """ # ============================================================================ # int xdo_clear_active_modifiers(const xdo_t *xdo, Window window, # charcodemap_t *active_mods, # int active_mods_n); libxdo.xdo_clear_active_modifiers.argtypes = ( xdo_ptr, window_t, charcodemap_ptr, c_int) libxdo.xdo_clear_active_modifiers.restype = c_int libxdo.xdo_clear_active_modifiers.errcheck = _errcheck libxdo.xdo_clear_active_modifiers.__doc__ = """\ Send any events necesary to clear the the active modifiers. For example, if you are holding 'alt' when xdo_get_active_modifiers is called, then this method will send a key-up for 'alt' """ # ============================================================================ # int xdo_set_active_modifiers(const xdo_t *xdo, Window window, # charcodemap_t *active_mods, # int active_mods_n); libxdo.xdo_set_active_modifiers.argtypes = ( xdo_ptr, window_t, charcodemap_ptr, c_int) libxdo.xdo_set_active_modifiers.restype = c_int libxdo.xdo_set_active_modifiers.errcheck = _errcheck libxdo.xdo_set_active_modifiers.__doc__ = """\ Send any events necessary to make these modifiers active. This is useful if you just cleared the active modifiers and then wish to restore them after. """