pydirectinput

Partial implementation of DirectInput function calls to simulate mouse and keyboard inputs.

   1"""
   2Partial implementation of DirectInput function calls to simulate
   3mouse and keyboard inputs.
   4"""
   5
   6from __future__ import annotations
   7
   8# native imports
   9import functools
  10import inspect
  11import os
  12import sys
  13import time
  14from collections.abc import Generator
  15from collections.abc import Sequence
  16from contextlib import contextmanager
  17from ctypes import POINTER
  18from ctypes import Array
  19from ctypes import LibraryLoader
  20from ctypes import Structure
  21from ctypes import Union
  22from ctypes import WinDLL
  23from ctypes import pointer
  24from ctypes import sizeof
  25from ctypes.wintypes import BOOL
  26from ctypes.wintypes import DWORD
  27from ctypes.wintypes import HMONITOR
  28from ctypes.wintypes import INT
  29from ctypes.wintypes import LONG
  30from ctypes.wintypes import LPCVOID
  31from ctypes.wintypes import PULONG
  32from ctypes.wintypes import UINT
  33from ctypes.wintypes import ULONG
  34from ctypes.wintypes import WORD
  35from math import ceil
  36from math import floor
  37from math import log10
  38from struct import unpack
  39from threading import Lock
  40
  41
  42# Windows-only
  43if sys.platform != "win32":
  44    raise ImportError(
  45        "This module makes Windows API calls and is thereby only available "
  46        "on that plattform!"
  47    )
  48
  49# Python 3.7 or higher
  50if sys.version_info >= (3, 7):
  51    # native imports
  52    from typing import TYPE_CHECKING
  53    from typing import Any
  54    from typing import Callable
  55    from typing import TypeVar
  56    from typing import cast as hint_cast  # prevent confusion with ctypes.cast
  57    from typing import overload
  58else:
  59    raise ImportError(
  60        "This module is strictly typed and can't be used in Python <3.7!"
  61    )
  62
  63# Python 3.8 or higher
  64if sys.version_info >= (3, 8):
  65    # native imports
  66    from typing import ClassVar
  67    from typing import Final
  68    from typing import Literal
  69    from typing import Protocol
  70else:
  71    # pip imports
  72    from typing_extensions import ClassVar
  73    from typing_extensions import Final
  74    from typing_extensions import Literal
  75    from typing_extensions import Protocol
  76
  77# Python 3.9 or higher
  78if sys.version_info >= (3, 9):
  79    List = list
  80else:
  81    # native imports
  82    from typing import List
  83
  84# Python 3.10 or higher
  85if sys.version_info >= (3, 10):
  86    # native imports
  87    from typing import ParamSpec
  88    from typing import TypeAlias
  89else:
  90    # pip imports
  91    from typing_extensions import ParamSpec
  92    from typing_extensions import TypeAlias
  93
  94# # Python 3.11 or higher
  95# if sys.version_info >= (3, 11):
  96#     pass
  97# else:
  98#     pass
  99
 100# ------------------------------------------------------------------------------
 101# https://github.com/python/mypy/issues/7540#issuecomment-845741357
 102if TYPE_CHECKING:
 103    # We have to get the private Pointer type from typeshed to make
 104    # the type checker shut up about the "incompatible types" error.
 105    # native imports
 106    from ctypes import _Pointer  # pyright: ignore[reportPrivateUsage]
 107
 108    _POINTER_TYPE = _Pointer
 109else:
 110
 111    class __pointer:
 112        """
 113        Create pointer proxy class that translates square bracket notation
 114        __pointer[type] into POINTER(type).
 115        """
 116
 117        @classmethod
 118        def __class_getitem__(cls, item):
 119            return POINTER(item)
 120
 121    _POINTER_TYPE = __pointer
 122# ------------------------------------------------------------------------------
 123
 124
 125# ==============================================================================
 126# ===== Internal WinDLL instance ===============================================
 127# ==============================================================================
 128# Importing the cached WinDLL instance from ctypes directly would cause
 129# conflicts with other packages that also use ctypes (notably pyautogui).
 130# Therefore we create our own WinDLL instance and use that instead.
 131windll: LibraryLoader[WinDLL] = LibraryLoader(WinDLL)
 132
 133
 134# ==============================================================================
 135# ===== Internal time source ===================================================
 136# ==============================================================================
 137_time: Callable[[], float] = time.perf_counter
 138_time_ns: Callable[[], int] = time.perf_counter_ns
 139_sleep: Callable[[float], None] = time.sleep
 140
 141
 142# ==============================================================================
 143# ===== External "constants" ===================================================
 144# ==============================================================================
 145
 146# ----- "Constants" for failsafe check and pause -------------------------------
 147# Intendend to be modified by callers
 148FAILSAFE: bool = True
 149"""
 150Stop execution if mouse is moved into one of `FAILSAFE_POINTS`.
 151Change to disable failsafe behaviour.
 152"""
 153FAILSAFE_POINTS: list[tuple[int, int]] = [(0, 0)]
 154"""
 155List of coordinates that trigger failafe exception. (default: top left corner)
 156"""
 157PAUSE: float | None = 0.01  # 1/100 second pause by default
 158"""
 159Default pause interval in seconds if _pause argument isn't set to False.
 1601/100 second pause by default.
 161
 162Set to None to disable automatic pauses entirely.
 163"""
 164MINIMUM_SLEEP_IDEAL: float = 1e-6
 165"""
 166Extremely small timer interval greater than 0 that still causes the system to
 167sleep. This is the ideal value, the system may not be able to sleep for this
 168short of a time. See `MINIMUM_SLEEP_ACTUAL` and `calibrate_real_sleep_minimum`.
 169"""
 170MINIMUM_SLEEP_ACTUAL: float = 0.002
 171"""
 172Actual time spent on sleeping with MINIMUM_SLEEP_IDEAL, rounded up for safety.
 173Determined ahead of time by `calibrate_real_sleep_minimum`. The `MINIMUM_SLEEP_`
 174values may differ between systems. If you're unsure, run the calibration
 175and correct this value after importing the module.
 176"""
 177# ------------------------------------------------------------------------------
 178
 179
 180# ----- Constants for the mouse button names -----------------------------------
 181MOUSE_LEFT: str = "left"
 182"""Name of left mouse button"""
 183MOUSE_MIDDLE: str = "middle"
 184"""Name of middle mouse button"""
 185MOUSE_RIGHT: str = "right"
 186"""Name of right mouse button"""
 187MOUSE_PRIMARY: str = "primary"
 188"""Name of primary mouse button (left mouse button unless swapped)"""
 189MOUSE_SECONDARY: str = "secondary"
 190"""Name of secondary mouse button (right mouse button unless swapped)"""
 191MOUSE_BUTTON4: str = "mouse4"
 192"""Name of first additional mouse button (usually a side button)"""
 193MOUSE_X1: str = "x1"
 194"""Name of first additional mouse button (usually a side button)"""
 195MOUSE_BUTTON5: str = "mouse5"
 196"""Name of second additional mouse button (usually a side button)"""
 197MOUSE_X2: str = "x2"
 198"""Name of second additional mouse button (usually a side button)"""
 199# ------------------------------------------------------------------------------
 200
 201
 202# ==============================================================================
 203# ===== External setup functions ===============================================
 204# ==============================================================================
 205
 206
 207# ----- automatically measure minimum sleep time -------------------------------
 208def calibrate_real_sleep_minimum(
 209    runs: int = 10, *, verbose: bool = False
 210) -> None:
 211    """
 212    Measure your system's minimum sleep duration and calibrate
 213    `MINIMUM_SLEEP_ACTUAL` accordingly.
 214
 215    Will try to sleep for `MINIMUM_SLEEP_IDEAL` seconds and measure actual time
 216    difference. Repeat for `runs` amount of times, take the highest measurement
 217    and round it up to the next higher value in the same order of magnitude.
 218
 219    Example: [0.001874, 0.001721, 0.001806] would round up to 0.002
 220    """
 221
 222    def round_up_same_magnitude(x: float) -> float:
 223        mag: float = 10 ** floor(log10(x))
 224        return ceil(x / mag) * mag
 225
 226    def stopwatch(duration: float) -> float:
 227        t1: int = _time_ns()
 228        _sleep(duration)
 229        t2: int = _time_ns()
 230        return (t2 - t1) * 1e-9
 231
 232    if verbose:
 233        print("Calibrating real sleep minimum...")
 234
 235    measurements = [stopwatch(MINIMUM_SLEEP_IDEAL) for _ in range(runs)]
 236    if verbose:
 237        print(f"Real measurements: {measurements}")
 238
 239    new_sleep_minimum = round_up_same_magnitude(max(measurements))
 240    if verbose:
 241        print(
 242            "Rounding max measurement to next higher value in same order of "
 243            f"magnitude: {new_sleep_minimum}"
 244        )
 245
 246    global MINIMUM_SLEEP_ACTUAL
 247    if verbose:
 248        print(
 249            f"Changing MINIMUM_SLEEP_ACTUAL from {MINIMUM_SLEEP_ACTUAL} to "
 250            f"{new_sleep_minimum}"
 251        )
 252    MINIMUM_SLEEP_ACTUAL = (  # pyright: ignore[reportConstantRedefinition]
 253        new_sleep_minimum
 254    )
 255    # --------------------------------------------------------------------------
 256
 257
 258# ==============================================================================
 259# ===== Internal constants =====================================================
 260# ==============================================================================
 261
 262
 263# ----- INPUT.type constants ---------------------------------------------------
 264_INPUT_MOUSE: Final = 0x0000  # c_ulong(0x0000)
 265"""The event is a mouse event. Use the mi structure of the union."""
 266_INPUT_KEYBOARD: Final = 0x0001  # c_ulong(0x0001)
 267"""The event is a keyboard event. Use the ki structure of the union."""
 268_INPUT_HARDWARE: Final = 0x0002  # c_ulong(0x0002)
 269"""The event is a hardware event. Use the hi structure of the union."""
 270# ------------------------------------------------------------------------------
 271
 272
 273# ----- MOUSEINPUT.mouseData constants -----------------------------------------
 274_XBUTTON1: Final = 0x0001  # c_ulong(0x0001)
 275"""Set if the first X button is pressed or released."""
 276_XBUTTON2: Final = 0x0002  # c_ulong(0x0002)
 277"""Set if the second X button is pressed or released."""
 278# ------------------------------------------------------------------------------
 279
 280
 281# ----- MOUSEINPUT.dwFlags constants -------------------------------------------
 282_MOUSEEVENTF_MOVE: Final = 0x0001  # c_ulong(0x0001)
 283"""Movement occurred."""
 284
 285_MOUSEEVENTF_LEFTDOWN: Final = 0x0002  # c_ulong(0x0002)
 286"""The left button was pressed."""
 287_MOUSEEVENTF_LEFTUP: Final = 0x0004  # c_ulong(0x0004)
 288"""The left button was released."""
 289_MOUSEEVENTF_LEFTCLICK: Final = (
 290    _MOUSEEVENTF_LEFTDOWN + _MOUSEEVENTF_LEFTUP  # c_ulong(0x0006)
 291)
 292"""Combined event: Left button was clicked."""
 293
 294_MOUSEEVENTF_RIGHTDOWN: Final = 0x0008  # c_ulong(0x0008)
 295"""The right button was pressed."""
 296_MOUSEEVENTF_RIGHTUP: Final = 0x0010  # c_ulong(0x0010)
 297"""The right button was released."""
 298_MOUSEEVENTF_RIGHTCLICK: Final = (
 299    _MOUSEEVENTF_RIGHTDOWN + _MOUSEEVENTF_RIGHTUP  # c_ulong(0x0018)
 300)
 301"""Combined event: Right button was clicked."""
 302
 303_MOUSEEVENTF_MIDDLEDOWN: Final = 0x0020  # c_ulong(0x0020)
 304"""The middle button was pressed."""
 305_MOUSEEVENTF_MIDDLEUP: Final = 0x0040  # c_ulong(0x0040)
 306"""The middle button was released."""
 307_MOUSEEVENTF_MIDDLECLICK: Final = (
 308    _MOUSEEVENTF_MIDDLEDOWN + _MOUSEEVENTF_MIDDLEUP  # c_ulong(0x0060)
 309)
 310"""Combined event: Middle button was clicked."""
 311
 312_MOUSEEVENTF_XDOWN: Final = 0x0080  # c_ulong(0x0080)
 313"""An X button was pressed."""
 314_MOUSEEVENTF_XUP: Final = 0x0100  # c_ulong(0x0100)
 315"""An X button was released."""
 316_MOUSEEVENTF_XCLICK: Final = (
 317    _MOUSEEVENTF_XDOWN + _MOUSEEVENTF_XUP  # c_ulong(0x0180)
 318)
 319"""Combined event: Side button was clicked."""
 320
 321_MOUSEEVENTF_WHEEL: Final = 0x0800  # c_ulong(0x0800)
 322"""
 323The wheel was moved, if the mouse has a wheel.
 324The amount of movement is specified in mouseData.
 325"""
 326_MOUSEEVENTF_HWHEEL: Final = 0x1000  # c_ulong(0x1000)
 327"""
 328The wheel was moved horizontally, if the mouse has a wheel. The amount of
 329movement is specified in mouseData.
 330Windows XP/2000: This value is not supported.
 331"""
 332
 333_MOUSEEVENTF_MOVE_NOCOALESCE: Final = 0x2000  # c_ulong(0x2000)
 334"""
 335The WM_MOUSEMOVE messages will not be coalesced. The default behavior is to
 336coalesce WM_MOUSEMOVE messages.
 337Windows XP/2000: This value is not supported.
 338"""
 339_MOUSEEVENTF_VIRTUALDESK: Final = 0x4000  # c_ulong(0x4000)
 340"""
 341Maps coordinates to the entire desktop. Must be used with MOUSEEVENTF_ABSOLUTE.
 342"""
 343_MOUSEEVENTF_ABSOLUTE: Final = 0x8000  # c_ulong(0x8000)
 344"""
 345The dx and dy members contain normalized absolute coordinates. If the flag is
 346not set, dx and dy contain relative data (the change in position since the last
 347reported position). This flag can be set, or not set, regardless of what kind of
 348mouse or other pointing device, if any, is connected to the system. For further
 349information about relative mouse motion, see the following Remarks section.
 350"""
 351# ------------------------------------------------------------------------------
 352
 353
 354# ----- MOUSEINPUT Remarks -----------------------------------------------------
 355"""
 356https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput#remarks
 357
 358----- Remarks -----
 359
 360If the mouse has moved, indicated by MOUSEEVENTF_MOVE, dx and dy specify
 361information about that movement. The information is specified as absolute or
 362relative integer values.
 363
 364If MOUSEEVENTF_ABSOLUTE value is specified, dx and dy contain normalized
 365absolute coordinates between 0 and 65,535. The event procedure maps these
 366coordinates onto the display surface. Coordinate (0,0) maps onto the upper-left
 367corner of the display surface; coordinate (65535,65535) maps onto the
 368lower-right corner. In a multimonitor system, the coordinates map to the
 369primary monitor.
 370
 371If MOUSEEVENTF_VIRTUALDESK is specified, the coordinates map to the entire
 372virtual desktop.
 373
 374If the MOUSEEVENTF_ABSOLUTE value is not specified, dx and dy specify movement
 375relative to the previous mouse event (the last reported position). Positive
 376values mean the mouse moved right (or down); negative values mean the mouse
 377moved left (or up).
 378
 379Relative mouse motion is subject to the effects of the mouse speed and the
 380two-mouse threshold values. A user sets these three values with the
 381Pointer Speed slider of the Control Panel's Mouse Properties sheet. You can
 382obtain and set these values using the SystemParametersInfo[1] function.
 383
 384The system applies two tests to the specified relative mouse movement. If the
 385specified distance along either the x or y axis is greater than the first
 386mouse threshold value, and the mouse speed is not zero, the system doubles the
 387distance. If the specified distance along either the x or y axis is greater
 388than the second mouse threshold value, and the mouse speed is equal to two,
 389the system doubles the distance that resulted from applying the first
 390threshold test. It is thus possible for the system to multiply specified
 391relative mouse movement along the x or y axis by up to four times.
 392
 393[1] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa
 394"""
 395# ------------------------------------------------------------------------------
 396
 397
 398# ----- Scrolling distance -----------------------------------------------------
 399_WHEEL_DELTA: Final = 120
 400"""
 401The delta was set to 120 to allow Microsoft or other vendors to build
 402finer-resolution wheels (a freely-rotating wheel with no notches) to send more
 403messages per rotation, but with a smaller value in each message.
 404
 405https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-mousewheel
 406"""
 407# ------------------------------------------------------------------------------
 408
 409
 410# ----- KEYBDINPUT.dwFlags Flags ------------------------------------------------
 411_KEYEVENTF_EXTENDEDKEY: Final = 0x0001  # c_ulong(0x0001)
 412"""
 413If specified, the scan code was preceded by a prefix byte that has the value
 4140xE0 (224).
 415"""
 416_KEYEVENTF_KEYUP: Final = 0x0002  # c_ulong(0x0002)
 417"""
 418If specified, the key is being released. If not specified, the key is being
 419pressed.
 420"""
 421_KEYEVENTF_UNICODE: Final = 0x0004  # c_ulong(0x0004)
 422"""
 423If specified, the system synthesizes a VK_PACKET keystroke. The wVk parameter
 424must be zero. This flag can only be combined with the KEYEVENTF_KEYUP flag.
 425For more information, see the Remarks section.
 426"""
 427_KEYEVENTF_SCANCODE: Final = 0x0008  # c_ulong(0x0008)
 428"""If specified, wScan identifies the key and wVk is ignored."""
 429# ------------------------------------------------------------------------------
 430
 431
 432# ----- KEYBDINPUT Remarks -----------------------------------------------------
 433"""
 434https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput#remarks
 435
 436----- Remarks -----
 437
 438INPUT_KEYBOARD supports nonkeyboard-input methods—such as handwriting
 439recognition or voice recognition—as if it were text input by using the
 440KEYEVENTF_UNICODE flag. If KEYEVENTF_UNICODE is specified, SendInput sends
 441a WM_KEYDOWN or WM_KEYUP message to the foreground thread's message queue with
 442wParam equal to VK_PACKET. Once GetMessage or PeekMessage obtains this message,
 443passing the message to TranslateMessage posts a WM_CHAR message with the
 444Unicode character originally specified by wScan. This Unicode character will
 445automatically be converted to the appropriate ANSI value if it is posted to
 446an ANSI window.
 447
 448Set the KEYEVENTF_SCANCODE flag to define keyboard input in terms of the scan
 449code. This is useful for simulating a physical keystroke regardless of which
 450keyboard is currently being used. You can also pass the KEYEVENTF_EXTENDEDKEY
 451flag if the scan code is an extended key. The virtual key value of a key can
 452change depending on the current keyboard layout or what other keys were pressed,
 453but the scan code will always be the same.
 454"""
 455# ------------------------------------------------------------------------------
 456
 457
 458# ----- MapVirtualKey Map Types ------------------------------------------------
 459_MAPVK_VK_TO_VSC: Final = 0  # c_unit(0)
 460"""
 461The uCode parameter is a virtual-key code and is translated into a scan code.
 462If it is a virtual-key code that does not distinguish between left- and
 463right-hand keys, the left-hand scan code is returned.
 464If there is no translation, the function returns 0.
 465"""
 466_MAPVK_VSC_TO_VK: Final = 1  # c_unit(1)
 467"""
 468The uCode parameter is a scan code and is translated into a virtual-key code
 469that does not distinguish between left- and right-hand keys.
 470If there is no translation, the function returns 0.
 471"""
 472_MAPVK_VK_TO_CHAR: Final = 2  # c_unit(2)
 473"""
 474The uCode parameter is a virtual-key code and is translated into an unshifted
 475character value in the low order word of the return value. Dead keys
 476(diacritics) are indicated by setting the top bit of the return value.
 477If there is no translation, the function returns 0.
 478"""
 479_MAPVK_VSC_TO_VK_EX: Final = 3  # c_unit(3)
 480"""
 481The uCode parameter is a scan code and is translated into a virtual-key code
 482that distinguishes between left- and right-hand keys.
 483If there is no translation, the function returns 0.
 484"""
 485_MAPVK_VK_TO_VSC_EX: Final = 4  # c_unit(4)
 486"""
 487Windows Vista and later: The uCode parameter is a virtual-key code and is
 488translated into a scan code. If it is a virtual-key code that does not
 489distinguish between left- and right-hand keys, the left-hand scan code is
 490returned. If the scan code is an extended scan code, the high byte of the uCode
 491value can contain either 0xe0 or 0xe1 to specify the extended scan code.
 492If there is no translation, the function returns 0.
 493"""
 494# ------------------------------------------------------------------------------
 495
 496
 497# ----- GetSystemMetrics nIndex arguments --------------------------------------
 498_SM_CXSCREEN: Final = 0
 499"""
 500The width of the screen of the primary display monitor, in pixels. This is the
 501same value obtained by calling GetDeviceCaps[1] as follows:
 502`GetDeviceCaps(hdcPrimaryMonitor, HORZRES)`.
 503
 504[1] https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getdevicecaps
 505"""
 506_SM_CYSCREEN: Final = 1
 507"""
 508The height of the screen of the primary display monitor, in pixels. This is
 509the same value obtained by calling GetDeviceCaps[1] as follows:
 510`GetDeviceCaps(hdcPrimaryMonitor, VERTRES)`.
 511
 512[1] https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getdevicecaps
 513"""
 514_SM_SWAPBUTTON: Final = 23
 515"""
 516Nonzero if the meanings of the left and right mouse buttons are swapped;
 517otherwise, 0.
 518"""
 519_SM_XVIRTUALSCREEN: Final = 76
 520"""
 521The coordinates for the left side of the virtual screen. The virtual screen is
 522the bounding rectangle of all display monitors. The SM_CXVIRTUALSCREEN metric
 523is the width of the virtual screen.
 524"""
 525_SM_YVIRTUALSCREEN: Final = 77
 526"""
 527The coordinates for the top of the virtual screen. The virtual screen is the
 528bounding rectangle of all display monitors. The SM_CYVIRTUALSCREEN metric is
 529the height of the virtual screen.
 530"""
 531_SM_CXVIRTUALSCREEN: Final = 78
 532"""
 533The width of the virtual screen, in pixels. The virtual screen is the bounding
 534rectangle of all display monitors. The SM_XVIRTUALSCREEN metric is the
 535coordinates for the left side of the virtual screen.
 536"""
 537_SM_CYVIRTUALSCREEN: Final = 79
 538"""
 539The height of the virtual screen, in pixels. The virtual screen is the bounding
 540rectangle of all display monitors. The SM_YVIRTUALSCREEN metric is the
 541coordinates for the top of the virtual screen.
 542"""
 543# ------------------------------------------------------------------------------
 544
 545
 546# ----- SystemParametersInfoW uiAction arguments -------------------------------
 547_SPI_GETMOUSE: Final = 0x0003  # c_uint
 548"""
 549Retrieves the two mouse threshold values and the mouse acceleration. The
 550pvParam parameter must point to an array of three integers that receives these
 551values. See mouse_event[1] for further information.
 552
 553https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event
 554"""
 555_SPI_SETMOUSE: Final = 0x0004  # c_uint
 556"""
 557Sets the two mouse threshold values and the mouse acceleration. The pvParam
 558parameter must point to an array of three integers that specifies these values.
 559See mouse_event[1] for further information.
 560
 561https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event
 562"""
 563_SPI_GETMOUSESPEED: Final = 0x0070  # c_uint
 564"""
 565Retrieves the current mouse speed. The mouse speed determines how far the
 566pointer will move based on the distance the mouse moves. The pvParam parameter
 567must point to an integer that receives a value which ranges between 1 (slowest)
 568and 20 (fastest). A value of 10 is the default. The value can be set by an
 569end-user using the mouse control panel application or by an application using
 570SPI_SETMOUSESPEED.
 571"""
 572_SPI_SETMOUSESPEED: Final = 0x0071  # c_uint
 573"""
 574Sets the current mouse speed. The pvParam parameter is an integer between
 5751 (slowest) and 20 (fastest). A value of 10 is the default. This value is
 576typically set using the mouse control panel application.
 577"""
 578# ------------------------------------------------------------------------------
 579
 580
 581# ----- MOUSEEVENTF Index constants --------------------------------------------
 582_MOUSE_PRESS: Final = 0
 583_MOUSE_RELEASE: Final = 1
 584_MOUSE_CLICK: Final = 2
 585# ------------------------------------------------------------------------------
 586
 587
 588# ----- MOUSEEVENTF Lookup dicts -----------------------------------------------
 589_MOUSEEVENTF_LEFT: tuple[int, int, int] = (
 590    _MOUSEEVENTF_LEFTDOWN,
 591    _MOUSEEVENTF_LEFTUP,
 592    _MOUSEEVENTF_LEFTCLICK,
 593)
 594_MOUSEEVENTF_MIDDLE: tuple[int, int, int] = (
 595    _MOUSEEVENTF_MIDDLEDOWN,
 596    _MOUSEEVENTF_MIDDLEUP,
 597    _MOUSEEVENTF_MIDDLECLICK,
 598)
 599_MOUSEEVENTF_RIGHT: tuple[int, int, int] = (
 600    _MOUSEEVENTF_RIGHTDOWN,
 601    _MOUSEEVENTF_RIGHTUP,
 602    _MOUSEEVENTF_RIGHTCLICK,
 603)
 604_MOUSEEVENTF_X: tuple[int, int, int] = (
 605    _MOUSEEVENTF_XDOWN,
 606    _MOUSEEVENTF_XUP,
 607    _MOUSEEVENTF_XCLICK,
 608)
 609_MOUSE_MAPPING_EVENTF: dict[str, tuple[int, int, int]] = {}
 610_MOUSE_MAPPING_DATA: dict[str, int] = {}
 611
 612
 613def update_MOUSEEVENT_mappings() -> None:
 614    """
 615    Update the MOUSEEVENT mappings if you change the name of the button name
 616    constants.
 617
 618    This function MUST be called every time one of the `MOUSE_*` constants
 619    has been changed!
 620    """
 621    _MOUSE_MAPPING_EVENTF.update(
 622        {
 623            MOUSE_LEFT: _MOUSEEVENTF_LEFT,
 624            MOUSE_MIDDLE: _MOUSEEVENTF_MIDDLE,
 625            MOUSE_RIGHT: _MOUSEEVENTF_RIGHT,
 626            MOUSE_BUTTON4: _MOUSEEVENTF_X,
 627            MOUSE_X1: _MOUSEEVENTF_X,
 628            MOUSE_BUTTON5: _MOUSEEVENTF_X,
 629            MOUSE_X2: _MOUSEEVENTF_X,
 630        }
 631    )
 632    _MOUSE_MAPPING_DATA.update(
 633        {
 634            MOUSE_LEFT: 0,
 635            MOUSE_MIDDLE: 0,
 636            MOUSE_RIGHT: 0,
 637            MOUSE_BUTTON4: _XBUTTON1,
 638            MOUSE_X1: _XBUTTON1,
 639            MOUSE_BUTTON5: _XBUTTON2,
 640            MOUSE_X2: _XBUTTON2,
 641        }
 642    )
 643
 644
 645update_MOUSEEVENT_mappings()  # call the function on import to set mappings.
 646# ------------------------------------------------------------------------------
 647
 648
 649# ----- MonitorFromPoint dwFlags constants -------------------------------------
 650_MONITOR_DEFAULTTONULL: Final = 0x00000000  # c_ulong
 651"""Returns NULL."""
 652_MONITOR_DEFAULTTOPRIMARY: Final = 0x00000001  # c_ulong
 653"""Returns a handle to the primary display monitor."""
 654_MONITOR_DEFAULTTONEAREST: Final = 0x00000002  # c_ulong
 655"""Returns a handle to the display monitor that is nearest to the point."""
 656# ------------------------------------------------------------------------------
 657
 658
 659# ==============================================================================
 660# ===== C struct redefinitions =================================================
 661# ==============================================================================
 662
 663
 664# ----- MOUSEINPUT -------------------------------------------------------------
 665class _MOUSEINPUT(Structure):
 666    """
 667    MOUSEINPUT structure (winuser.h)
 668
 669    Contains information about a simulated mouse event.
 670
 671    https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput
 672    """
 673
 674    # Python side type hinting
 675    dx: int  # LONG
 676    """
 677    The absolute position of the mouse, or the amount of motion since the last
 678    mouse event was generated, depending on the value of the dwFlags member.
 679    Absolute data is specified as the x coordinate of the mouse; relative data
 680    is specified as the number of pixels moved.
 681    """
 682    dy: int  # LONG
 683    """
 684    The absolute position of the mouse, or the amount of motion since the last
 685    mouse event was generated, depending on the value of the dwFlags member.
 686    Absolute data is specified as the y coordinate of the mouse; relative data
 687    is specified as the number of pixels moved.
 688    """
 689    mouseData: int  # DWORD
 690    """
 691    If dwFlags contains MOUSEEVENTF_WHEEL, then mouseData specifies the amount
 692    of wheel movement. A positive value indicates that the wheel was rotated
 693    forward, away from the user; a negative value indicates that the wheel was
 694    rotated backward, toward the user. One wheel click is defined as
 695    WHEEL_DELTA, which is 120.
 696
 697    Windows Vista: If dwFlags contains MOUSEEVENTF_HWHEEL, then dwData
 698    specifies the amount of wheel movement. A positive value indicates that
 699    the wheel was rotated to the right; a negative value indicates that the
 700    wheel was rotated to the left. One wheel click is defined as WHEEL_DELTA,
 701    which is 120.
 702
 703    If dwFlags does not contain MOUSEEVENTF_WHEEL, MOUSEEVENTF_XDOWN, or
 704    MOUSEEVENTF_XUP, then mouseData should be zero.
 705
 706    If dwFlags contains MOUSEEVENTF_XDOWN or MOUSEEVENTF_XUP, then mouseData
 707    specifies which X buttons were pressed or released. This value may be any
 708    combination of the following flags. (See _XBUTTON* constants)
 709    """
 710    dwFlags: int  # DWORD
 711    """
 712    A set of bit flags that specify various aspects of mouse motion and button
 713    clicks. The bits in this member can be any reasonable combination of the
 714    following values.
 715
 716    The bit flags that specify mouse button status are set to indicate changes
 717    in status, not ongoing conditions. For example, if the left mouse button is
 718    pressed and held down, MOUSEEVENTF_LEFTDOWN is set when the left button is
 719    first pressed, but not for subsequent motions. Similarly MOUSEEVENTF_LEFTUP
 720    is set only when the button is first released.
 721
 722    You cannot specify both the MOUSEEVENTF_WHEEL flag and either
 723    MOUSEEVENTF_XDOWN or MOUSEEVENTF_XUP flags simultaneously in the dwFlags
 724    parameter, because they both require use of the mouseData field.
 725    (See _MOUSEEVENTF_* constants)
 726    """
 727    time: int  # DWORD
 728    """
 729    The time stamp for the event, in milliseconds. If this parameter is 0, the
 730    system will provide its own time stamp.
 731    """
 732    dwExtraInfo: ULONG  # ULONG_PTR
 733    """
 734    An additional value associated with the keystroke. Use the
 735    GetMessageExtraInfo[1] function to obtain this information.
 736
 737    [1] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessageextrainfo
 738    """
 739    # ctypes side struct definition
 740    _fields_ = [
 741        ("dx", LONG),
 742        ("dy", LONG),
 743        ("mouseData", DWORD),
 744        ("dwFlags", DWORD),
 745        ("time", DWORD),
 746        ("dwExtraInfo", PULONG),
 747    ]
 748    # --------------------------------------------------------------------------
 749
 750
 751# ----- KEYBDINPUT -------------------------------------------------------------
 752class _KEYBDINPUT(Structure):
 753    """
 754    KEYBDINPUT structure (winuser.h)
 755
 756    Contains information about a simulated keyboard event.
 757
 758    https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput
 759    """
 760
 761    # Python side type hinting
 762    wVk: int  # WORD
 763    """
 764    A virtual-key code. The code must be a value in the range 1 to 254. If the
 765    dwFlags member specifies KEYEVENTF_UNICODE, wVk must be 0.
 766    """
 767    wScan: int  # WORD
 768    """
 769    A hardware scan code for the key. If dwFlags specifies KEYEVENTF_UNICODE,
 770    wScan specifies a Unicode character which is to be sent to the foreground
 771    application.
 772    """
 773    dwFlags: int  # DWORD
 774    """
 775    Specifies various aspects of a keystroke. This member can be certain
 776    combinations of the following values. (See _KEYEVENTF_* constants)
 777    """
 778    time: int  # DWORD
 779    """
 780    The time stamp for the event, in milliseconds. If this parameter is zero,
 781    the system will provide its own time stamp.
 782    """
 783    dwExtraInfo: ULONG  # ULONG_PTR
 784    """
 785    An additional value associated with the keystroke. Use the
 786    GetMessageExtraInfo[1] function to obtain this information.
 787
 788    [1] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessageextrainfo
 789    """
 790    # ctypes side struct definition
 791    _fields_ = [
 792        ("wVk", WORD),
 793        ("wScan", WORD),
 794        ("dwFlags", DWORD),
 795        ("time", DWORD),
 796        ("dwExtraInfo", PULONG),
 797    ]
 798    # --------------------------------------------------------------------------
 799
 800
 801# ----- HARDWAREINPUT ----------------------------------------------------------
 802class _HARDWAREINPUT(Structure):
 803    """
 804    HARDWAREINPUT structure (winuser.h)
 805
 806    Contains information about a simulated message generated by an input
 807    device other than a keyboard or mouse.
 808
 809    https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-hardwareinput
 810    """
 811
 812    # Python side type hinting
 813    uMsg: int  # DWORD
 814    """The message generated by the input hardware."""
 815    wParamL: int  # WORD
 816    """The low-order word of the lParam parameter for uMsg."""
 817    wParamH: int  # WORD
 818    """The high-order word of the lParam parameter for uMsg."""
 819    # ctypes side struct definition
 820    _fields_ = [
 821        ("uMsg", DWORD),
 822        ("wParamL", WORD),
 823        ("wParamH", WORD),
 824    ]
 825    # --------------------------------------------------------------------------
 826
 827
 828# ----- POINT ------------------------------------------------------------------
 829class _POINT(Structure):
 830    """
 831    POINT structure
 832
 833    The POINT structure defines the x- and y- coordinates of a point.
 834
 835    https://docs.microsoft.com/en-us/previous-versions/dd162805(v=vs.85)
 836    """
 837
 838    # Python side type hinting
 839    x: int  # LONG
 840    """The x-coordinate of the point."""
 841    y: int  # LONG
 842    """The y-coordinate of the point."""
 843    # ctypes side struct definition
 844    _fields_ = [("x", LONG), ("y", LONG)]
 845    # --------------------------------------------------------------------------
 846
 847
 848# ----- INPUT ------------------------------------------------------------------
 849class _INPUT_UNION(Union):
 850    """
 851    https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input
 852    """
 853
 854    # Python side type hinting
 855    mi: _MOUSEINPUT
 856    ki: _KEYBDINPUT
 857    hi: _HARDWAREINPUT
 858    # ctypes side struct definition
 859    _fields_ = [
 860        ("mi", _MOUSEINPUT),
 861        ("ki", _KEYBDINPUT),
 862        ("hi", _HARDWAREINPUT),
 863    ]
 864
 865
 866class _INPUT(Structure):
 867    """
 868    INPUT structure (winuser.h)
 869
 870    Used by SendInput to store information for synthesizing input events such
 871    as keystrokes, mouse movement, and mouse clicks.
 872
 873    https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input
 874    """
 875
 876    # Python side type hinting
 877    type: Literal[0, 1, 2]  # DWORD
 878    """
 879    The type of the input event. This member can be one of the following
 880    values. (See _INPUT_* constants)
 881    """
 882    ii: _INPUT_UNION
 883    mi: _MOUSEINPUT  # part of _INPUT_UNION ii
 884    """The information about a simulated mouse event."""
 885    ki: _KEYBDINPUT  # part of _INPUT_UNION ii
 886    """The information about a simulated keyboard event."""
 887    hi: _HARDWAREINPUT  # part of _INPUT_UNION ii
 888    """The information about a simulated hardware event."""
 889    # ctypes side struct definition
 890    _anonymous_ = ("ii",)
 891    _fields_ = [
 892        ("type", DWORD),
 893        ("ii", _INPUT_UNION),
 894    ]
 895    # --------------------------------------------------------------------------
 896
 897
 898# ==============================================================================
 899# ===== C struct factory functions =============================================
 900# ==============================================================================
 901
 902
 903# ----- _create_mouse_input ----------------------------------------------------
 904def _create_mouse_input(
 905    dx: int = 0,  # c_long
 906    dy: int = 0,  # c_long
 907    mouseData: int = 0,  # c_ulong
 908    dwFlags: int = 0,  # c_ulong
 909    time: int = 0,  # c_ulong
 910) -> _INPUT:
 911    """Create INPUT structure for mouse input"""
 912    dwExtraInfo: ULONG = ULONG(0)
 913    input_struct: _INPUT = _INPUT(_INPUT_MOUSE)
 914    input_struct.mi = _MOUSEINPUT(
 915        dx, dy, mouseData, dwFlags, time, pointer(dwExtraInfo)
 916    )
 917    return input_struct
 918    # --------------------------------------------------------------------------
 919
 920
 921# ----- _create_keyboard_input -------------------------------------------------
 922def _create_keyboard_input(
 923    wVk: int = 0,  # c_ushort
 924    wScan: int = 0,  # c_ushort
 925    dwFlags: int = 0,  # c_ulong
 926    time: int = 0,  # c_ulong
 927) -> _INPUT:
 928    """Create INPUT structure for keyboard input"""
 929    dwExtraInfo: ULONG = ULONG(0)
 930    input_struct: _INPUT = _INPUT(_INPUT_KEYBOARD)
 931    input_struct.ki = _KEYBDINPUT(
 932        wVk, wScan, dwFlags, time, pointer(dwExtraInfo)
 933    )
 934    return input_struct
 935    # --------------------------------------------------------------------------
 936
 937
 938# ----- _create_hardware_input -------------------------------------------------
 939def _create_hardware_input(  # pyright: ignore[reportUnusedFunction]
 940    uMsg: int = 0,  # c_ulong
 941    wParamL: int = 0,  # c_short
 942    wParamH: int = 0,  # c_ushort
 943) -> _INPUT:
 944    """Create INPUT structure for hardware input"""
 945    input_struct: _INPUT = _INPUT(_INPUT_HARDWARE)
 946    input_struct.hi = _HARDWAREINPUT(uMsg, wParamL, wParamH)
 947    return input_struct
 948    # --------------------------------------------------------------------------
 949
 950
 951# ==============================================================================
 952# ==== User32 functions ========================================================
 953# ==============================================================================
 954
 955# ----- user32.dll -------------------------------------------------------------
 956_user32: WinDLL = windll.user32
 957# ------------------------------------------------------------------------------
 958
 959
 960# ----- SendInput Declaration --------------------------------------------------
 961class _SendInputType(Protocol):
 962    argtypes: tuple[type[UINT], type[_POINTER_TYPE[_INPUT]], type[INT]]
 963    restype: type[UINT]
 964
 965    def __call__(
 966        self,
 967        cInputs: UINT | int,
 968        pInputs: _POINTER_TYPE[_INPUT] | _INPUT | Array[_INPUT],
 969        cbSize: INT | int,
 970    ) -> int:  # UINT
 971        ...
 972
 973
 974_SendInput: _SendInputType = hint_cast(_SendInputType, _user32.SendInput)
 975"""
 976----- SendInput function (winuser.h) -----
 977
 978Synthesizes keystrokes, mouse motions, and button clicks.
 979
 980https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput
 981
 982----- Parameters -----
 983
 984[in] cInputs
 985
 986Type: UINT
 987
 988The number of structures in the pInputs array.
 989
 990[in] pInputs
 991
 992Type: LPINPUT
 993
 994An array of INPUT structures. Each structure represents an event to be
 995inserted into the keyboard or mouse input stream.
 996
 997[in] cbSize
 998
 999Type: int
1000
1001The size, in bytes, of an INPUT structure. If cbSize is not the size of an
1002INPUT structure, the function fails.
1003
1004----- Return value -----
1005
1006Type: UINT
1007
1008The function returns the number of events that it successfully inserted into
1009the keyboard or mouse input stream. If the function returns zero, the input
1010was already blocked by another thread. To get extended error information, call
1011GetLastError.
1012
1013This function fails when it is blocked by UIPI. Note that neither GetLastError
1014nor the return value will indicate the failure was caused by UIPI blocking.
1015
1016----- Remarks -----
1017
1018This function is subject to UIPI. Applications are permitted to inject input
1019only into applications that are at an equal or lesser integrity level.
1020
1021The SendInput function inserts the events in the INPUT structures serially
1022into the keyboard or mouse input stream. These events are not interspersed
1023with other keyboard or mouse input events inserted either by the user (with
1024the keyboard or mouse) or by calls to keybd_event, mouse_event, or other
1025calls to SendInput.
1026
1027This function does not reset the keyboard's current state. Any keys that are
1028already pressed when the function is called might interfere with the events
1029that this function generates. To avoid this problem, check the keyboard's
1030state with the GetAsyncKeyState function and correct as necessary.
1031
1032Because the touch keyboard uses the surrogate macros defined in winnls.h to
1033send input to the system, a listener on the keyboard event hook must decode
1034input originating from the touch keyboard. For more information, see Surrogates
1035and Supplementary Characters.
1036
1037An accessibility application can use SendInput to inject keystrokes
1038corresponding to application launch shortcut keys that are handled by the
1039shell.
1040This functionality is not guaranteed to work for other types of applications.
1041"""
1042_SendInput.argtypes = UINT, POINTER(_INPUT), INT
1043_SendInput.restype = UINT
1044
1045
1046def _send_input(
1047    inputs: _INPUT | Sequence[_INPUT],
1048) -> int:
1049    """
1050    Abstraction layer over SendInput (winuser.h)
1051
1052    See https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput
1053    """
1054    # prepare arguments
1055    cInputs: UINT
1056    inputs_array: Array[_INPUT]
1057    if isinstance(inputs, _INPUT):
1058        # -> single element array
1059        cInputs = UINT(1)
1060        inputs_array = (_INPUT * 1)(inputs)
1061    else:
1062        cInputs = UINT(len(inputs))
1063        inputs_array = (_INPUT * len(inputs))(*inputs)
1064    cbSize: INT = INT(sizeof(_INPUT))
1065    # execute function
1066    # inputs_array will be automatically be referenced by pointer
1067    return _SendInput(cInputs, inputs_array, cbSize)
1068    # --------------------------------------------------------------------------
1069
1070
1071# ----- MapVirtualKeyW Declaration ---------------------------------------------
1072class _MapVirtualKeyWType(Protocol):
1073    argtypes: tuple[type[UINT], type[UINT]]
1074    restype: type[UINT]
1075
1076    def __call__(
1077        self,
1078        uCode: UINT | int,
1079        uMapType: UINT | int,
1080    ) -> int:  # UINT
1081        ...
1082
1083
1084_MapVirtualKeyW: _MapVirtualKeyWType = hint_cast(
1085    _MapVirtualKeyWType, _user32.MapVirtualKeyW
1086)
1087"""
1088----- MapVirtualKeyW function (winuser.h) -----
1089
1090Translates (maps) a virtual-key code into a scan code or character value, or
1091translates a scan code into a virtual-key code.
1092
1093To specify a handle to the keyboard layout to use for translating the specified
1094code, use the MapVirtualKeyEx function.
1095
1096https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mapvirtualkeyw
1097
1098----- Parameters -----
1099
1100[in] uCode
1101
1102Type: UINT
1103
1104The virtual key code[1] or scan code for a key. How this value is interpreted
1105depends on the value of the uMapType parameter.
1106
1107Starting with Windows Vista, the high byte of the uCode value can contain
1108either 0xe0 or 0xe1 to specify the extended scan code.
1109
1110[in] uMapType
1111
1112Type: UINT
1113
1114The translation to be performed. The value of this parameter depends on the
1115value of the uCode parameter. (See _MAPVK_VK_TO_* constants)
1116
1117----- Return value -----
1118
1119Type: UINT
1120
1121The return value is either a scan code, a virtual-key code, or a character
1122value, depending on the value of uCode and uMapType. If there is no
1123translation, the return value is zero.
1124
1125----- Remarks -----
1126
1127An application can use MapVirtualKey to translate scan codes to the
1128virtual-key code constants VK_SHIFT, VK_CONTROL, and VK_MENU, and vice versa.
1129These translations do not distinguish between the left and right instances
1130of the SHIFT, CTRL, or ALT keys.
1131
1132An application can get the scan code corresponding to the left or right
1133instance of one of these keys by calling MapVirtualKey with uCode set to
1134one of the following virtual-key code constants:
1135
1136    VK_LSHIFT
1137    VK_RSHIFT
1138    VK_LCONTROL
1139    VK_RCONTROL
1140    VK_LMENU
1141    VK_RMENU
1142
1143These left- and right-distinguishing constants are available to an application
1144only through the GetKeyboardState[2], SetKeyboardState[3], GetAsyncKeyState[4],
1145GetKeyState, MapVirtualKey, and MapVirtualKeyEx functions. For list complete
1146table of virtual key codes, see Virtual Key Codes[1].
1147
1148[1] https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
1149
1150[2] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeyboardstate
1151
1152[3] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setkeyboardstate
1153
1154[4] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getasynckeystate
1155
1156[5] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeystate
1157"""
1158_MapVirtualKeyW.argtypes = UINT, UINT
1159_MapVirtualKeyW.restype = UINT
1160
1161
1162def _map_virtual_key(
1163    uCode: int,
1164    uMapType: Literal[0, 1, 2, 3, 4],  # See _MAPVK_* constants
1165) -> int:
1166    """
1167    Abstraction layer over MapVirtualKeyW (winuser.h)
1168
1169    Accepted values for uMapType are:
1170    - _MAPVK_VK_TO_VSC = 0
1171    - _MAPVK_VSC_TO_VK = 1
1172    - _MAPVK_VK_TO_CHAR = 2
1173    - _MAPVK_VSC_TO_VK_EX = 3
1174    - _MAPVK_VK_TO_VSC_EX = 4
1175
1176    See https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mapvirtualkeyw
1177    """
1178    return _MapVirtualKeyW(UINT(uCode), UINT(uMapType))
1179    # --------------------------------------------------------------------------
1180
1181
1182# ----- GetSystemMetrics Declaration -------------------------------------------
1183class _GetSystemMetricsType(Protocol):
1184    argtypes: tuple[type[INT]]
1185    restype: type[INT]
1186
1187    def __call__(
1188        self,
1189        nIndex: INT | int,
1190    ) -> int:  # c_int
1191        ...
1192
1193
1194_GetSystemMetrics: _GetSystemMetricsType = hint_cast(
1195    _GetSystemMetricsType, _user32.GetSystemMetrics
1196)
1197"""
1198----- GetSystemMetrics function (winuser.h) -----
1199
1200Retrieves the specified system metric or system configuration setting.
1201
1202Note that all dimensions retrieved by GetSystemMetrics are in pixels.
1203
1204https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetrics
1205
1206----- Parameters -----
1207
1208[in] nIndex
1209
1210Type: int
1211
1212The system metric or configuration setting to be retrieved. This parameter can
1213be one of the following values. Note that all SM_CX* values are widths and all
1214SM_CY* values are heights. Also note that all settings designed to return
1215Boolean data represent TRUE as any nonzero value, and FALSE as a zero value.
1216(See _SM_* constants)
1217
1218----- Return value -----
1219
1220Type: int
1221
1222If the function succeeds, the return value is the requested system metric or
1223configuration setting.
1224
1225If the function fails, the return value is 0. GetLastError does not provide
1226extended error information.
1227
1228----- Remarks -----
1229
1230System metrics can vary from display to display.
1231
1232GetSystemMetrics(SM_CMONITORS) counts only visible display monitors. This is
1233different from EnumDisplayMonitors[1], which enumerates both visible display
1234monitors and invisible pseudo-monitors that are associated with mirroring
1235drivers. An invisible pseudo-monitor is associated with a pseudo-device used
1236to mirror application drawing for remoting or other purposes.
1237
1238This API is not DPI aware, and should not be used if the calling thread is
1239per-monitor DPI aware. For the DPI-aware version of this API, see
1240GetSystemMetricsForDPI[2]. For more information on DPI awareness, see the
1241Windows High DPI documentation[3].
1242
1243[1] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors
1244
1245[2] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetricsfordpi
1246
1247[3] https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows
1248"""  # noqa (URL too long)
1249_GetSystemMetrics.argtypes = (INT,)
1250_GetSystemMetrics.restype = INT
1251
1252
1253def _get_system_metrics(nIndex: int) -> int:
1254    """
1255    Abstraction layer over GetSystemMetrics (winuser.h)
1256
1257    See the _SM_* constants for accepted values for the nIndex argument.
1258
1259    See https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetrics
1260    """
1261    return _GetSystemMetrics(nIndex)
1262    # --------------------------------------------------------------------------
1263
1264
1265# ----- MonitorFromPoint Declaration -----------------------------------------------
1266class _MonitorFromPointType(Protocol):
1267    argtypes: tuple[type[_POINT], type[DWORD]]
1268    restype: type[HMONITOR]
1269
1270    def __call__(
1271        self,
1272        pt: _POINT,
1273        dwFlags: DWORD | int,
1274    ) -> int | None:  # HMONITOR
1275        ...
1276
1277
1278_MonitorFromPoint: _MonitorFromPointType = hint_cast(
1279    _MonitorFromPointType, _user32.MonitorFromPoint
1280)
1281"""
1282----- MonitorFromPoint function (winuser.h) -----
1283
1284Retrieves the position of the mouse cursor, in screen coordinates.
1285
1286https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfrompoint
1287
1288----- Parameters -----
1289
1290[in] pt
1291
1292Type: POINT
1293
1294A POINT structure that specifies the point of interest in
1295virtual-screen coordinates.
1296
1297[in] dwFlags
1298
1299Type: DWORD
1300
1301Determines the function's return value if the point is not contained within
1302any display monitor.
1303
1304This parameter can be one of the following values.
1305
1306MONITOR_DEFAULTTONULL      0x00000000   Returns NULL.
1307MONITOR_DEFAULTTOPRIMARY   0x00000001   Returns a handle to the primary
1308                                        display monitor.
1309MONITOR_DEFAULTTONEAREST   0x00000002   Returns a handle to the display monitor
1310                                        that is nearest to the point.
1311
1312----- Return value -----
1313
1314If the point is contained by a display monitor, the return value is an
1315HMONITOR handle to that display monitor.
1316
1317If the point is not contained by a display monitor, the return value depends
1318on the value of dwFlags.
1319"""
1320_MonitorFromPoint.argtypes = (_POINT, DWORD)
1321_MonitorFromPoint.restype = HMONITOR
1322
1323
1324def _monitor_from_point(x: int, y: int) -> int | None:
1325    """
1326    Abstraction layer over MonitorFromPoint (winuser.h)
1327
1328    See https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfrompoint
1329    """
1330    pt = _POINT()
1331    pt.x, pt.y = x, y
1332    return _MonitorFromPoint(pt, _MONITOR_DEFAULTTONULL)
1333    # --------------------------------------------------------------------------
1334
1335
1336# ----- GetCursorPos Declaration -----------------------------------------------
1337class _GetCursorPosType(Protocol):
1338    argtypes: tuple[type[_POINTER_TYPE[_POINT]]]
1339    restype: type[BOOL]
1340
1341    def __call__(
1342        self,
1343        lpPoint: _POINTER_TYPE[_POINT] | _POINT,
1344    ) -> bool:  # BOOL
1345        ...
1346
1347
1348_GetCursorPos: _GetCursorPosType = hint_cast(
1349    _GetCursorPosType, _user32.GetCursorPos
1350)
1351"""
1352----- GetCursorPos function (winuser.h) -----
1353
1354Retrieves the position of the mouse cursor, in screen coordinates.
1355
1356https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorpos
1357
1358----- Parameters -----
1359
1360[out] lpPoint
1361
1362Type: LPPOINT
1363
1364A pointer to a POINT structure that receives the screen coordinates of the
1365cursor.
1366
1367----- Return value -----
1368
1369Type: BOOL
1370
1371Returns nonzero if successful or zero otherwise. To get extended error
1372information, call GetLastError.
1373
1374----- Remarks -----
1375
1376The cursor position is always specified in screen coordinates and is not
1377affected by the mapping mode of the window that contains the cursor.
1378
1379The calling process must have WINSTA_READATTRIBUTES access to the window
1380station.
1381
1382The input desktop must be the current desktop when you call GetCursorPos. Call
1383OpenInputDesktop[1] to determine whether the current desktop is the input
1384desktop. If it is not, call SetThreadDesktop[2] with the HDESK returned by
1385OpenInputDesktop to switch to that desktop.
1386
1387[1] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-openinputdesktop
1388
1389[2] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setthreaddesktop
1390"""
1391_GetCursorPos.argtypes = (POINTER(_POINT),)
1392_GetCursorPos.restype = BOOL
1393
1394
1395def _get_cursor_pos() -> _POINT:
1396    """
1397    Abstraction layer over GetCursorPos (winuser.h)
1398
1399    See https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorpos
1400    """
1401    cursor = _POINT()
1402    # cursor will be automatically be referenced by pointer
1403    _GetCursorPos(cursor)
1404    return cursor
1405    # --------------------------------------------------------------------------
1406
1407
1408# ----- SystemParametersInfoW Declaration --------------------------------------
1409class _SystemParametersInfoW_Type(Protocol):
1410    argtypes: tuple[type[UINT], type[UINT], type[LPCVOID], type[UINT]]
1411    restype: type[BOOL]
1412
1413    def __call__(
1414        self,
1415        uiAction: UINT | int,
1416        uiParam: UINT | int,
1417        pvParam: LPCVOID | Any,
1418        fWinIni: UINT | int,
1419    ) -> bool:  # BOOL
1420        ...
1421
1422
1423_SystemParametersInfoW: _SystemParametersInfoW_Type = hint_cast(
1424    _SystemParametersInfoW_Type, _user32.SystemParametersInfoW
1425)
1426"""
1427----- SystemParametersInfoW function (winuser.h) -----
1428
1429Retrieves or sets the value of one of the system-wide parameters. This
1430function can also update the user profile while setting a parameter.
1431
1432https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow
1433
1434"""
1435_SystemParametersInfoW.argtypes = UINT, UINT, LPCVOID, UINT
1436_SystemParametersInfoW.restype = BOOL
1437
1438
1439# ----- Get system settings for mouse movement ---------------------------------
1440def _get_mouse_parameters() -> tuple[int, int, int]:
1441    """
1442    Query system parameters for user's mouse settings.
1443
1444    Abstraction layer over SystemParametersInfoW (winuser.h)
1445
1446    https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow
1447
1448    Information on _SPI_GETMOUSE
1449
1450    Retrieves the two mouse threshold values and the mouse acceleration. The
1451    pvParam parameter must point to an array of three integers that receives
1452    these values. See mouse_event[1] for further information.
1453
1454    https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event
1455    """
1456    pvParam: Array[UINT] = (UINT * 3)()
1457    _SystemParametersInfoW(_SPI_GETMOUSE, 0, pointer(pvParam), 0)
1458    return (pvParam[0], pvParam[1], pvParam[2])
1459    # --------------------------------------------------------------------------
1460
1461
1462# ----- Set system settings for mouse movement ---------------------------------
1463def _set_mouse_parameters(
1464    threshold1: int, threshold2: int, enhanced_pointer_precision: int
1465) -> bool:
1466    """
1467    Set system parameters for user's mouse settings.
1468
1469    Abstraction layer over SystemParametersInfoW (winuser.h)
1470
1471    https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow
1472
1473    Information on _SPI_SETMOUSE
1474
1475    Sets the two mouse threshold values and the mouse acceleration.
1476    The pvParam parameter must point to an array of three integers that
1477    specifies these values.
1478    See mouse_event[1] for further information.
1479
1480    https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event
1481    """
1482    pvParam: Final[Array[UINT]] = (UINT * 3)(
1483        threshold1, threshold2, enhanced_pointer_precision
1484    )
1485    # leave last parameter as 0 to make changes non-permanent and restore
1486    # themselves upon reboot if something goes wrong and the wrong setting
1487    # was overwritten.
1488    return _SystemParametersInfoW(_SPI_SETMOUSE, 0, pointer(pvParam), 0)
1489    # --------------------------------------------------------------------------
1490
1491
1492# ----- Get system settings for mouse speed ------------------------------------
1493def _get_mouse_speed() -> int:
1494    """
1495    Query system parameters for user's mouse settings.
1496
1497    Abstraction layer over SystemParametersInfoW (winuser.h)
1498
1499    https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow
1500
1501    Information on SPI_GETMOUSESPEED
1502
1503    Retrieves the current mouse speed. The mouse speed determines how far the
1504    pointer will move based on the distance the mouse moves. The pvParam
1505    parameter must point to an integer that receives a value which ranges
1506    between 1 (slowest) and 20 (fastest). A value of 10 is the default. The
1507    value can be set by an end-user using the mouse control panel application
1508    or by an application using SPI_SETMOUSESPEED.
1509    """
1510    pvParam: Array[UINT] = (UINT * 1)()
1511    _SystemParametersInfoW(_SPI_GETMOUSESPEED, 0, pointer(pvParam), 0)
1512    return pvParam[0]
1513    # --------------------------------------------------------------------------
1514
1515
1516# ----- Set system settings for mouse movement ---------------------------------
1517def _set_mouse_speed(mouse_speed: int) -> bool:
1518    """
1519    Set system parameters for user's mouse settings.
1520
1521    Abstraction layer over SystemParametersInfoW (winuser.h)
1522
1523    https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow
1524
1525    Information on SPI_SETMOUSESPEED
1526
1527    Sets the current mouse speed. The pvParam parameter is an integer between
1528    1 (slowest) and 20 (fastest). A value of 10 is the default. This value is
1529    typically set using the mouse control panel application.
1530    """
1531    pvParam: Final[Array[UINT]] = (UINT * 1)(mouse_speed)
1532    # leave last parameter as 0 to make changes non-permanent and restore
1533    # themselves upon reboot if something goes wrong and the wrong setting
1534    # was overwritten.
1535    return _SystemParametersInfoW(_SPI_SETMOUSESPEED, 0, pointer(pvParam), 0)
1536    # --------------------------------------------------------------------------
1537
1538
1539# ==============================================================================
1540# ===== Keyboard Scan Code Mappings ============================================
1541# ==============================================================================
1542
1543
1544# ----- Special class for key extended scancode sequences ----------------------
1545class ScancodeSequence(List[int]):
1546    """
1547    A special class with the sole purpose of representing extended scancode
1548    sequences that should be grouped together in a single INPUT array.
1549
1550    Inserting non-scancode elements is illegal, but no runtime checks exist
1551    to verify correct input! Violations could lead to unpredictable runtime
1552    behaviour. You've been warned.
1553    """
1554
1555    pass
1556    # --------------------------------------------------------------------------
1557
1558
1559# ----- TypeAlias for KEYBOARD_MAPPING values ----------------------------------
1560ScancodeTypes: TypeAlias = "int | ScancodeSequence"  # TODO 3.10: remove quotes
1561"""
1562Acceptable value types in KEYBOARD_MAPPING.
1563
1564Accepts single standalone scancode integer or multiple scancode integers
1565contained in a special class ScancodeSequence instance.
1566"""
1567# ------------------------------------------------------------------------------
1568
1569
1570# ----- Offsets for values in KEYBOARD_MAPPING ---------------------------------
1571_OFFSET_EXTENDEDKEY: Final = 0xE000
1572_OFFSET_SHIFTKEY: Final = 0x10000
1573# ------------------------------------------------------------------------------
1574
1575
1576# ----- KEYBOARD_MAPPING -------------------------------------------------------
1577_SHIFT_SCANCODE: Final = 0x2A  # Used in auto-shifting
1578# should be keyboard MAKE scancodes ( <0x80 )
1579# some keys require the use of EXTENDEDKEY flags, they
1580US_QWERTY_MAPPING: Final[dict[str, ScancodeTypes]] = {
1581    "escape": 0x01,
1582    "esc": 0x01,
1583    "f1": 0x3B,
1584    "f2": 0x3C,
1585    "f3": 0x3D,
1586    "f4": 0x3E,
1587    "f5": 0x3F,
1588    "f6": 0x40,
1589    "f7": 0x41,
1590    "f8": 0x42,
1591    "f9": 0x43,
1592    "f10": 0x44,
1593    "f11": 0x57,
1594    "f12": 0x58,
1595    "printscreen": 0x54,  # same result as ScancodeSequence([0xE02A, 0xE037])
1596    "prntscrn": 0x54,  # same result as ScancodeSequence([0xE02A, 0xE037])
1597    "prtsc": 0x54,  # same result as ScancodeSequence([0xE02A, 0xE037])
1598    "prtscr": 0x54,  # same result as ScancodeSequence([0xE02A, 0xE037])
1599    "scrolllock": 0x46,
1600    "ctrlbreak": 0x46 + _OFFSET_EXTENDEDKEY,
1601    "pause": ScancodeSequence([0xE11D, 0x45, 0xE19D, 0xC5]),
1602    "`": 0x29,
1603    "1": 0x02,
1604    "2": 0x03,
1605    "3": 0x04,
1606    "4": 0x05,
1607    "5": 0x06,
1608    "6": 0x07,
1609    "7": 0x08,
1610    "8": 0x09,
1611    "9": 0x0A,
1612    "0": 0x0B,
1613    "-": 0x0C,
1614    "=": 0x0D,
1615    "~": 0x29 + _OFFSET_SHIFTKEY,
1616    "!": 0x02 + _OFFSET_SHIFTKEY,
1617    "@": 0x03 + _OFFSET_SHIFTKEY,
1618    "#": 0x04 + _OFFSET_SHIFTKEY,
1619    "$": 0x05 + _OFFSET_SHIFTKEY,
1620    "%": 0x06 + _OFFSET_SHIFTKEY,
1621    "^": 0x07 + _OFFSET_SHIFTKEY,
1622    "&": 0x08 + _OFFSET_SHIFTKEY,
1623    "*": 0x09 + _OFFSET_SHIFTKEY,
1624    "(": 0x0A + _OFFSET_SHIFTKEY,
1625    ")": 0x0B + _OFFSET_SHIFTKEY,
1626    "_": 0x0C + _OFFSET_SHIFTKEY,
1627    "+": 0x0D + _OFFSET_SHIFTKEY,
1628    "backspace": 0x0E,
1629    "\b": 0x0E,
1630    "insert": 0x52 + _OFFSET_EXTENDEDKEY,
1631    "home": 0x47 + _OFFSET_EXTENDEDKEY,
1632    "pageup": 0x49 + _OFFSET_EXTENDEDKEY,
1633    "pgup": 0x49 + _OFFSET_EXTENDEDKEY,
1634    "pagedown": 0x51 + _OFFSET_EXTENDEDKEY,
1635    "pgdn": 0x51 + _OFFSET_EXTENDEDKEY,
1636    # numpad
1637    "numlock": 0x45,
1638    "divide": 0x35 + _OFFSET_EXTENDEDKEY,
1639    "multiply": 0x37,
1640    "subtract": 0x4A,
1641    "add": 0x4E,
1642    "decimal": 0x53,
1643    "numperiod": 0x53,
1644    "numpadenter": 0x1C + _OFFSET_EXTENDEDKEY,
1645    "numpad1": 0x4F,
1646    "numpad2": 0x50,
1647    "numpad3": 0x51,
1648    "numpad4": 0x4B,
1649    "numpad5": 0x4C,
1650    "numpad6": 0x4D,
1651    "numpad7": 0x47,
1652    "numpad8": 0x48,
1653    "numpad9": 0x49,
1654    "num0": 0x52,
1655    "num1": 0x4F,
1656    "num2": 0x50,
1657    "num3": 0x51,
1658    "num4": 0x4B,
1659    "num5": 0x4C,
1660    "num6": 0x4D,
1661    "num7": 0x47,
1662    "num8": 0x48,
1663    "num9": 0x49,
1664    "clear": 0x4C,  # name from pyautogui
1665    # end numpad
1666    "tab": 0x0F,
1667    "\t": 0x0F,
1668    "q": 0x10,
1669    "w": 0x11,
1670    "e": 0x12,
1671    "r": 0x13,
1672    "t": 0x14,
1673    "y": 0x15,
1674    "u": 0x16,
1675    "i": 0x17,
1676    "o": 0x18,
1677    "p": 0x19,
1678    "[": 0x1A,
1679    "]": 0x1B,
1680    "\\": 0x2B,
1681    "Q": 0x10 + _OFFSET_SHIFTKEY,
1682    "W": 0x11 + _OFFSET_SHIFTKEY,
1683    "E": 0x12 + _OFFSET_SHIFTKEY,
1684    "R": 0x13 + _OFFSET_SHIFTKEY,
1685    "T": 0x14 + _OFFSET_SHIFTKEY,
1686    "Y": 0x15 + _OFFSET_SHIFTKEY,
1687    "U": 0x16 + _OFFSET_SHIFTKEY,
1688    "I": 0x17 + _OFFSET_SHIFTKEY,
1689    "O": 0x18 + _OFFSET_SHIFTKEY,
1690    "P": 0x19 + _OFFSET_SHIFTKEY,
1691    "{": 0x1A + _OFFSET_SHIFTKEY,
1692    "}": 0x1B + _OFFSET_SHIFTKEY,
1693    "|": 0x2B + _OFFSET_SHIFTKEY,
1694    "del": 0x53 + _OFFSET_EXTENDEDKEY,
1695    "delete": 0x53 + _OFFSET_EXTENDEDKEY,
1696    "end": 0x4F + _OFFSET_EXTENDEDKEY,
1697    "capslock": 0x3A,
1698    "a": 0x1E,
1699    "s": 0x1F,
1700    "d": 0x20,
1701    "f": 0x21,
1702    "g": 0x22,
1703    "h": 0x23,
1704    "j": 0x24,
1705    "k": 0x25,
1706    "l": 0x26,
1707    ";": 0x27,
1708    "'": 0x28,
1709    "A": 0x1E + _OFFSET_SHIFTKEY,
1710    "S": 0x1F + _OFFSET_SHIFTKEY,
1711    "D": 0x20 + _OFFSET_SHIFTKEY,
1712    "F": 0x21 + _OFFSET_SHIFTKEY,
1713    "G": 0x22 + _OFFSET_SHIFTKEY,
1714    "H": 0x23 + _OFFSET_SHIFTKEY,
1715    "J": 0x24 + _OFFSET_SHIFTKEY,
1716    "K": 0x25 + _OFFSET_SHIFTKEY,
1717    "L": 0x26 + _OFFSET_SHIFTKEY,
1718    ":": 0x27 + _OFFSET_SHIFTKEY,
1719    '"': 0x28 + _OFFSET_SHIFTKEY,
1720    "enter": 0x1C,
1721    "return": 0x1C,
1722    "\n": 0x1C,
1723    "shift": _SHIFT_SCANCODE,
1724    "shiftleft": _SHIFT_SCANCODE,
1725    "z": 0x2C,
1726    "x": 0x2D,
1727    "c": 0x2E,
1728    "v": 0x2F,
1729    "b": 0x30,
1730    "n": 0x31,
1731    "m": 0x32,
1732    ",": 0x33,
1733    ".": 0x34,
1734    "/": 0x35,
1735    "Z": 0x2C + _OFFSET_SHIFTKEY,
1736    "X": 0x2D + _OFFSET_SHIFTKEY,
1737    "C": 0x2E + _OFFSET_SHIFTKEY,
1738    "V": 0x2F + _OFFSET_SHIFTKEY,
1739    "B": 0x30 + _OFFSET_SHIFTKEY,
1740    "N": 0x31 + _OFFSET_SHIFTKEY,
1741    "M": 0x32 + _OFFSET_SHIFTKEY,
1742    "<": 0x33 + _OFFSET_SHIFTKEY,
1743    ">": 0x34 + _OFFSET_SHIFTKEY,
1744    "?": 0x35 + _OFFSET_SHIFTKEY,
1745    "shiftright": 0x36,
1746    "ctrl": 0x1D,
1747    "ctrlleft": 0x1D,
1748    "win": 0x5B + _OFFSET_EXTENDEDKEY,
1749    "super": 0x5B + _OFFSET_EXTENDEDKEY,  # name from pyautogui
1750    "winleft": 0x5B + _OFFSET_EXTENDEDKEY,
1751    "alt": 0x38,
1752    "altleft": 0x38,
1753    " ": 0x39,
1754    "space": 0x39,
1755    "altright": 0x38 + _OFFSET_EXTENDEDKEY,
1756    "winright": 0x5C + _OFFSET_EXTENDEDKEY,
1757    "apps": 0x5D + _OFFSET_EXTENDEDKEY,
1758    "context": 0x5D + _OFFSET_EXTENDEDKEY,
1759    "contextmenu": 0x5D + _OFFSET_EXTENDEDKEY,
1760    "ctrlright": 0x1D + _OFFSET_EXTENDEDKEY,
1761    # Originally from learncodebygaming/pydirectinput:
1762    # arrow key scancodes can be different depending on the hardware,
1763    # so I think the best solution is to look it up based on the virtual key
1764    # https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mapvirtualkeya
1765    "up": _map_virtual_key(0x26, _MAPVK_VK_TO_VSC) + _OFFSET_EXTENDEDKEY,
1766    "left": _map_virtual_key(0x25, _MAPVK_VK_TO_VSC) + _OFFSET_EXTENDEDKEY,
1767    "down": _map_virtual_key(0x28, _MAPVK_VK_TO_VSC) + _OFFSET_EXTENDEDKEY,
1768    "right": _map_virtual_key(0x27, _MAPVK_VK_TO_VSC) + _OFFSET_EXTENDEDKEY,
1769    # While forking the original repository and working on the code,
1770    # I'm starting to doubt this still holds true.
1771    # As far as I can see, arrow keys are just the Numpad scancodes for Num
1772    # 2, 4, 6, and 8 with EXTENDEDKEY flag.
1773    # In fact, looking up the virtual key codes will just return the very same
1774    # scancodes the Numpad keys occupy.
1775    "help": 0x63,
1776    "sleep": 0x5F + _OFFSET_EXTENDEDKEY,
1777    "medianext": 0x19 + _OFFSET_EXTENDEDKEY,
1778    "nexttrack": 0x19 + _OFFSET_EXTENDEDKEY,
1779    "mediaprevious": 0x10 + _OFFSET_EXTENDEDKEY,
1780    "prevtrack": 0x10 + _OFFSET_EXTENDEDKEY,
1781    "mediastop": 0x24 + _OFFSET_EXTENDEDKEY,
1782    "stop": 0x24 + _OFFSET_EXTENDEDKEY,
1783    "mediaplay": 0x22 + _OFFSET_EXTENDEDKEY,
1784    "mediapause": 0x22 + _OFFSET_EXTENDEDKEY,
1785    "playpause": 0x22 + _OFFSET_EXTENDEDKEY,
1786    "mute": 0x20 + _OFFSET_EXTENDEDKEY,
1787    "volumemute": 0x20 + _OFFSET_EXTENDEDKEY,
1788    "volumeup": 0x30 + _OFFSET_EXTENDEDKEY,
1789    "volup": 0x30 + _OFFSET_EXTENDEDKEY,
1790    "volumedown": 0x2E + _OFFSET_EXTENDEDKEY,
1791    "voldown": 0x2E + _OFFSET_EXTENDEDKEY,
1792    "media": 0x6D + _OFFSET_EXTENDEDKEY,
1793    "launchmediaselect": 0x6D + _OFFSET_EXTENDEDKEY,
1794    "email": 0x6C + _OFFSET_EXTENDEDKEY,
1795    "launchmail": 0x6C + _OFFSET_EXTENDEDKEY,
1796    "calculator": 0x21 + _OFFSET_EXTENDEDKEY,
1797    "calc": 0x21 + _OFFSET_EXTENDEDKEY,
1798    "launch1": 0x6B + _OFFSET_EXTENDEDKEY,
1799    "launchapp1": 0x6B + _OFFSET_EXTENDEDKEY,
1800    "launch2": 0x21 + _OFFSET_EXTENDEDKEY,
1801    "launchapp2": 0x21 + _OFFSET_EXTENDEDKEY,
1802    "browsersearch": 0x65 + _OFFSET_EXTENDEDKEY,
1803    "browserhome": 0x32 + _OFFSET_EXTENDEDKEY,
1804    "browserforward": 0x69 + _OFFSET_EXTENDEDKEY,
1805    "browserback": 0x6A + _OFFSET_EXTENDEDKEY,
1806    "browserstop": 0x68 + _OFFSET_EXTENDEDKEY,
1807    "browserrefresh": 0x67 + _OFFSET_EXTENDEDKEY,
1808    "browserfavorites": 0x66 + _OFFSET_EXTENDEDKEY,
1809    "f13": 0x64,
1810    "f14": 0x65,
1811    "f15": 0x66,
1812    "f16": 0x67,
1813    "f17": 0x68,
1814    "f18": 0x69,
1815    "f19": 0x6A,
1816    "f20": 0x6B,
1817    "f21": 0x6C,
1818    "f22": 0x6D,
1819    "f23": 0x6E,
1820    "f24": 0x76,
1821}
1822"""
1823Maps a string representation of keyboard keys to their corresponding hardware
1824scan code. Based on standard US QWERTY-Layout.
1825
1826Not intended to be changed at runtime!
1827
1828If you want to change the keyboard mapping to better reflect your own keyboard
1829layout, use `KEYBOARD_MAPPING.update(your_dict)` where `your_dict` is a dict
1830that maps keynames to scancodes.
1831"""
1832
1833KEYBOARD_MAPPING: dict[str, ScancodeTypes] = {}
1834"""
1835Maps a string representation of keyboard keys to their corresponding hardware
1836scan code. Based on standard US QWERTY-Layout by default.
1837
1838If you want to change the keyboard mapping to better reflect your own keyboard
1839layout, use `KEYBOARD_MAPPING.update(your_dict)`, where `your_dict` is a dict
1840that maps keynames to scancodes.
1841"""
1842KEYBOARD_MAPPING.update(US_QWERTY_MAPPING)  # use US QWERTY by default
1843# ------------------------------------------------------------------------------
1844
1845
1846# ==============================================================================
1847# ===== Fail Safe and Pause implementation =====================================
1848# ==============================================================================
1849
1850
1851# ----- Exceptions -------------------------------------------------------------
1852class FailSafeException(Exception):
1853    """Raised when _failSafeCheck detects failsafe mouse position."""
1854
1855    pass
1856
1857
1858class PriorInputFailedException(Exception):
1859    """Raised in hold() context managers when raise_on_failure is set."""
1860
1861    pass
1862    # --------------------------------------------------------------------------
1863
1864
1865# ----- Failsafe Check ---------------------------------------------------------
1866def _failSafeCheck() -> None:
1867    """
1868    Check if mouse has been moved into one of the defined failsafe points,
1869    indicated by global var `FAILSAFE_POINTS`, and raise `FailSafeException`
1870    if that's the case.
1871
1872    Set global var `FAILSAFE` to False to stop raising exceptions.
1873    """
1874    if FAILSAFE and tuple(position()) in FAILSAFE_POINTS:
1875        raise FailSafeException(
1876            "PyDirectInput fail-safe triggered from mouse moving to a corner "
1877            "of the screen. "
1878            "To disable this fail-safe, set pydirectinput.FAILSAFE to False. "
1879            "DISABLING FAIL-SAFE IS NOT RECOMMENDED."
1880        )
1881    # --------------------------------------------------------------------------
1882
1883
1884# ----- handle pause for generic input checks ----------------------------------
1885def _handlePause(_pause: Any) -> None:
1886    """
1887    Pause the default amount of time if `_pause=True` in function arguments.
1888    """
1889    if _pause and PAUSE:
1890        _sleep(PAUSE)
1891    # --------------------------------------------------------------------------
1892
1893
1894# ----- generic input check decorator ------------------------------------------
1895_PS = ParamSpec("_PS")  # param spec
1896_RT = TypeVar("_RT")  # return type
1897
1898
1899# direct copy of _genericPyAutoGUIChecks()
1900def _genericPyDirectInputChecks(
1901    wrappedFunction: Callable[_PS, _RT],
1902) -> Callable[_PS, _RT]:
1903    """
1904    Decorator for wrapping input functions.
1905
1906    Performs failsafe checking and inserts artifical delay after input
1907    functions have been executed unless disabled.
1908
1909    The delay amount is set by global var `PAUSE`.
1910    """
1911
1912    @functools.wraps(wrappedFunction)
1913    def wrapper(*args: _PS.args, **kwargs: _PS.kwargs) -> _RT:
1914        returnVal: _RT
1915        if PAUSE:  # Skip _pause checks if PAUSE has been globally disabled.
1916            _pause: Any
1917            if "_pause" in kwargs:  # Fast track, low cost lookup.
1918                _pause = kwargs[
1919                    "_pause"
1920                ]  # pyrefly: ignore[unsupported-operation]
1921            else:  # Slow track, inspect.getcallargs() is expensive.
1922                funcArgs: dict[str, Any] = inspect.getcallargs(
1923                    wrappedFunction,
1924                    *args,
1925                    **kwargs,  # pyrefly: ignore[bad-argument-type]
1926                )
1927                _pause = funcArgs.get("_pause")
1928            _failSafeCheck()
1929            returnVal = wrappedFunction(*args, **kwargs)
1930            _handlePause(_pause)
1931        else:
1932            _failSafeCheck()
1933            returnVal = wrappedFunction(*args, **kwargs)
1934        return returnVal
1935
1936    return wrapper
1937    # --------------------------------------------------------------------------
1938
1939
1940# ==============================================================================
1941# ===== Helper Functions =======================================================
1942# ==============================================================================
1943# --------------------------------------------------------------------------
1944def _calc_normalized_screen_coord(pixel_coord: int, display_total: int) -> int:
1945    """
1946    Convert a pixel coordinate to normalized Windows screen coordinate value
1947    (range 0 - 65535) by taking the average of two neighboring pixels.
1948    """
1949    # formula from this strange (probably machine translated) article
1950    # https://sourceexample.com/make-a-statement-in-the-source-code-of-the-coordinate-conversion-of-sendinput-(windows-api)-that-is-overflowing-in-the-streets-23df9/
1951    # win_coord = (x * 65536 + width - 1) // width
1952    # This alone is not enough, but we can do better by taking the average of
1953    # this pixel plus the next pixel.
1954    # In my testing this perfectly reflected the real pixel that SendInput
1955    # moves to, down to the pixels that Windows itself can't resolve.
1956    this_pixel: Final[int] = (
1957        pixel_coord * 65536 + display_total - 1
1958    ) // display_total
1959    next_pixel: Final[int] = (
1960        (pixel_coord + 1) * 65536 + display_total - 1
1961    ) // display_total
1962    return (this_pixel + next_pixel) // 2
1963    # --------------------------------------------------------------------------
1964
1965
1966# ----- translate pixels to normalized Windows coordinates ---------------------
1967def _to_windows_coordinates(
1968    x: int = 0, y: int = 0, *, virtual: bool = False
1969) -> tuple[int, int]:
1970    """
1971    Convert x,y coordinates to normalized windows coordinates and return as
1972    tuple (x, y).
1973    """
1974    display_width: int
1975    display_height: int
1976    offset_left: int
1977    offset_top: int
1978
1979    if virtual:
1980        display_width, display_height, offset_left, offset_top = virtual_size()
1981    else:
1982        display_width, display_height = size()
1983        offset_left, offset_top = 0, 0
1984
1985    windows_x: int = _calc_normalized_screen_coord(
1986        x - offset_left, display_width
1987    )
1988    windows_y: int = _calc_normalized_screen_coord(
1989        y - offset_top, display_height
1990    )
1991
1992    return windows_x, windows_y
1993    # --------------------------------------------------------------------------
1994
1995
1996# ----- line coordinates based on Bresenham's line algorithm -------------------
1997def _bresenham(x0: int, y0: int, x1: int, y1: int) -> list[tuple[int, int]]:
1998    """
1999    Return a list of coordinates on the line from (x0, y0) to (x1, y1).
2000    Start and end point are included in the result.
2001
2002    Based on Bresenham's line algorithm.
2003    Using Petr Viktorin's implementation (MIT license)
2004    https://github.com/encukou/bresenham
2005    """
2006
2007    def gen() -> Generator[tuple[int, int], None, None]:
2008        """
2009        Yield integer coordinates on the line from (x0, y0) to (x1, y1).
2010
2011        Input coordinates should be integers.
2012
2013        The result will contain both the start and the end point.
2014        """
2015        dx: int = x1 - x0
2016        dy: int = y1 - y0
2017
2018        xsign: Literal[1, -1] = 1 if dx > 0 else -1
2019        ysign: Literal[1, -1] = 1 if dy > 0 else -1
2020
2021        dx = abs(dx)
2022        dy = abs(dy)
2023
2024        xx: Literal[1, 0, -1]
2025        xy: Literal[1, 0, -1]
2026        yx: Literal[1, 0, -1]
2027        yy: Literal[1, 0, -1]
2028
2029        if dx > dy:
2030            xx, xy, yx, yy = xsign, 0, 0, ysign
2031        else:
2032            dx, dy = dy, dx
2033            xx, xy, yx, yy = 0, ysign, xsign, 0
2034
2035        d: int = 2 * dy - dx
2036        y = 0
2037        for x in range(dx + 1):
2038            yield x0 + x * xx + y * yx, y0 + x * xy + y * yy
2039            if d >= 0:
2040                y += 1
2041                d -= 2 * dx
2042            d += 2 * dy
2043
2044    return list(gen())
2045    # --------------------------------------------------------------------------
2046
2047
2048# ----- path function type alias -----------------------------------------------
2049PathFunction: TypeAlias = (
2050    "Callable[[int, int, int, int], list[tuple[int, int]]]"
2051)
2052# ------------------------------------------------------------------------------
2053
2054
2055# ----- linear tweening function -----------------------------------------------
2056def _linear(n: float) -> float:
2057    """
2058    Linear tweening function that clamps n between 0.0 and 1.0.
2059    Otherwise returns n unchanged.
2060    """
2061    if n >= 1.0:
2062        return 1.0
2063    if n <= 0.0:
2064        return 0.0
2065    return n
2066    # --------------------------------------------------------------------------
2067
2068
2069# ----- get mouse position -----------------------------------------------------
2070def position(
2071    x: int | float | None = None, y: int | float | None = None
2072) -> tuple[int, int]:
2073    """
2074    Return a postion tuple `(x, y)`.
2075
2076    If x and/or y argument(s) are not given, use current mouse cursor coordinate
2077    instead.
2078    """
2079    cursor: _POINT = _get_cursor_pos()
2080    return (
2081        cursor.x if x is None else int(x),
2082        cursor.y if y is None else int(y),
2083    )
2084    # --------------------------------------------------------------------------
2085
2086
2087# ----- get primary screen resolution ------------------------------------------
2088def size() -> tuple[int, int]:
2089    """
2090    Return the size of the primary display as tuple `(width, height)`.
2091    """
2092    return (
2093        _get_system_metrics(_SM_CXSCREEN),
2094        _get_system_metrics(_SM_CYSCREEN),
2095    )
2096    # --------------------------------------------------------------------------
2097
2098
2099# ----- get resolution of multi monitor bounding box ---------------------------
2100def virtual_size() -> tuple[int, int, int, int]:
2101    """
2102    Return the the display size of the complete virtual monitor bounding box
2103    rectangle as tuple `(width, height, left_offset, top_offset)`.
2104
2105    On a single monitor system, this function is equal to `(*size(), 0, 0)`.
2106
2107    `left_offset` and `top_offset` are measured from the top left pixel of the
2108    primary monitor.
2109    """
2110    return (
2111        _get_system_metrics(_SM_CXVIRTUALSCREEN),
2112        _get_system_metrics(_SM_CYVIRTUALSCREEN),
2113        _get_system_metrics(_SM_XVIRTUALSCREEN),
2114        _get_system_metrics(_SM_YVIRTUALSCREEN),
2115    )
2116    # --------------------------------------------------------------------------
2117
2118
2119# ----- are coordinates on primary monitor -------------------------------------
2120@overload
2121def on_primary_monitor(
2122    x: int | None = None,
2123    y: int | None = None,
2124) -> bool: ...
2125
2126
2127@overload
2128def on_primary_monitor(
2129    x: tuple[int, int],
2130    y: None = None,
2131) -> bool: ...
2132
2133
2134def on_primary_monitor(
2135    x: int | tuple[int, int] | None = None,
2136    y: int | None = None,
2137) -> bool:
2138    """
2139    Returns whether the given xy coordinates are on the primary screen or not.
2140
2141    If x and/or y argument(s) are not given, current mouse cursor coordinates
2142    will be used instead.
2143    """
2144    _x: int | None
2145    _y: int | None
2146    if isinstance(x, Sequence):
2147        assert not isinstance(x, int)  # remove int annotation, mypy needs this
2148        if y is not None:
2149            raise ValueError(
2150                "onScreen() does not accept Sequence-types as first argument "
2151                "if a second argument is also provided!"
2152            )
2153        try:
2154            _x, _y = x[0], x[1]
2155        except IndexError as e:
2156            raise ValueError(
2157                "onScreen() does not accept single element sequences "
2158                "as first argument!"
2159            ) from e
2160    else:
2161        _x, _y = x, y
2162
2163    x, y = position(_x, _y)
2164    display_width: int
2165    display_height: int
2166    display_width, display_height = size()
2167
2168    return 0 <= x < display_width and 0 <= y < display_height
2169
2170
2171onScreen = on_primary_monitor
2172# ------------------------------------------------------------------------------
2173
2174
2175# ----- are coordinates on any monitor -----------------------------------------
2176@overload
2177def valid_screen_coordinates(
2178    x: int | None = None,
2179    y: int | None = None,
2180) -> bool: ...
2181
2182
2183@overload
2184def valid_screen_coordinates(x: tuple[int, int], y: None = None) -> bool: ...
2185
2186
2187def valid_screen_coordinates(
2188    x: int | tuple[int, int] | None = None, y: int | None = None
2189) -> bool:
2190    """
2191    Returns whether the given xy coordinates are on a real monitor or not.
2192
2193    If x and/or y argument(s) are not given, current mouse cursor coordinates
2194    will be used instead.
2195    """
2196    _x: int | None
2197    _y: int | None
2198    if isinstance(x, Sequence):
2199        assert not isinstance(x, int)  # remove int annotation, mypy needs this
2200        if y is not None:
2201            raise ValueError(
2202                "onScreen() does not accept Sequence-types as first argument "
2203                "if a second argument is also provided!"
2204            )
2205        try:
2206            _x, _y = x[0], x[1]
2207        except IndexError as e:
2208            raise ValueError(
2209                "onScreen() does not accept single element sequences "
2210                "as first argument!"
2211            ) from e
2212    else:
2213        _x, _y = x, y
2214
2215    x, y = position(_x, _y)
2216    return _monitor_from_point(x, y) is not None
2217    # --------------------------------------------------------------------------
2218
2219
2220# ----- lookup function for MOUSEINPUT data ------------------------------------
2221def _get_mouse_struct_data(
2222    button: str, method: Literal[0, 1, 2]
2223) -> tuple[int | None, int]:
2224    """
2225    Translate a button string to INPUT struct data.
2226
2227    Automatically detect the correct button if `MOUSE_PRIMARY` or
2228    `MOUSE_SECONDARY` are given as the button argument.
2229    """
2230    if not (0 <= method <= 2):
2231        raise ValueError(f"method index {method} is not a valid argument!")
2232
2233    buttons_swapped: bool
2234    if button == MOUSE_PRIMARY:
2235        buttons_swapped = _get_system_metrics(_SM_SWAPBUTTON) != 0
2236        button = MOUSE_RIGHT if buttons_swapped else MOUSE_LEFT
2237    elif button == MOUSE_SECONDARY:
2238        buttons_swapped = _get_system_metrics(_SM_SWAPBUTTON) != 0
2239        button = MOUSE_LEFT if buttons_swapped else MOUSE_RIGHT
2240
2241    event_value: int | None
2242    event_value = _MOUSE_MAPPING_EVENTF.get(button, (None, None, None))[method]
2243    mouseData: int = _MOUSE_MAPPING_DATA.get(button, 0)
2244
2245    return event_value, mouseData
2246    # --------------------------------------------------------------------------
2247
2248
2249# ----- normalize key name to lower case if not shifiting ----------------------
2250def _normalize_key(key: str, *, auto_shift: bool = False) -> str:
2251    """
2252    return a lowercase representation of `key` if key is longer than one
2253    character or automatic shifting is disabled (default).
2254    """
2255    return key.lower() if (len(key) > 1 or not auto_shift) else key
2256    # --------------------------------------------------------------------------
2257
2258
2259# ------------------------------------------------------------------------------
2260# ----- Mouse acceleration and Ehanced Pointer Precision storage singleton -----
2261class __MouseSpeedSettings:
2262    """
2263    Allows controlled storage of Windows Enhanced Pointer Precision and mouse
2264    speed settings.
2265    """
2266
2267    __context_manager_epp: ClassVar[int | None] = None
2268    __context_manager_speed: ClassVar[int | None] = None
2269    __context_manager_count: ClassVar[int] = 0
2270    __context_manager_lock: ClassVar[Lock] = Lock()
2271    __manual_store_epp: ClassVar[int | None] = None
2272    __manual_store_speed: ClassVar[int | None] = None
2273    __manual_lock: ClassVar[Lock] = Lock()
2274    # --------------------------------------------------------------------------
2275
2276    @classmethod
2277    def get_manual_mouse_settings(cls) -> tuple[int | None, int | None]:
2278        with cls.__manual_lock:
2279            return (cls.__manual_store_epp, cls.__manual_store_speed)
2280        # ----------------------------------------------------------------------
2281
2282    @classmethod
2283    def set_manual_mouse_settings(
2284        cls, enhanced_pointer_precision_enabled: int, mouse_speed: int
2285    ) -> None:
2286        with cls.__manual_lock:
2287            cls.__manual_store_epp = enhanced_pointer_precision_enabled
2288            cls.__manual_store_speed = mouse_speed
2289        # ----------------------------------------------------------------------
2290
2291    @classmethod
2292    def get_ctxtmgr_mouse_settings(cls) -> tuple[int | None, int | None]:
2293        with cls.__context_manager_lock:
2294            cls.__context_manager_count -= 1
2295            if cls.__context_manager_count > 0:
2296                # Don't retrieve stored value until last
2297                return (None, None)
2298            epp_enabled: int | None = cls.__context_manager_epp
2299            mouse_speed: int | None = cls.__context_manager_speed
2300            cls.__context_manager_epp = None
2301            cls.__context_manager_speed = None
2302            return (epp_enabled, mouse_speed)
2303        # ----------------------------------------------------------------------
2304
2305    @classmethod
2306    def set_ctxtmgr_mouse_settings(
2307        cls, enhanced_pointer_precision_enabled: int, mouse_speed: int
2308    ) -> None:
2309        with cls.__context_manager_lock:
2310            cls.__context_manager_count += 1
2311            if cls.__context_manager_count > 1:
2312                # Don't allow changing the stored value if another value is
2313                # already stored
2314                return
2315            cls.__context_manager_epp = enhanced_pointer_precision_enabled
2316            cls.__context_manager_speed = mouse_speed
2317        # ----------------------------------------------------------------------
2318
2319
2320# ----- Temporarily disable Enhanced Pointer Precision -------------------------
2321@contextmanager
2322def _no_mouse_acceleration() -> Generator[None, None, None]:
2323    """
2324    Context manager that allows temporarily disabling Windows Enhanced Pointer
2325    Precision on enter and restoring the previous setting on exit.
2326    """
2327    th1: int
2328    th2: int
2329    precision: int | None
2330    # store mouse parameters (thresholds, enhanced pointer precision)
2331    th1, th2, precision = _get_mouse_parameters()
2332    speed: int | None = _get_mouse_speed()
2333    assert isinstance(precision, int)
2334    assert isinstance(speed, int)
2335    __MouseSpeedSettings.set_ctxtmgr_mouse_settings(precision, speed)
2336    try:
2337        # modify mouse parameters
2338        if precision != 0:
2339            _set_mouse_parameters(th1, th2, 0)
2340        if speed != 10:
2341            _set_mouse_speed(10)
2342        yield
2343    finally:
2344        # restore mouse parameters
2345        precision, speed = __MouseSpeedSettings.get_ctxtmgr_mouse_settings()
2346        if precision is not None:
2347            _set_mouse_parameters(th1, th2, precision)
2348        if speed is not None:
2349            _set_mouse_speed(speed)
2350    # --------------------------------------------------------------------------
2351
2352
2353# ----- manually store current enhanced pointer precision setting --------------
2354def store_mouse_acceleration_settings() -> None:
2355    """
2356    Manually save the current Windows Enhanced Pointer Precision setting so
2357    that it can be restored later with `restore_mouse_acceleration_settings()`.
2358    """
2359    precision: int
2360    _, _, precision = _get_mouse_parameters()
2361    speed: int = _get_mouse_speed()
2362    __MouseSpeedSettings.set_manual_mouse_settings(precision, speed)
2363    # --------------------------------------------------------------------------
2364
2365
2366# ----- manually restore current enhanced pointer precision setting ------------
2367def restore_mouse_acceleration_settings() -> None:
2368    """
2369    Manually restore the current Windows Enhanced Pointer Precision setting to
2370    what it was beforehand when it was saved with
2371    `store_mouse_acceleration_settings()`.
2372    """
2373    precision: int | None
2374    speed: int | None
2375    precision, speed = __MouseSpeedSettings.get_manual_mouse_settings()
2376    if precision is None or speed is None:
2377        raise ValueError(
2378            "Can't restore Enhanced Pointer Precision setting! "
2379            "Setting was not saved beforehand!"
2380        )
2381    th1: int
2382    th2: int
2383    th1, th2, _ = _get_mouse_parameters()
2384    _set_mouse_parameters(th1, th2, precision)
2385    _set_mouse_speed(speed)
2386    # --------------------------------------------------------------------------
2387
2388
2389# ==============================================================================
2390# ===== Main Mouse Functions ===================================================
2391# ==============================================================================
2392
2393
2394# ----- mouseDown --------------------------------------------------------------
2395@_genericPyDirectInputChecks
2396def mouseDown(
2397    x: int | None = None,
2398    y: int | None = None,
2399    button: str = MOUSE_PRIMARY,
2400    duration: float = 0.0,
2401    tween: Callable[[float], float] | None = None,
2402    logScreenshot: bool = False,
2403    _pause: bool = True,
2404    *,
2405    relative: bool = False,
2406    virtual: bool = False,
2407    path_function: PathFunction | None = None,
2408    attempt_pixel_perfect: bool = False,
2409    disable_mouse_acceleration: bool = False,
2410) -> None:
2411    """
2412    Press down mouse button `button`.
2413
2414    If `x` or `y` are given and not None, then the mouse will move the
2415    indicated postion before pressing the button.
2416
2417    `button` is the name of the button to press. Use the public `MOUSE_*`
2418    constants to get valid argument values. (If you change the constants, then
2419    you will have to call `update_MOUSEEVENT_mappings()` to resync the lookup
2420    functions)
2421
2422    If `_pause` is True (default), then an automatic sleep will be performed
2423    after the function finshes executing. The duration is set by the global
2424    variable `PAUSE`.
2425
2426    `duration`, `tween`, `relative`, `virtual`, `path_function`,
2427    `attempt_pixel_perfect`, `disable_mouse_acceleration` are only relevant
2428    if x or y are given.
2429    See `moveTo()` for further information.
2430
2431    Raises `ValueError` if `button` is not a valid mouse button name.
2432
2433    ----------------------------------------------------------------------------
2434
2435    NOTE: `logScreenshot` is currently unsupported.
2436    """
2437    # TODO: bounding box check for valid position
2438    if x is not None or y is not None:
2439        moveTo(
2440            x,
2441            y,
2442            duration=duration,
2443            tween=tween,
2444            logScreenshot=logScreenshot,
2445            _pause=False,  # don't add an additional pause
2446            relative=relative,
2447            virtual=virtual,
2448            path_function=path_function,
2449            attempt_pixel_perfect=attempt_pixel_perfect,
2450            disable_mouse_acceleration=disable_mouse_acceleration,
2451        )
2452
2453    event_value: int | None = None
2454    mouseData: int
2455    event_value, mouseData = _get_mouse_struct_data(button, _MOUSE_PRESS)
2456
2457    if not event_value:
2458        raise ValueError(
2459            f"Invalid button argument! "
2460            f'Expected "{MOUSE_LEFT}", "{MOUSE_RIGHT}", "{MOUSE_MIDDLE}", '
2461            f'"{MOUSE_BUTTON4}", "{MOUSE_BUTTON5}", "{MOUSE_PRIMARY}" or '
2462            f'"{MOUSE_SECONDARY}"; got "{button}" instead!'
2463        )
2464
2465    _send_input(_create_mouse_input(mouseData=mouseData, dwFlags=event_value))
2466    # --------------------------------------------------------------------------
2467
2468
2469# ----- mouseUp ----------------------------------------------------------------
2470@_genericPyDirectInputChecks
2471def mouseUp(
2472    x: int | None = None,
2473    y: int | None = None,
2474    button: str = MOUSE_PRIMARY,
2475    duration: float = 0.0,
2476    tween: Callable[[float], float] | None = None,
2477    logScreenshot: bool = False,
2478    _pause: bool = True,
2479    *,
2480    relative: bool = False,
2481    virtual: bool = False,
2482    path_function: PathFunction | None = None,
2483    attempt_pixel_perfect: bool = False,
2484    disable_mouse_acceleration: bool = False,
2485) -> None:
2486    """
2487    Lift up mouse button `button`.
2488
2489    If `x` or `y` are given and not None, then the mouse will move the
2490    indicated postion before lifting the button.
2491
2492    `button` is the name of the button to press. Use the public `MOUSE_*`
2493    constants to get valid argument values. (If you change the constants, then
2494    you will have to call `update_MOUSEEVENT_mappings()` to resync the lookup
2495    functions)
2496
2497    If `_pause` is True (default), then an automatic sleep will be performed
2498    after the function finshes executing. The duration is set by the global
2499    variable `PAUSE`.
2500
2501    `duration`, `tween`, `relative`, `virtual`, `path_function`,
2502    `attempt_pixel_perfect`, `disable_mouse_acceleration` are only relevant
2503    if x or y are given.
2504    See `moveTo()` for further information.
2505
2506    Raises `ValueError` if `button` is not a valid mouse button name.
2507
2508    ----------------------------------------------------------------------------
2509
2510    NOTE: `logScreenshot` is currently unsupported.
2511    """
2512    # TODO: bounding box check for valid position
2513    if x is not None or y is not None:
2514        moveTo(
2515            x,
2516            y,
2517            duration=duration,
2518            tween=tween,
2519            logScreenshot=logScreenshot,
2520            _pause=False,  # don't add an additional pause
2521            relative=relative,
2522            virtual=virtual,
2523            path_function=path_function,
2524            attempt_pixel_perfect=attempt_pixel_perfect,
2525            disable_mouse_acceleration=disable_mouse_acceleration,
2526        )
2527
2528    event_value: int | None = None
2529    mouseData: int
2530    event_value, mouseData = _get_mouse_struct_data(button, _MOUSE_RELEASE)
2531
2532    if not event_value:
2533        raise ValueError(
2534            "Invalid button argument! "
2535            f'Expected "{MOUSE_LEFT}", "{MOUSE_RIGHT}", "{MOUSE_MIDDLE}", '
2536            f'"{MOUSE_BUTTON4}", "{MOUSE_BUTTON5}", "{MOUSE_PRIMARY}" or '
2537            f'"{MOUSE_SECONDARY}"; got "{button}" instead!'
2538        )
2539
2540    _send_input(_create_mouse_input(mouseData=mouseData, dwFlags=event_value))
2541    # --------------------------------------------------------------------------
2542
2543
2544# ----- click ------------------------------------------------------------------
2545@_genericPyDirectInputChecks
2546def click(
2547    x: int | None = None,
2548    y: int | None = None,
2549    clicks: int = 1,
2550    interval: float = 0.0,
2551    button: str = MOUSE_PRIMARY,
2552    duration: float = 0.0,
2553    tween: Callable[[float], float] | None = None,
2554    logScreenshot: bool = False,
2555    _pause: bool = True,
2556    *,
2557    relative: bool = False,
2558    virtual: bool = False,
2559    path_function: PathFunction | None = None,
2560    attempt_pixel_perfect: bool = False,
2561    disable_mouse_acceleration: bool = False,
2562) -> None:
2563    """
2564    Click mouse button `button` (combining press down and lift up).
2565
2566    If `x` or `y` are given and not None, then the mouse will move the
2567    indicated postion before clicking the button.
2568
2569    `button` is the name of the button to press. Use the public `MOUSE_*`
2570    constants to get valid argument values. (If you change the constants, then
2571    you will have to call `update_MOUSEEVENT_mappings()` to resync the lookup
2572    functions)
2573
2574    `clicks` is an integer that determines the amount of times the button will
2575    be clicked.
2576
2577    `interval` is the wait time in seconds between clicks.
2578
2579    If `_pause` is True (default), then an automatic sleep will be performed
2580    after the function finshes executing. The duration is set by the global
2581    variable `PAUSE`.
2582
2583    `duration`, `tween`, `relative`, `virtual`, `path_function`,
2584    `attempt_pixel_perfect`, `disable_mouse_acceleration` are only relevant
2585    if x or y are given.
2586    See `moveTo()` for further information.
2587
2588    Raises `ValueError` if `button` is not a valid mouse button name.
2589
2590    ----------------------------------------------------------------------------
2591
2592    NOTE: `logScreenshot` is currently unsupported.
2593    """
2594    # TODO: bounding box check for valid position
2595    if x is not None or y is not None:
2596        moveTo(
2597            x,
2598            y,
2599            duration=duration,
2600            tween=tween,
2601            logScreenshot=logScreenshot,
2602            _pause=False,  # don't add an additional pause
2603            relative=relative,
2604            virtual=virtual,
2605            path_function=path_function,
2606            attempt_pixel_perfect=attempt_pixel_perfect,
2607            disable_mouse_acceleration=disable_mouse_acceleration,
2608        )
2609
2610    event_value: int | None = None
2611    mouseData: int
2612    event_value, mouseData = _get_mouse_struct_data(button, _MOUSE_CLICK)
2613
2614    if not event_value:
2615        raise ValueError(
2616            f"Invalid button argument! "
2617            f'Expected "{MOUSE_LEFT}", "{MOUSE_RIGHT}", "{MOUSE_MIDDLE}", '
2618            f'"{MOUSE_BUTTON4}", "{MOUSE_BUTTON5}", "{MOUSE_PRIMARY}" or '
2619            f'"{MOUSE_SECONDARY}"; got "{button}" instead!'
2620        )
2621
2622    apply_interval: bool = False
2623    for _ in range(clicks):
2624        if apply_interval:  # Don't delay first press
2625            _sleep(interval)
2626        apply_interval = True
2627
2628        _send_input(
2629            _create_mouse_input(mouseData=mouseData, dwFlags=event_value)
2630        )
2631    # --------------------------------------------------------------------------
2632
2633
2634# ----- leftClick --------------------------------------------------------------
2635def leftClick(
2636    x: int | None = None,
2637    y: int | None = None,
2638    interval: float = 0.0,
2639    duration: float = 0.0,
2640    tween: Callable[[float], float] | None = None,
2641    logScreenshot: bool = False,
2642    _pause: bool = True,
2643    *,
2644    relative: bool = False,
2645    virtual: bool = False,
2646    path_function: PathFunction | None = None,
2647    attempt_pixel_perfect: bool = False,
2648    disable_mouse_acceleration: bool = False,
2649) -> None:
2650    """
2651    Click Left Mouse button.
2652
2653    See `click()` for more information
2654    """
2655    click(
2656        x,
2657        y,
2658        clicks=1,
2659        interval=interval,
2660        button=MOUSE_LEFT,
2661        duration=duration,
2662        tween=tween,
2663        logScreenshot=logScreenshot,
2664        _pause=_pause,  # Keep _pause since this function has no input checks
2665        relative=relative,
2666        virtual=virtual,
2667        path_function=path_function,
2668        attempt_pixel_perfect=attempt_pixel_perfect,
2669        disable_mouse_acceleration=disable_mouse_acceleration,
2670    )
2671    # --------------------------------------------------------------------------
2672
2673
2674# ----- rightClick -------------------------------------------------------------
2675def rightClick(
2676    x: int | None = None,
2677    y: int | None = None,
2678    interval: float = 0.0,
2679    duration: float = 0.0,
2680    tween: Callable[[float], float] | None = None,
2681    logScreenshot: bool = False,
2682    _pause: bool = True,
2683    *,
2684    relative: bool = False,
2685    virtual: bool = False,
2686    path_function: PathFunction | None = None,
2687    attempt_pixel_perfect: bool = False,
2688    disable_mouse_acceleration: bool = False,
2689) -> None:
2690    """
2691    Click Right Mouse button.
2692
2693    See `click()` for more information
2694    """
2695    click(
2696        x,
2697        y,
2698        clicks=1,
2699        interval=interval,
2700        button=MOUSE_RIGHT,
2701        duration=duration,
2702        tween=tween,
2703        logScreenshot=logScreenshot,
2704        _pause=_pause,  # Keep _pause since this function has no input checks
2705        relative=relative,
2706        virtual=virtual,
2707        path_function=path_function,
2708        attempt_pixel_perfect=attempt_pixel_perfect,
2709        disable_mouse_acceleration=disable_mouse_acceleration,
2710    )
2711    # --------------------------------------------------------------------------
2712
2713
2714# ----- middleClick ------------------------------------------------------------
2715def middleClick(
2716    x: int | None = None,
2717    y: int | None = None,
2718    interval: float = 0.0,
2719    duration: float = 0.0,
2720    tween: Callable[[float], float] | None = None,
2721    logScreenshot: bool = False,
2722    _pause: bool = True,
2723    *,
2724    relative: bool = False,
2725    virtual: bool = False,
2726    path_function: PathFunction | None = None,
2727    attempt_pixel_perfect: bool = False,
2728    disable_mouse_acceleration: bool = False,
2729) -> None:
2730    """
2731    Click Middle Mouse button.
2732
2733    See `click()` for more information
2734    """
2735    click(
2736        x,
2737        y,
2738        clicks=1,
2739        interval=interval,
2740        button=MOUSE_MIDDLE,
2741        duration=duration,
2742        tween=tween,
2743        logScreenshot=logScreenshot,
2744        _pause=_pause,  # Keep _pause since this function has no input checks
2745        relative=relative,
2746        virtual=virtual,
2747        path_function=path_function,
2748        attempt_pixel_perfect=attempt_pixel_perfect,
2749        disable_mouse_acceleration=disable_mouse_acceleration,
2750    )
2751    # --------------------------------------------------------------------------
2752
2753
2754# ----- doubleClick ------------------------------------------------------------
2755def doubleClick(
2756    x: int | None = None,
2757    y: int | None = None,
2758    interval: float = 0.0,
2759    button: str = MOUSE_LEFT,
2760    duration: float = 0.0,
2761    tween: Callable[[float], float] | None = None,
2762    logScreenshot: bool = False,
2763    _pause: bool = True,
2764    *,
2765    relative: bool = False,
2766    virtual: bool = False,
2767    path_function: PathFunction | None = None,
2768    attempt_pixel_perfect: bool = False,
2769    disable_mouse_acceleration: bool = False,
2770) -> None:
2771    """
2772    Double click `button`.
2773
2774    See `click()` for more information
2775    """
2776    click(
2777        x,
2778        y,
2779        clicks=2,
2780        interval=interval,
2781        button=button,
2782        duration=duration,
2783        tween=tween,
2784        logScreenshot=logScreenshot,
2785        _pause=_pause,  # Keep _pause since this function has no input checks
2786        relative=relative,
2787        virtual=virtual,
2788        path_function=path_function,
2789        attempt_pixel_perfect=attempt_pixel_perfect,
2790        disable_mouse_acceleration=disable_mouse_acceleration,
2791    )
2792    # --------------------------------------------------------------------------
2793
2794
2795# ----- tripleClick ------------------------------------------------------------
2796def tripleClick(
2797    x: int | None = None,
2798    y: int | None = None,
2799    interval: float = 0.0,
2800    button: str = MOUSE_LEFT,
2801    duration: float = 0.0,
2802    tween: Callable[[float], float] | None = None,
2803    logScreenshot: bool = False,
2804    _pause: bool = True,
2805    *,
2806    relative: bool = False,
2807    virtual: bool = False,
2808    path_function: PathFunction | None = None,
2809    attempt_pixel_perfect: bool = False,
2810    disable_mouse_acceleration: bool = False,
2811) -> None:
2812    """
2813    Triple click `button`.
2814
2815    See `click()` for more information
2816    """
2817    click(
2818        x,
2819        y,
2820        clicks=3,
2821        interval=interval,
2822        button=button,
2823        duration=duration,
2824        tween=tween,
2825        logScreenshot=logScreenshot,
2826        _pause=_pause,  # Keep _pause since this function has no input checks
2827        relative=relative,
2828        virtual=virtual,
2829        path_function=path_function,
2830        attempt_pixel_perfect=attempt_pixel_perfect,
2831        disable_mouse_acceleration=disable_mouse_acceleration,
2832    )
2833    # --------------------------------------------------------------------------
2834
2835
2836# ----- scroll -----------------------------------------------------------------
2837# Originally implemented by
2838# https://github.com/learncodebygaming/pydirectinput/pull/22
2839@_genericPyDirectInputChecks
2840def scroll(
2841    clicks: int = 0,
2842    x: Any = None,  # x and y do absolutely nothing, still keeping the arguments
2843    y: Any = None,  # to stay consistent with PyAutoGUI.
2844    logScreenshot: bool = False,
2845    _pause: bool = True,
2846    *,
2847    interval: float = 0.0,
2848) -> None:
2849    """
2850    Vertically scroll mouse `clicks` number of times, waiting `interval`
2851    seconds between every scroll.
2852
2853    Negative values of `clicks` will scroll down, postive values will scroll
2854    up.
2855
2856    `x` and `y` are intentionally ignored and only exists to keep the call
2857    signature backwards-compatible with PyAutoGui.
2858    If you need to change the mouse position before scrolling use one of the
2859    `move()` functions.
2860
2861    If `_pause` is True (default), then an automatic sleep will be performed
2862    after the function finshes executing. The duration is set by the global
2863    variable `PAUSE`.
2864
2865    ----------------------------------------------------------------------------
2866
2867    NOTE: `logScreenshot` is currently unsupported.
2868    """
2869    direction: Literal[-1, 1]
2870    if clicks >= 0:
2871        direction = 1
2872    else:
2873        direction = -1
2874        clicks = abs(clicks)
2875
2876    apply_interval: bool = False
2877    for _ in range(clicks):
2878        if apply_interval:
2879            _sleep(interval)
2880        apply_interval = True
2881
2882        _send_input(
2883            _create_mouse_input(
2884                mouseData=(direction * _WHEEL_DELTA), dwFlags=_MOUSEEVENTF_WHEEL
2885            )
2886        )
2887    # --------------------------------------------------------------------------
2888
2889
2890# ----- hscroll ----------------------------------------------------------------
2891@_genericPyDirectInputChecks
2892def hscroll(
2893    clicks: int = 0,
2894    x: Any = None,  # x and y do absolutely nothing, still keeping the arguments
2895    y: Any = None,  # to stay consistent with PyAutoGUI.
2896    logScreenshot: bool = False,
2897    _pause: bool = True,
2898    *,
2899    interval: float = 0.0,
2900) -> None:
2901    """
2902    Horizontally scroll mouse `clicks` number of times, waiting `interval`
2903    seconds between every scroll.
2904
2905    Negative values of `clicks` will scroll left, postive values will scroll
2906    right.
2907
2908    `x` and `y` are intentionally ignored and only exists to keep the call
2909    signature backwards-compatible with PyAutoGui.
2910    If you need to change the mouse position before scrolling use one of the
2911    `move()` functions.
2912
2913    If `_pause` is True (default), then an automatic sleep will be performed
2914    after the function finshes executing. The duration is set by the global
2915    variable `PAUSE`.
2916
2917    ----------------------------------------------------------------------------
2918
2919    NOTE: `logScreenshot` is currently unsupported.
2920    """
2921    direction: Literal[-1, 1]
2922    if clicks >= 0:
2923        direction = 1
2924    else:
2925        direction = -1
2926        clicks = abs(clicks)
2927
2928    apply_interval: bool = False
2929    for _ in range(clicks):
2930        if apply_interval:
2931            _sleep(interval)
2932        apply_interval = True
2933
2934        _send_input(
2935            _create_mouse_input(
2936                mouseData=(direction * _WHEEL_DELTA),
2937                dwFlags=_MOUSEEVENTF_HWHEEL,
2938            )
2939        )
2940    # --------------------------------------------------------------------------
2941
2942
2943# ----- scroll alias -----------------------------------------------------------
2944vscroll = scroll
2945# ------------------------------------------------------------------------------
2946
2947
2948# ----- absolute_mouse_move ----------------------------------------------------
2949def _absolute_mouse_move(
2950    x: int | None = None,
2951    y: int | None = None,
2952    duration: float = 0.0,
2953    tween: Callable[[float], float] | None = None,
2954    logScreenshot: bool = False,
2955    target_coords_relative: bool = False,
2956    *,
2957    virtual: bool = False,
2958    path_function: PathFunction | None = None,
2959    attempt_pixel_perfect: bool = False,
2960    disable_mouse_acceleration: bool = False,
2961) -> None:
2962    """
2963    Move the mouse to an absolute(*) postion indicated by the arguments of
2964    `x` and `y`. The coordinates 0,0 represent the top left pixel of the
2965    primary monitor.
2966
2967    If `duration` is floating point number greater than 0, then this function
2968    will automatically split the movement into microsteps instead of moving
2969    straight to the target position.
2970
2971    `tween` is a function that takes a floating point number between 0.0 and
2972    1.0 and returns another floating point number between 0.0 and 1.0. The
2973    returned number will be used to calculate the next position of the
2974    mouse between the start and the end position based on the current duration.
2975    The default tweening function is linear, which will move the mouse at a
2976    constant speed. See the `pytweening` package for more tweening functions.
2977
2978    `path_function` is a function that takes the start and end coordinates of
2979    the mouse movement (4 integers) and returns a list of coordinates (list of
2980    tuples containting 2 integers each) that the mouse will move through.
2981    The default path function is Bresenham's line algorithm, which will move
2982    the mouse in a straight line.
2983
2984    (*) If `target_coords_relative` is set: Use absolute mouse movement to move
2985    the mouse cursor to the current mouse position offset by arguments
2986    `x` and `y`.
2987
2988    If `_pause` is True (default), then an automatic sleep will be performed
2989    after the function finshes executing. The duration is set by the global
2990    variable `PAUSE`.
2991
2992    Setting `virtual` to True (default: False) changes the way internal APIs
2993    handle coordinates and is intended for multi monitor systems. It should be
2994    pretty much unncessary even for multi monitor systems, since all the
2995    necessary internal calculations beyond the border of the primay monitor
2996    work without it.
2997
2998    The way that Windows calculates the target pixel coordinates internally
2999    unfortunately leads to inaccuracies and unreachable pixels, especially
3000    if the `virtual` option is used.
3001
3002    If you need the target position to be pixel perfect, you can try setting
3003    `attempt_pixel_perfect` to True, which will use tiny relative movements
3004    to correct the unreachable position.
3005
3006    Relative movement is influenced by mouse speed and Windows Enhanced Pointer
3007    Precision, which can be temporarily disabled by setting
3008    `disable_mouse_acceleration`.
3009
3010    ----------------------------------------------------------------------------
3011    Careful! Disabling mouse acceleration settings is MAYBE thread-safe,
3012    NOT multiprocessing-safe, and DEFINITELY NOT independent processses safe!
3013
3014    If you you start a relative movement while another is already in progress
3015    than the second movement could overwrite the first setting and disable
3016    Enhanced Pointer Precision and change mouse speed.
3017    There are some measures in place to try to mitigate that risk, such as an
3018    internal counter that only allows storing and restoring the acceleration
3019    settings as long as no other movement is currently in progress.
3020    Additionally, the acceleration settings can be manually saved and
3021    restored with `store_mouse_acceleration_settings()` and
3022    `restore_mouse_acceleration_settings()`. For your convenience, the
3023    store function is automatically called during import to save your current
3024    setting. You can then call the restore function at any time.
3025
3026    If all fails, the setting is not written permanently to your Windows
3027    settings, so it should restore itself upon reboot.
3028
3029    Bottom line: Don't use the `disable_mouse_acceleration` argument if you use
3030    this library in multiple threads / processes / programs at the same time!
3031
3032    ----------------------------------------------------------------------------
3033
3034    NOTE: `logScreenshot` is currently unsupported.
3035    """
3036    # TODO: bounding box check for valid position
3037    if tween is None:
3038        tween = _linear
3039    if path_function is None:
3040        path_function = _bresenham
3041    final_x: int
3042    final_y: int
3043    current_x: int = 0
3044    current_y: int = 0
3045    current_x, current_y = position()
3046    if target_coords_relative:
3047        final_x = current_x + (0 if x is None else x)
3048        final_y = current_y + (0 if y is None else y)
3049    else:
3050        # if only x or y is provided, will keep the current position for the
3051        # other axis
3052        final_x, final_y = position(x, y)
3053
3054    dwFlags: int = _MOUSEEVENTF_MOVE | _MOUSEEVENTF_ABSOLUTE
3055    if virtual:
3056        dwFlags |= _MOUSEEVENTF_VIRTUALDESK
3057
3058    if duration <= 0.0:
3059        # no duration -> move mouse instantly
3060        x, y = _to_windows_coordinates(final_x, final_y, virtual=virtual)
3061        _send_input(_create_mouse_input(dx=x, dy=y, dwFlags=dwFlags))
3062
3063    else:
3064        start_time: Final[float] = _time()
3065        final_time: Final[float] = start_time + duration
3066        path: list[tuple[int, int]] = path_function(
3067            current_x, current_y, final_x, final_y
3068        )
3069        path_length = len(path)
3070        keep_looping: bool = True
3071        sleep_duration: float = MINIMUM_SLEEP_IDEAL
3072
3073        apply_duration: bool = False
3074        while keep_looping:
3075            if apply_duration:
3076                _sleep(sleep_duration)  # sleep between iterations
3077            else:
3078                apply_duration = True
3079
3080            _failSafeCheck()
3081
3082            current_time = _time()
3083            if current_time >= final_time:
3084                keep_looping = False
3085                segment_count = path_length - 1
3086            else:
3087                time_ratio = (current_time - start_time) / duration
3088                if time_ratio <= 0.0:
3089                    time_ratio = 0.0
3090                if time_ratio >= 1.0:
3091                    time_ratio = 1.0
3092                path_ratio = tween(time_ratio)
3093                segment_count = int(path_length * path_ratio)
3094                if segment_count >= path_length:
3095                    segment_count = path_length - 1
3096
3097            current_x, current_y = position()
3098            x, y = path[segment_count]
3099
3100            if x == current_x and y == current_y:
3101                # no change in movement for current segment ->try again
3102                continue
3103
3104            x, y = _to_windows_coordinates(x, y, virtual=virtual)
3105            _send_input(_create_mouse_input(dx=x, dy=y, dwFlags=dwFlags))
3106
3107    # After-care: Did Windows move the cursor correctly?
3108    # If not, attempt to fix off-by-one errors.
3109    if attempt_pixel_perfect:
3110        current_x, current_y = position()
3111        if current_x == final_x and current_y == final_y:
3112            return  # We are already pixel perfect, great!
3113        _relative_mouse_move(
3114            x=final_x - current_x,
3115            y=final_y - current_y,
3116            duration=0.0,
3117            target_coords_relative=True,
3118            virtual=virtual,
3119            disable_mouse_acceleration=disable_mouse_acceleration,
3120        )
3121    # --------------------------------------------------------------------------
3122
3123
3124# ----- relative_mouse_move ----------------------------------------------------
3125def _relative_mouse_move(
3126    x: int | None = None,
3127    y: int | None = None,
3128    duration: float = 0.0,
3129    tween: Callable[[float], float] | None = None,
3130    logScreenshot: bool = False,
3131    target_coords_relative: bool = True,
3132    *,
3133    virtual: bool = False,
3134    path_function: PathFunction | None = None,
3135    disable_mouse_acceleration: bool = False,
3136) -> None:
3137    """
3138    Move the mouse a relative amount determined by `x` and `y`.
3139
3140    If `duration` is floating point number greater than 0, then this function
3141    will automatically split the movement into microsteps instead of moving the
3142    complete distance instantly.
3143
3144    `tween` is a function that takes a floating point number between 0.0 and
3145    1.0 and returns another floating point number between 0.0 and 1.0. The
3146    returned number will be used to calculate the next position of the
3147    mouse between the start and the end position based on the current duration.
3148    The default tweening function is linear, which will move the mouse at a
3149    constant speed. See the `pytweening` package for more tweening functions.
3150
3151    `path_function` is a function that takes the start and end coordinates of
3152    the mouse movement (4 integers) and returns a list of coordinates (list of
3153    tuples containting 2 integers each) that the mouse will move through.
3154    The default path function is Bresenham's line algorithm, which will move
3155    the mouse in a straight line.
3156
3157    `target_coords_relative` parameter decides how `x` and `y` are interpreted:
3158
3159    -> `False`: `x` and `y` are assumed to be absolute coordinates
3160    and the offset to move will be calculated based on the current mouse
3161    position. Movement is then performed using relative mouse movements
3162    (can be inconsistent).
3163
3164    -> `True`: `x` and `y` are assumed to be relative coordinates
3165    and the mouse will be moved by that amount. Movement is then performed
3166    using relative mouse movements (can be inconsistent).
3167
3168    The inconsistency issue can be solved by disabling Enhanced Pointer
3169    Precision and set Mouse speed to 10 in Windows mouse settings. Since users
3170    may not want to permanently change their input settings just for this
3171    library, the `disable_mouse_acceleration` argument can be used to
3172    temporarily disable Enhanced Pointer Precision and fix mouse speed at 10
3173    and restore it after the mouse movement.
3174
3175    If `_pause` is True (default), then an automatic sleep will be performed
3176    after the function finshes executing. The duration is set by the global
3177    variable `PAUSE`.
3178
3179    Setting `virtual` to True (default: False) changes the way internal APIs
3180    handle coordinates and is intended for multi monitor systems. It should be
3181    pretty much unncessary even for multi monitor systems, since all the
3182    necessary internal calculations beyond the border of the primay monitor
3183    work without it.
3184
3185    ----------------------------------------------------------------------------
3186    Careful! Disabling mouse acceleration settings is MAYBE thread-safe,
3187    NOT multiprocessing-safe, and DEFINITELY NOT independent processses safe!
3188
3189    If you you start a relative movement while another is already in progress
3190    than the second movement could overwrite the first setting and disable
3191    Enhanced Pointer Precision and change mouse speed.
3192    There are some measures in place to try to mitigate that risk, such as an
3193    internal counter that only allows storing and restoring the acceleration
3194    settings as long as no other movement is currently in progress.
3195    Additionally, the acceleration settings can be manually saved and
3196    restored with `store_mouse_acceleration_settings()` and
3197    `restore_mouse_acceleration_settings()`. For your convinnience, the
3198    store function is automatically called during import to save your current
3199    setting. You can then call the restore function at any time.
3200
3201    If all fails, the setting is not written permanently to your Windows
3202    settings, so it should restore itself upon reboot.
3203
3204    Bottom line: Don't use the `disable_mouse_acceleration` argument if you use
3205    this library in multiple threads / processes / programs at the same time!
3206
3207    ----------------------------------------------------------------------------
3208
3209    NOTE: `logScreenshot` is are currently unsupported.
3210    """
3211    # TODO: bounding box check for valid position
3212    if tween is None:
3213        tween = _linear
3214    if path_function is None:
3215        path_function = _bresenham
3216    x, y = _helper_relative_move_target_coords(x, y, target_coords_relative)
3217
3218    if duration <= 0.0:
3219        # no duration -> move mouse instantly
3220        _helper_relative_mouse_move(x, y, disable_mouse_acceleration)
3221        return
3222
3223    current_x: int = 0
3224    current_y: int = 0
3225
3226    next_x: int
3227    next_y: int
3228
3229    final_x: int = x
3230    final_y: int = y
3231
3232    start_time: Final[float] = _time()
3233    final_time: Final[float] = start_time + duration
3234
3235    path: list[tuple[int, int]] = path_function(
3236        current_x, current_y, final_x, final_y
3237    )
3238    path_length: int = len(path)
3239
3240    keep_looping: bool = True
3241    sleep_duration: float = MINIMUM_SLEEP_IDEAL
3242
3243    apply_duration: bool = False
3244    while keep_looping:
3245        if apply_duration:
3246            _sleep(sleep_duration)  # sleep between iterations
3247        else:
3248            apply_duration = True
3249
3250        _failSafeCheck()
3251
3252        current_time: float = _time()
3253        if current_time >= final_time:
3254            keep_looping = False
3255            segment_count: int = path_length - 1
3256        else:
3257            time_ratio: float = (current_time - start_time) / duration
3258            if time_ratio <= 0.0:
3259                time_ratio = 0.0
3260            if time_ratio >= 1.0:
3261                time_ratio = 1.0
3262            path_ratio: float = tween(time_ratio)
3263            segment_count = int(path_length * path_ratio)
3264            if segment_count >= path_length:
3265                segment_count = path_length - 1
3266
3267        next_x, next_y = path[segment_count]
3268        x = next_x - current_x
3269        y = next_y - current_y
3270
3271        if x == 0 and y == 0:
3272            # no change in movement for current segment ->try again
3273            continue
3274
3275        current_x = next_x
3276        current_y = next_y
3277
3278        _helper_relative_mouse_move(x, y, disable_mouse_acceleration)
3279    # --------------------------------------------------------------------------
3280
3281
3282# ----- helper_relative_move_target_coords -------------------------------------
3283def _helper_relative_move_target_coords(
3284    x: int | None,
3285    y: int | None,
3286    target_coords_relative: bool,
3287) -> tuple[int, int]:
3288    """
3289    Calculate target coordinates for relative mouse movement.
3290    """
3291    if target_coords_relative:
3292        if x is None:
3293            x = 0
3294        if y is None:
3295            y = 0
3296    else:
3297        if x is None and y is None:
3298            # Prevent unnecessary API calls
3299            return 0, 0
3300        current_x: int
3301        current_y: int
3302        current_x, current_y = position()
3303        if x is None:
3304            x = 0
3305        else:
3306            x = x - current_x
3307        if y is None:
3308            y = 0
3309        else:
3310            y = y - current_y
3311    return x, y
3312    # --------------------------------------------------------------------------
3313
3314
3315# ----- helper_relative_mouse_move ---------------------------------------------
3316def _helper_relative_mouse_move(
3317    x: int,
3318    y: int,
3319    disable_mouse_acceleration: bool,
3320) -> None:
3321    """
3322    When using MOUSEEVENTF_MOVE for relative movement the results may
3323    be inconsistent. "Relative mouse motion is subject to the effects
3324    of the mouse speed and the two-mouse threshold values. A user
3325    sets these three values with the Pointer Speed slider of the
3326    Control Panel's Mouse Properties sheet. You can obtain and set
3327    these values using the SystemParametersInfo function."
3328
3329    https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput
3330    https://stackoverflow.com/questions/50601200/pyhon-directinput-mouse-relative-moving-act-not-as-expected
3331
3332    We can solve this issue by just disabling Enhanced Pointer
3333    Precision and forcing Mouse speed to neutral 10.
3334    Since that is a user setting that users may want to have
3335    enabled, use a optional keyword-only argument and a
3336    state-restoring context manager to give users the choice if they
3337    want this library messing around in their Windows settings.
3338    """
3339    input_struct: _INPUT = _create_mouse_input(
3340        dx=x, dy=y, dwFlags=_MOUSEEVENTF_MOVE
3341    )
3342    if disable_mouse_acceleration:
3343        # Use a context manager to temporarily disable enhanced pointer
3344        # precision
3345        with _no_mouse_acceleration():
3346            _send_input(input_struct)
3347    else:
3348        _send_input(input_struct)
3349    # --------------------------------------------------------------------------
3350
3351
3352# ----- moveTo -----------------------------------------------------------------
3353@_genericPyDirectInputChecks
3354def moveTo(
3355    x: int | None = None,
3356    y: int | None = None,
3357    duration: float = 0.0,
3358    tween: Callable[[float], float] | None = None,
3359    logScreenshot: bool = False,
3360    _pause: bool = True,
3361    relative: bool = False,
3362    *,
3363    virtual: bool = False,
3364    path_function: PathFunction | None = None,
3365    attempt_pixel_perfect: bool = False,
3366    disable_mouse_acceleration: bool = False,
3367) -> None:
3368    """
3369    Move the mouse to an absolute(*) postion indicated by the arguments of
3370    `x` and `y`. The coordinates 0,0 represent the top left pixel of the
3371    primary monitor.
3372
3373    If `duration` is floating point number greater than 0, then this function
3374    will automatically split the movement into microsteps instead of moving
3375    straight to the target position.
3376
3377    `tween` is a function that takes a floating point number between 0.0 and
3378    1.0 and returns another floating point number between 0.0 and 1.0. The
3379    returned number will be used to calculate the next position of the
3380    mouse between the start and the end position based on the current duration.
3381    The default tweening function is linear, which will move the mouse at a
3382    constant speed. See the `pytweening` package for more tweening functions.
3383    Be aware that tweening only controls to the timing of movement, not the
3384    path itself. If you need to non-linear movement (e.g. overshoot), then
3385    use the `path_function` argument.
3386
3387    `path_function` is a function that takes the start and end coordinates of
3388    the mouse movement (4 integers) and returns a list of coordinates (list of
3389    tuples, each containting 2 integers (x,y)) that the mouse will move 
3390    through. The default path function is Bresenham's line algorithm, which 
3391    will move the mouse in a straight line.
3392
3393    (*) `relative` parameter decides how the movement is executed:
3394
3395    -> `False`: Target postion is given and absolute movement is used.
3396
3397    -> `True`: Calculates target offset and uses relative movement API
3398    (can be inconsistent)
3399
3400    If `_pause` is True (default), then an automatic sleep will be performed
3401    after the function finshes executing. The duration is set by the global
3402    variable `PAUSE`.
3403
3404    Setting `virtual` to True (default: False) changes the way internal APIs
3405    handle coordinates and is intended for multi monitor systems. It should be
3406    pretty much unncessary even for multi monitor systems, since all the
3407    necessary internal calculations beyond the border of the primay monitor
3408    work without it.
3409
3410    The way that Windows calculates the target pixel coordinates internally
3411    unfortunately leads to inaccuracies and unreachable pixels, especially
3412    if the `virtual` option is used.
3413
3414    If you need the target position to be pixel perfect, you can try setting
3415    `attempt_pixel_perfect` to True, which will use tiny relative movements
3416    to correct the unreachable position.
3417
3418    Relative movement is influenced by mouse speed and Windows Enhanced Pointer
3419    Precision, which can be temporarily disabled by setting
3420    `disable_mouse_acceleration`.
3421
3422    ----------------------------------------------------------------------------
3423    Careful! Disabling mouse acceleration settings is MAYBE thread-safe,
3424    NOT multiprocessing-safe, and DEFINITELY NOT independent processses safe!
3425
3426    If you you start a relative movement while another is already in progress
3427    than the second movement could overwrite the first setting and disable
3428    Enhanced Pointer Precision and change mouse speed.
3429    There are some measures in place to try to mitigate that risk, such as an
3430    internal counter that only allows storing and restoring the acceleration
3431    settings as long as no other movement is currently in progress.
3432    Additionally, the acceleration settings can be manually saved and
3433    restored with `store_mouse_acceleration_settings()` and
3434    `restore_mouse_acceleration_settings()`. For your convenience, the
3435    store function is automatically called during import to save your current
3436    setting. You can then call the restore function at any time.
3437
3438    If all fails, the setting is not written permanently to your Windows
3439    settings, so it should restore itself upon reboot.
3440
3441    Bottom line: Don't use the `disable_mouse_acceleration` argument if you use
3442    this library in multiple threads / processes / programs at the same time!
3443
3444    ----------------------------------------------------------------------------
3445
3446    NOTE: `logScreenshot` is currently unsupported.
3447    """
3448    if relative:
3449        _relative_mouse_move(
3450            x=x,
3451            y=y,
3452            duration=duration,
3453            tween=tween,
3454            logScreenshot=logScreenshot,
3455            target_coords_relative=False,
3456            virtual=virtual,
3457            path_function=path_function,
3458            disable_mouse_acceleration=disable_mouse_acceleration,
3459        )
3460    else:
3461        _absolute_mouse_move(
3462            x=x,
3463            y=y,
3464            duration=duration,
3465            tween=tween,
3466            logScreenshot=logScreenshot,
3467            target_coords_relative=False,
3468            virtual=virtual,
3469            path_function=path_function,
3470            attempt_pixel_perfect=attempt_pixel_perfect,
3471            disable_mouse_acceleration=disable_mouse_acceleration,
3472        )
3473    # --------------------------------------------------------------------------
3474
3475
3476# ----- moveRel ----------------------------------------------------------------
3477@_genericPyDirectInputChecks
3478def moveRel(
3479    xOffset: int | None = None,
3480    yOffset: int | None = None,
3481    duration: float = 0.0,
3482    tween: Callable[[float], float] | None = None,
3483    logScreenshot: bool = False,
3484    _pause: bool = True,
3485    relative: bool = False,
3486    *,
3487    virtual: bool = False,
3488    path_function: PathFunction | None = None,
3489    attempt_pixel_perfect: bool = False,
3490    disable_mouse_acceleration: bool = False,
3491) -> None:
3492    """
3493    Move the mouse a relative amount determined by `xOffset` and `yOffset`.
3494
3495    If `duration` is floating point number greater than 0, then this function
3496    will automatically split the movement into microsteps instead of moving the
3497    complete distance instantly.
3498
3499    `tween` is a function that takes a floating point number between 0.0 and
3500    1.0 and returns another floating point number between 0.0 and 1.0. The
3501    returned number will be used to calculate the next position of the
3502    mouse between the start and the end position based on the current duration.
3503    The default tweening function is linear, which will move the mouse at a
3504    constant speed. See the `pytweening` package for more tweening functions.
3505    Be aware that tweening only controls to the timing of movement, not the
3506    path itself. If you need to non-linear movement (e.g. overshoot), then
3507    use the `path_function` argument.
3508
3509    `path_function` is a function that takes the start and end coordinates of
3510    the mouse movement (4 integers) and returns a list of coordinates (list of
3511    tuples, each containting 2 integers (x,y)) that the mouse will move 
3512    through. The default path function is Bresenham's line algorithm, which 
3513    will move the mouse in a straight line.
3514
3515    `relative` parameter decides how the movement is executed:
3516
3517    -> `False`: Target postion is calculated and absolute movement is used.
3518
3519    -> `True`: Target offset is given and relative movement API is used
3520    (can be inconsistent)
3521
3522    The inconsistency issue can be solved by disabling Enhanced Pointer
3523    Precision and set Mouse speed to 10 in Windows mouse settings. Since users
3524    may not want to permanently change their input settings just for this
3525    library, the `disable_mouse_acceleration` argument can be used to
3526    temporarily disable Enhanced Pointer Precision and fix mouse speed at 10
3527    and restore it after the mouse movement.
3528
3529    If `_pause` is True (default), then an automatic sleep will be performed
3530    after the function finshes executing. The duration is set by the global
3531    variable `PAUSE`.
3532
3533    Setting `virtual` to True (default: False) changes the way internal APIs
3534    handle coordinates and is intended for multi monitor systems. It should be
3535    pretty much unncessary even for multi monitor systems, since all the
3536    necessary internal calculations beyond the border of the primay monitor
3537    work without it.
3538
3539    The way that Windows calculates the target pixel coordinates internally
3540    unfortunately leads to inaccuracies and unreachable pixels, especially
3541    if the `virtual` option is used.
3542
3543    If you need the target position to be pixel perfect, you can try setting
3544    `attempt_pixel_perfect` to True, which will use tiny relative movements
3545    to correct the unreachable position.
3546
3547    ----------------------------------------------------------------------------
3548    Careful! Disabling mouse acceleration settings is MAYBE thread-safe,
3549    NOT multiprocessing-safe, and DEFINITELY NOT independent processses safe!
3550
3551    If you you start a relative movement while another is already in progress
3552    than the second movement could overwrite the first setting and disable
3553    Enhanced Pointer Precision and change mouse speed.
3554    There are some measures in place to try to mitigate that risk, such as an
3555    internal counter that only allows storing and restoring the acceleration
3556    settings as long as no other movement is currently in progress.
3557    Additionally, the acceleration settings can be manually saved and
3558    restored with `store_mouse_acceleration_settings()` and
3559    `restore_mouse_acceleration_settings()`. For your convinnience, the
3560    store function is automatically called during import to save your current
3561    setting. You can then call the restore function at any time.
3562
3563    If all fails, the setting is not written permanently to your Windows
3564    settings, so it should restore itself upon reboot.
3565
3566    Bottom line: Don't use the `disable_mouse_acceleration` argument if you use
3567    this library in multiple threads / processes / programs at the same time!
3568
3569    ----------------------------------------------------------------------------
3570
3571    NOTE: `logScreenshot` is are currently unsupported.
3572    """
3573    if relative:
3574        _relative_mouse_move(
3575            x=xOffset,
3576            y=yOffset,
3577            duration=duration,
3578            tween=tween,
3579            logScreenshot=logScreenshot,
3580            target_coords_relative=True,
3581            virtual=virtual,
3582            path_function=path_function,
3583            disable_mouse_acceleration=disable_mouse_acceleration,
3584        )
3585    else:
3586        _absolute_mouse_move(
3587            x=xOffset,
3588            y=yOffset,
3589            duration=duration,
3590            tween=tween,
3591            logScreenshot=logScreenshot,
3592            target_coords_relative=True,
3593            virtual=virtual,
3594            path_function=path_function,
3595            attempt_pixel_perfect=attempt_pixel_perfect,
3596            disable_mouse_acceleration=disable_mouse_acceleration,
3597        )
3598    # --------------------------------------------------------------------------
3599
3600
3601# ----- move alias -------------------------------------------------------------
3602# move() and moveRel() are equivalent.
3603move = moveRel
3604# ------------------------------------------------------------------------------
3605
3606
3607# ----- dragTo -----------------------------------------------------------------
3608@_genericPyDirectInputChecks
3609def dragTo(
3610    x: int | None = None,
3611    y: int | None = None,
3612    duration: float = 0.0,
3613    tween: Callable[[float], float] | None = None,
3614    button: str | None = None,
3615    logScreenshot: bool = False,
3616    _pause: bool = True,
3617    mouseDownUp: bool = True,
3618    *,
3619    relative: bool = False,
3620    virtual: bool = False,
3621    path_function: PathFunction | None = None,
3622    attempt_pixel_perfect: bool = False,
3623    disable_mouse_acceleration: bool = False,
3624) -> None:
3625    """
3626    Press and hold a mouse button while moving to the target coordinates.
3627
3628    See `moveTo` for more information on most arguments.
3629
3630    `button` is a string that is one of the following constants:
3631    MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE, MOUSE_BUTTON4, MOUSE_BUTTON5,
3632    MOUSE_PRIMARY (default), MOUSE_SECONDARY.
3633
3634    If `mouseDownUp` (default: True) is manually set to False, then this
3635    function is basically the same as `moveTo`. Only exists to match PyAutoGUI.
3636
3637    If `_pause` is True (default), then an automatic sleep will be performed
3638    after the function finshes executing. The duration is set by the global
3639    variable `PAUSE`.
3640
3641    ----------------------------------------------------------------------------
3642
3643    NOTE: `logScreenshot` is currently unsupported.
3644    """
3645    # TODO: bounding box check for valid position
3646    if button is None:
3647        button = MOUSE_PRIMARY
3648    if mouseDownUp:
3649        mouseDown(button=button, _pause=False, virtual=virtual)
3650    moveTo(
3651        x,
3652        y,
3653        duration=duration,
3654        tween=tween,
3655        logScreenshot=logScreenshot,
3656        _pause=False,  # don't add an additional pause
3657        relative=relative,
3658        virtual=virtual,
3659        path_function=path_function,
3660        attempt_pixel_perfect=attempt_pixel_perfect,
3661        disable_mouse_acceleration=disable_mouse_acceleration,
3662    )
3663    if mouseDownUp:
3664        mouseUp(button=button, _pause=False, virtual=virtual)
3665    # --------------------------------------------------------------------------
3666
3667
3668# ----- dragRel ----------------------------------------------------------------
3669@_genericPyDirectInputChecks
3670def dragRel(
3671    xOffset: int | None = None,
3672    yOffset: int | None = None,
3673    duration: float = 0.0,
3674    tween: Callable[[float], float] | None = None,
3675    button: str | None = None,
3676    logScreenshot: bool = False,
3677    _pause: bool = True,
3678    mouseDownUp: bool = True,
3679    *,
3680    relative: bool = False,
3681    virtual: bool = False,
3682    path_function: PathFunction | None = None,
3683    disable_mouse_acceleration: bool = False,
3684) -> None:
3685    """
3686    Press and hold a mouse button while moving a relative distance
3687
3688    See `moveRel` for more information on most arguments.
3689
3690    `button` is a string that is one of the following constants:
3691    MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE, MOUSE_BUTTON4, MOUSE_BUTTON5,
3692    MOUSE_PRIMARY (default), MOUSE_SECONDARY.
3693
3694    If `mouseDownUp` (default: True) is manually set to False, then this
3695    function is basically the same as `moveTo`. Only exists to match PyAutoGUI.
3696
3697    If `_pause` is True (default), then an automatic sleep will be performed
3698    after the function finshes executing. The duration is set by the global
3699    variable `PAUSE`.
3700
3701    ----------------------------------------------------------------------------
3702
3703    NOTE: `logScreenshot` is currently unsupported.
3704    """
3705    # TODO: bounding box check for valid position
3706    if button is None:
3707        button = MOUSE_PRIMARY
3708    if mouseDownUp:
3709        mouseDown(button=button, _pause=False, virtual=virtual)
3710    moveRel(
3711        xOffset,
3712        yOffset,
3713        duration=duration,
3714        tween=tween,
3715        logScreenshot=logScreenshot,
3716        _pause=False,  # don't add an additional pause
3717        relative=relative,
3718        virtual=virtual,
3719        path_function=path_function,
3720        disable_mouse_acceleration=disable_mouse_acceleration,
3721    )
3722    if mouseDownUp:
3723        mouseUp(button=button, _pause=False, virtual=virtual)
3724    # --------------------------------------------------------------------------
3725
3726
3727# ----- drag alias -------------------------------------------------------------
3728drag = dragRel
3729# ------------------------------------------------------------------------------
3730
3731
3732# ==============================================================================
3733# ===== Keyboard Functions =====================================================
3734# ==============================================================================
3735
3736
3737# ----- is_valid_key -------------------------------------------------------------
3738def is_valid_key(key: str) -> bool:
3739    """
3740    Returns true if key name `key` can be translated into a valid scan code.
3741    """
3742    return key in KEYBOARD_MAPPING
3743
3744
3745isValidKey = is_valid_key
3746# ------------------------------------------------------------------------------
3747
3748
3749# ===== scancode functions =====================================================
3750
3751
3752# ----- scancode_keyDown -------------------------------------------------------
3753@_genericPyDirectInputChecks
3754def scancode_keyDown(
3755    scancodes: ScancodeTypes,
3756    logScreenshot: None = None,
3757    _pause: bool = True,
3758    *,
3759    auto_shift: bool = False,
3760) -> bool:
3761    """
3762    Press down key corresponding to `scancodes`.
3763
3764    The actually pressed key will depend on your system keyboard layout.
3765    Limits the available character set but should provide the best
3766    compatibility.
3767
3768    If `_pause` is True (default), then an automatic sleep will be performed
3769    after the function finshes executing. The duration is set by the global
3770    variable `PAUSE`.
3771
3772    `auto_shift` is used internally by higher level functions to automatically
3773    press the shift key before supported scancodes (indicitated by a special
3774    bit outside the regular scancode range, while it technically can be used,
3775    it's not intended for public access).
3776
3777    ----------------------------------------------------------------------------
3778
3779    NOTE: `logScreenshot` is currently unsupported.
3780    """
3781    scancodes_sequence: ScancodeSequence
3782    if isinstance(scancodes, int):
3783        scancodes_sequence = ScancodeSequence([scancodes])
3784    else:
3785        scancodes_sequence = scancodes
3786
3787    keybdFlags: int = _KEYEVENTF_SCANCODE
3788    input_structs: list[_INPUT] = []
3789    extendedFlag: int
3790
3791    # Init event tracking
3792    insertedEvents: int = 0
3793    expectedEvents: int = 0
3794
3795    for scancode in scancodes_sequence:
3796        if auto_shift and scancode & _OFFSET_SHIFTKEY:
3797            input_structs += [
3798                _create_keyboard_input(
3799                    wScan=_SHIFT_SCANCODE, dwFlags=keybdFlags
3800                )
3801            ]
3802            expectedEvents += 1
3803
3804        scancode = scancode & 0xFFFF
3805
3806        extendedFlag = _KEYEVENTF_EXTENDEDKEY if scancode >= 0xE000 else 0
3807        input_structs += [
3808            _create_keyboard_input(
3809                wScan=scancode, dwFlags=keybdFlags | extendedFlag
3810            )
3811        ]
3812        expectedEvents += 1
3813
3814    insertedEvents += _send_input(input_structs)
3815
3816    # SendInput returns the number of event successfully inserted into
3817    # input stream
3818    # https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput#return-value
3819    return insertedEvents == expectedEvents
3820    # --------------------------------------------------------------------------
3821
3822
3823# ----- scancode_keyUp ---------------------------------------------------------
3824@_genericPyDirectInputChecks
3825def scancode_keyUp(
3826    scancodes: ScancodeTypes,
3827    logScreenshot: None = None,
3828    _pause: bool = True,
3829    *,
3830    auto_shift: bool = False,
3831) -> bool:
3832    """
3833    Release key corresponding to `scancodes`.
3834
3835    The actually pressed key will depend on your system keyboard layout.
3836    Limits the available character set but should provide the best
3837    compatibility.
3838
3839    If `_pause` is True (default), then an automatic sleep will be performed
3840    after the function finshes executing. The duration is set by the global
3841    variable `PAUSE`.
3842
3843    `auto_shift` is used internally by higher level functions to automatically
3844    press the shift key before supported scancodes (indicitated by a special
3845    bit outside the regular scancode range, while it technically can be used,
3846    it's not intended for public access).
3847
3848    ----------------------------------------------------------------------------
3849
3850    NOTE: `logScreenshot` is currently unsupported.
3851    """
3852    scancodes_sequence: ScancodeSequence
3853    if isinstance(scancodes, int):
3854        scancodes_sequence = ScancodeSequence([scancodes])
3855    else:
3856        scancodes_sequence = scancodes
3857
3858    keybdFlags: int = _KEYEVENTF_SCANCODE | _KEYEVENTF_KEYUP
3859    input_structs: list[_INPUT] = []
3860    extendedFlag: int
3861
3862    # Init event tracking
3863    insertedEvents: int = 0
3864    expectedEvents: int = 0
3865
3866    for scancode in scancodes_sequence:
3867        if auto_shift and scancode & _OFFSET_SHIFTKEY:
3868            input_structs += [
3869                _create_keyboard_input(
3870                    wScan=_SHIFT_SCANCODE, dwFlags=keybdFlags
3871                )
3872            ]
3873            expectedEvents += 1
3874
3875        scancode = scancode & 0xFFFF
3876
3877        extendedFlag = _KEYEVENTF_EXTENDEDKEY if scancode >= 0xE000 else 0
3878        input_structs += [
3879            _create_keyboard_input(
3880                wScan=scancode & 0xFFFF, dwFlags=keybdFlags | extendedFlag
3881            )
3882        ]
3883        expectedEvents += 1
3884
3885    insertedEvents += _send_input(input_structs)
3886    return insertedEvents == expectedEvents
3887    # --------------------------------------------------------------------------
3888
3889
3890# ----- _helper_scancode_press -------------------------------------------------
3891def _helper_scancode_press(
3892    scancodes: ScancodeTypes,
3893    duration: float = 0.0,
3894    _pause: bool = True,
3895    auto_shift: bool = False,
3896) -> bool:
3897    """
3898    Press `scancode`, wait for `duration` seconds, release `scancode`.
3899
3900    Return `True` if complete press was successful.
3901    """
3902    downed: bool = scancode_keyDown(
3903        scancodes, _pause=_pause, auto_shift=auto_shift
3904    )
3905    _sleep(duration)
3906    upped: bool = scancode_keyUp(
3907        scancodes, _pause=_pause, auto_shift=auto_shift
3908    )
3909    # Count key press as complete if key was "downed" and "upped"
3910    # successfully
3911    return bool(downed and upped)
3912    # --------------------------------------------------------------------------
3913
3914
3915# ----- scancode_press ---------------------------------------------------------
3916# Ignored parameters: logScreenshot
3917@_genericPyDirectInputChecks
3918def scancode_press(
3919    scancodes: ScancodeTypes | Sequence[ScancodeTypes],
3920    presses: int = 1,
3921    interval: float = 0.0,
3922    logScreenshot: None = None,
3923    _pause: bool = True,
3924    *,
3925    auto_shift: bool = False,
3926    delay: float = 0.0,
3927    duration: float = 0.0,
3928) -> bool:
3929    """
3930    Press the sequence of `keys` for `presses` amount of times.
3931
3932    The actually pressed key will depend on your system keyboard layout.
3933    Limits the available character set but should provide the best
3934    compatibility.
3935
3936    Explanation of time parameters (seconds as floating point numbers):
3937
3938    - `interval` is the time spent waiting between sequences. If `keys` is a
3939    str instance or single element list, then `interval` will be ignored.
3940    - `delay` is the time from one complete key (press+release) to the next one
3941    in the same sequence. If there is only a single key in a sequence, then
3942    `delay` will be ignored.
3943    - `duration` is the time spent on holding every key before releasing it
3944    again.
3945
3946    If `_pause` is True (default), then an automatic sleep will be performed
3947    after the function finshes executing. The duration is set by the global
3948    variable `PAUSE`.
3949    Be aware, that the global pause defined by the PAUSE `constant` only
3950    applies after every call to this function, not inbetween (no extra pause
3951    between pressing and releasing key, use the `duration` argument instead)!
3952
3953    `auto_shift` is used internally by higher level functions to automatically
3954    press the shift key before supported scancodes (indicitated by a special
3955    bit outside the regular scancode range, while it technically can be used,
3956    it's not intended for public access).
3957
3958    ----------------------------------------------------------------------------
3959
3960    NOTE: `logScreenshot` is currently unsupported.
3961    """
3962    scancodes_sequence: Sequence[ScancodeTypes]
3963    if isinstance(scancodes, int):
3964        scancodes_sequence = [ScancodeSequence([scancodes])]
3965    elif isinstance(scancodes, ScancodeSequence):
3966        scancodes_sequence = [scancodes]
3967    else:
3968        scancodes_sequence = scancodes
3969
3970    # We need to press x keys y times, which comes out to x*y presses in total
3971    expectedPresses: int = presses * len(scancodes_sequence)
3972    completedPresses: int = 0
3973
3974    apply_interval: bool = False
3975    for _ in range(presses):
3976        if apply_interval:  # Don't delay first press
3977            _sleep(interval)
3978        apply_interval = True
3979
3980        apply_delay: bool = False
3981        for c in scancodes_sequence:
3982            if apply_delay:  # Don't delay first press
3983                _sleep(delay)
3984            apply_delay = True
3985
3986            completedPresses += _helper_scancode_press(
3987                c, duration, _pause=False, auto_shift=auto_shift
3988            )
3989
3990    return completedPresses == expectedPresses
3991    # --------------------------------------------------------------------------
3992
3993
3994# ----- scancode_hold ----------------------------------------------------------
3995@contextmanager
3996@_genericPyDirectInputChecks
3997def scancode_hold(
3998    scancodes: ScancodeTypes | Sequence[ScancodeTypes],
3999    logScreenshot: None = None,
4000    _pause: bool = True,
4001    *,
4002    auto_shift: bool = False,
4003    raise_on_failure: bool = False,
4004) -> Generator[None, None, None]:
4005    """
4006    Hold the sequence of keys corresponding to `scancodes` as long as the
4007    context manager is in scope (press upon entry, release upon exit).
4008
4009    Keys will be released in reverse order (LIFO), but still practically
4010    instantenous.
4011
4012    The actually pressed key will depend on your system keyboard layout.
4013    Limits the available character set but should provide the best
4014    compatibility.
4015
4016    If `_pause` is True (default), then an automatic sleep will be performed
4017    after the function finshes executing. The duration is set by the global
4018    variable `PAUSE`.
4019    Be aware, that the global pause defined by the PAUSE `constant` only
4020    applies after every call to this function, not inbetween (no pause between
4021    press and releasing key)!
4022
4023    `auto_shift` is used internally by higher level functions to automatically
4024    press the shift key before supported scancodes (indicitated by a special
4025    bit outside the regular scancode range, while it technically can be used,
4026    it's not intended for public access).
4027
4028    If `raise_on_failure` is True, then `PriorInputFailedException` will be
4029    raised if not all keyboard inputs could be executed successfully.
4030
4031    ----------------------------------------------------------------------------
4032
4033    NOTE: `logScreenshot` is currently unsupported.
4034    """
4035    scancodes_sequence: Sequence[ScancodeTypes]
4036    if isinstance(scancodes, int):
4037        scancodes_sequence = [ScancodeSequence([scancodes])]
4038    elif isinstance(scancodes, ScancodeSequence):
4039        scancodes_sequence = [scancodes]
4040    else:
4041        scancodes_sequence = scancodes
4042
4043    expectedPresses: int = len(scancodes_sequence)
4044    downed: int = 0
4045    upped: int = 0
4046
4047    try:
4048        for c in scancodes_sequence:
4049            downed += scancode_keyDown(c, _pause=False, auto_shift=auto_shift)
4050        yield
4051    finally:
4052        for c in reversed(scancodes_sequence):
4053            upped += scancode_keyUp(c, _pause=False, auto_shift=auto_shift)
4054        if raise_on_failure and not (expectedPresses == downed == upped):
4055            raise PriorInputFailedException
4056    # --------------------------------------------------------------------------
4057
4058
4059# ----- scancode_hotkey --------------------------------------------------------
4060@_genericPyDirectInputChecks
4061def scancode_hotkey(
4062    *args: ScancodeTypes,
4063    interval: float = 0.0,
4064    wait: float = 0.0,
4065    logScreenshot: None = None,
4066    _pause: bool = True,
4067    auto_shift: bool = True,
4068) -> bool:
4069    """
4070    Press down buttons in order they are specified as arguments,
4071    releasing them in reverse order, e.g. 0x1D, 0x2E will first press
4072    Control, then C and release C before releasing Control.
4073
4074    Use keyword-only argument `interval` to specify a delay between single
4075    keys when pressing and releasing and `wait` for delay between last press
4076    and first release.
4077
4078    If `_pause` is True (default), then an automatic sleep will be performed
4079    after the function finshes executing. The duration is set by the global
4080    variable `PAUSE`.
4081    Be aware, that the global pause defined by the PAUSE `constant` only
4082    applies after every call to this function, not inbetween (no pause between
4083    press and releasing key)!
4084
4085    `auto_shift` is used internally by higher level functions to automatically
4086    press the shift key before supported scancodes (indicitated by a special
4087    bit outside the regular scancode range, while it technically can be used,
4088    it's not intended for public access).
4089
4090    ----------------------------------------------------------------------------
4091
4092    NOTE: `logScreenshot` is currently unsupported.
4093    """
4094    expectedPresses: int = len(args)
4095    downed: int = 0
4096    upped: int = 0
4097
4098    apply_interval: bool = False
4099    for code in args:
4100        if apply_interval:
4101            _sleep(interval)  # sleep between iterations
4102        apply_interval = True
4103
4104        downed += scancode_keyDown(code, _pause=False, auto_shift=auto_shift)
4105
4106    _sleep(wait)
4107
4108    apply_interval = False
4109    for code in reversed(args):
4110        if apply_interval:
4111            _sleep(interval)  # sleep between iterations
4112        apply_interval = True
4113
4114        upped += scancode_keyUp(code, _pause=False, auto_shift=auto_shift)
4115
4116    return expectedPresses == downed == upped
4117    # --------------------------------------------------------------------------
4118
4119
4120# ===== keyname functions ======================================================
4121
4122
4123# ----- keyDown ----------------------------------------------------------------
4124# Ignored parameters: logScreenshot
4125def keyDown(
4126    key: str,
4127    logScreenshot: None = None,
4128    _pause: bool = True,
4129    *,
4130    auto_shift: bool = False,
4131) -> bool:
4132    """
4133    Press down key corresponding to key name `key`.
4134
4135    `key` will be interpreted as a keyboard key (US QWERTY).
4136    The actually pressed key will depend on your system keyboard layout.
4137    Limits the available character set but should provide the best
4138    compatibility.
4139
4140    If `_pause` is True (default), then an automatic sleep will be performed
4141    after the function finshes executing. The duration is set by the global
4142    variable `PAUSE`.
4143
4144    If `auto_shift` is True, then "shifted" characters like upper case letters
4145    and the symbols on the number row automatically insert a Shift scancode
4146    into the input sequence.
4147
4148    ----------------------------------------------------------------------------
4149
4150    NOTE: `logScreenshot` is currently unsupported.
4151    """
4152    scancode: ScancodeTypes | None = KEYBOARD_MAPPING.get(key)
4153    if scancode is None:
4154        return False
4155    return scancode_keyDown(
4156        scancode, logScreenshot, _pause=_pause, auto_shift=auto_shift
4157    )
4158    # --------------------------------------------------------------------------
4159
4160
4161# ----- keyUp ------------------------------------------------------------------
4162# Ignored parameters: logScreenshot
4163def keyUp(
4164    key: str,
4165    logScreenshot: None = None,
4166    _pause: bool = True,
4167    *,
4168    auto_shift: bool = False,
4169) -> bool:
4170    """
4171    Lift up key corresponding to key name `key`.
4172
4173    `key` will be interpreted as a keyboard key (US QWERTY).
4174    The actually lifted key will depend on your system keyboard layout.
4175    Limits the available character set but should provide the best
4176    compatibility.
4177
4178    If `_pause` is True (default), then an automatic sleep will be performed
4179    after the function finshes executing. The duration is set by the global
4180    variable `PAUSE`.
4181
4182    If `auto_shift` is True, then "shifted" characters like upper case letters
4183    and the symbols on the number row automatically insert a Shift scancode
4184    into the input sequence.
4185
4186    ----------------------------------------------------------------------------
4187
4188    NOTE: `logScreenshot` is currently unsupported.
4189    """
4190    scancode: ScancodeTypes | None = KEYBOARD_MAPPING.get(key)
4191    if scancode is None:
4192        return False
4193    return scancode_keyUp(
4194        scancode, logScreenshot, _pause=_pause, auto_shift=auto_shift
4195    )
4196    # --------------------------------------------------------------------------
4197
4198
4199# ----- _helper_press ----------------------------------------------------------
4200def _helper_press(
4201    key: str,
4202    duration: float = 0.0,
4203    _pause: bool = True,
4204    auto_shift: bool = False,
4205) -> bool:
4206    """
4207    Press `key`, wait for `duration` seconds, release `key`.
4208
4209    Return `True` if complete press was successful.
4210    """
4211    downed: bool = keyDown(key, _pause=_pause, auto_shift=auto_shift)
4212    _sleep(duration)
4213    upped: bool = keyUp(key, _pause=_pause, auto_shift=auto_shift)
4214    # Count key press as complete if key was "downed" and "upped"
4215    # successfully
4216    return bool(downed and upped)
4217    # --------------------------------------------------------------------------
4218
4219
4220# ----- press ------------------------------------------------------------------
4221# Ignored parameters: logScreenshot
4222@_genericPyDirectInputChecks
4223def press(
4224    keys: str | Sequence[str],
4225    presses: int = 1,
4226    interval: float = 0.0,
4227    logScreenshot: None = None,
4228    _pause: bool = True,
4229    *,
4230    auto_shift: bool = False,
4231    delay: float = 0.0,
4232    duration: float = 0.0,
4233) -> bool:
4234    """
4235    Press the sequence of `keys` for `presses` amount of times.
4236
4237    `keys` will be interpreted as sequence of keyboard keys (US QWERTY).
4238    The actually pressed key will depend on your system keyboard layout.
4239    Limits the available character set but should provide the best
4240    compatibility.
4241
4242    Explanation of time parameters (seconds as floating point numbers):
4243
4244    - `interval` is the time spent waiting between sequences. If `keys` is a
4245    str instance, single element list or presses equals 1 (the default),
4246    then `interval` will be ignored.
4247    - `delay` is the time from one complete key (press+release) to the next one
4248    in the same sequence. If there is only a single key in a sequence, then
4249    `delay` will be ignored.
4250    - `duration` is the time spent on holding every key before releasing it
4251    again.
4252
4253    If `_pause` is True (default), then an automatic sleep will be performed
4254    after the function finshes executing. The duration is set by the global
4255    variable `PAUSE`.
4256    Be aware, that the global pause defined by the `PAUSE` var only applies
4257    after every call to this function, not inbetween (no extra pause between
4258    pressing and releasing key, use the `duration` argument instead)!
4259
4260    If `auto_shift` is True, then "shifted" characters like upper case letters
4261    and the symbols on the number row automatically insert a Shift scancode
4262    into the input sequence.
4263
4264    ----------------------------------------------------------------------------
4265
4266    NOTE: `logScreenshot` is currently unsupported.
4267    """
4268    if isinstance(keys, str):
4269        keys = [keys]  # If keys is 'enter', convert it to ['enter'].
4270    keys = [_normalize_key(key, auto_shift=auto_shift) for key in keys]
4271
4272    # We need to press x keys y times, which comes out to x*y presses in total
4273    expectedPresses: int = presses * len(keys)
4274    completedPresses: int = 0
4275
4276    apply_interval: bool = False
4277    for _ in range(presses):
4278        if apply_interval:  # Don't delay first press
4279            _sleep(interval)
4280        apply_interval = True
4281
4282        apply_delay: bool = False
4283        for k in keys:
4284            if apply_delay:  # Don't delay first press
4285                _sleep(delay)
4286            apply_delay = True
4287
4288            completedPresses += _helper_press(
4289                k, duration, _pause=False, auto_shift=auto_shift
4290            )
4291
4292    return completedPresses == expectedPresses
4293    # --------------------------------------------------------------------------
4294
4295
4296# ----- hold -------------------------------------------------------------------
4297@contextmanager
4298@_genericPyDirectInputChecks
4299def hold(
4300    keys: str | Sequence[str],
4301    logScreenshot: None = None,
4302    _pause: bool = True,
4303    *,
4304    auto_shift: bool = False,
4305    raise_on_failure: bool = False,
4306) -> Generator[None, None, None]:
4307    """
4308    Hold the sequence of keys corresponding to key names in `keys` as long as
4309    the context manager is in scope (press upon entry, release upon exit).
4310
4311    Keys will be released in reverse order (LIFO), but still practically
4312    instantenous.
4313
4314    `key` will be interpreted as a keyboard key (US QWERTY).
4315    The actually pressed key will depend on your system keyboard layout.
4316    Limits the available character set but should provide the best
4317    compatibility.
4318
4319    If `_pause` is True (default), then an automatic sleep will be performed
4320    after the function finshes executing. The duration is set by the global
4321    variable `PAUSE`.
4322    Be aware, that the global pause defined by the PAUSE `constant` only
4323    applies after every call to this function, not inbetween (no pause between
4324    press and releasing key)!
4325
4326    `auto_shift` is used internally by higher level functions to automatically
4327    press the shift key before supported scancodes (indicitated by a special
4328    bit outside the regular scancode range, while it technically can be used,
4329    it's not intended for public access).
4330
4331    If `raise_on_failure` is True, then `PriorInputFailedException` will be
4332    raised if not all keyboard inputs could be executed successfully.
4333
4334    ----------------------------------------------------------------------------
4335
4336    NOTE: `logScreenshot` is currently unsupported.
4337    """
4338    if isinstance(keys, str):
4339        keys = [keys]  # make single element into iterable
4340    keys = [_normalize_key(key, auto_shift=auto_shift) for key in keys]
4341
4342    expectedPresses: int = len(keys)
4343    downed: int = 0
4344    upped: int = 0
4345
4346    try:
4347        for k in keys:
4348            downed += keyDown(k, auto_shift=auto_shift)
4349        yield
4350    finally:
4351        for k in reversed(keys):
4352            upped += keyUp(k, auto_shift=auto_shift)
4353        if raise_on_failure and not (expectedPresses == downed == upped):
4354            raise PriorInputFailedException
4355    # --------------------------------------------------------------------------
4356
4357
4358# ----- typewrite --------------------------------------------------------------
4359@_genericPyDirectInputChecks
4360def typewrite(
4361    message: str,
4362    interval: float = 0.0,
4363    logScreenshot: None = None,
4364    _pause: bool = True,
4365    *,
4366    auto_shift: bool = False,
4367    delay: float = 0.0,
4368    duration: float = 0.0,
4369) -> None:
4370    """
4371    Break down `message` into a single character key sequence and press each
4372    key one by one.
4373
4374    `message` will be interpreted as sequence of keyboard keys (US QWERTY).
4375    The actually pressed keys will depend on your system keyboard layout.
4376    Limits the available character set but should provide the best
4377    compatibility.
4378
4379    Explanation of time parameters (seconds as floating point numbers):
4380
4381    - `interval` is the time spent waiting between sequences. If `message` is a
4382    single character string, then `interval` will be ignored.
4383    - `delay` is the time from one complete key (press+release) to the next one
4384    in the same sequence. If there is only a single key in a sequence, then
4385    `delay` will be ignored.
4386    - `duration` is the time spent on holding every key before releasing it
4387    again.
4388
4389    If `_pause` is True (default), then an automatic sleep will be performed
4390    after the function finshes executing. The duration is set by the global
4391    variable `PAUSE`.
4392    Be aware, that the global pause defined by the PAUSE `constant` only
4393    applies after every call to this function, not inbetween (no pause between
4394    press and releasing key)!
4395
4396    `auto_shift` is used internally by higher level functions to automatically
4397    press the shift key before supported scancodes (indicitated by a special
4398    bit outside the regular scancode range, while it technically can be used,
4399    it's not intended for public access).
4400
4401    ----------------------------------------------------------------------------
4402
4403    NOTE: `logScreenshot` is currently unsupported.
4404    """
4405
4406    apply_interval: bool = False
4407    for key in message:
4408        if apply_interval:  # Don't delay first press
4409            _sleep(interval)
4410        apply_interval = True
4411
4412        press(
4413            key,
4414            _pause=False,
4415            auto_shift=auto_shift,
4416            delay=delay,
4417            duration=duration,
4418        )
4419    # --------------------------------------------------------------------------
4420
4421
4422# ----- typewrite alias --------------------------------------------------------
4423write = typewrite
4424# ------------------------------------------------------------------------------
4425
4426
4427# ----- hotkey -----------------------------------------------------------------
4428# Originally implemented by
4429# https://github.com/learncodebygaming/pydirectinput/pull/30
4430@_genericPyDirectInputChecks
4431def hotkey(
4432    *args: str,
4433    interval: float = 0.0,
4434    wait: float = 0.0,
4435    logScreenshot: None = None,
4436    _pause: bool = True,
4437    auto_shift: bool = True,
4438) -> None:
4439    """
4440    Press down buttons in order they are specified as arguments,
4441    releasing them in reverse order, e.g. 'ctrl', 'c' will first press
4442    Control, then C and release C before releasing Control.
4443
4444    Use keyword-only argument `interval` to specify a delay between single
4445    keys when pressing and releasing and `wait` for delay between last press
4446    and first release.
4447
4448    If `_pause` is True (default), then an automatic sleep will be performed
4449    after the function finshes executing. The duration is set by the global
4450    variable `PAUSE`.
4451    Be aware, that the global pause defined by the PAUSE `constant` only
4452    applies after every call to this function, not inbetween (no pause between
4453    press and releasing key)!
4454
4455    `auto_shift` is used internally by higher level functions to automatically
4456    press the shift key before supported scancodes (indicitated by a special
4457    bit outside the regular scancode range, while it technically can be used,
4458    it's not intended for public access).
4459
4460    ----------------------------------------------------------------------------
4461
4462    NOTE: `logScreenshot` is currently unsupported.
4463    """
4464    apply_interval: bool = False
4465    for key in args:
4466        if apply_interval:
4467            _sleep(interval)  # sleep between iterations
4468        apply_interval = True
4469
4470        keyDown(key, _pause=False, auto_shift=auto_shift)
4471
4472    _sleep(wait)
4473
4474    apply_interval = False
4475    for key in reversed(args):
4476        if apply_interval:
4477            _sleep(interval)  # sleep between iterations
4478        apply_interval = True
4479
4480        keyUp(key, _pause=False, auto_shift=auto_shift)
4481    # --------------------------------------------------------------------------
4482
4483
4484# ===== unicode functions ======================================================
4485
4486
4487# ----- unicode_charDown -------------------------------------------------------
4488@_genericPyDirectInputChecks
4489def unicode_charDown(
4490    char: str, logScreenshot: None = None, _pause: bool = True
4491) -> bool:
4492    """
4493    Send Unicode character(s) `char` to currently focused application as
4494    WM_KEYDOWN message.
4495
4496    `char` will be interpreted as a string of Unicode characters
4497    (independet from keyboard layout). Supports complete Unicode character set
4498    but may not be compatible with every application.
4499
4500    If `_pause` is True (default), then an automatic sleep will be performed
4501    after the function finshes executing. The duration is set by the global
4502    variable `PAUSE`.
4503
4504    ----------------------------------------------------------------------------
4505
4506    NOTE: `logScreenshot` is currently unsupported.
4507    """
4508    utf16surrogates: bytes = char.encode("utf-16be")
4509    codes: Sequence[int] = unpack(
4510        f">{len(utf16surrogates) // 2}H", utf16surrogates
4511    )
4512
4513    keybdFlags: int = _KEYEVENTF_UNICODE
4514
4515    input_structs: list[_INPUT] = [
4516        _create_keyboard_input(wVk=0, wScan=charcode, dwFlags=keybdFlags)
4517        for charcode in codes
4518    ]
4519    # Init event tracking
4520    expectedEvents: int = len(input_structs)
4521    insertedEvents: int = _send_input(input_structs)
4522
4523    return insertedEvents == expectedEvents
4524    # --------------------------------------------------------------------------
4525
4526
4527# ----- unicode_charUp ---------------------------------------------------------
4528@_genericPyDirectInputChecks
4529def unicode_charUp(
4530    char: str, logScreenshot: None = None, _pause: bool = True
4531) -> bool:
4532    """
4533    Send Unicode character(s) `char` to currently focused application as
4534    WM_KEYUP message.
4535
4536    `char` will be interpreted as a string of Unicode characters
4537    (independet from keyboard layout). Supports complete Unicode character set
4538    but may not be compatible with every application.
4539
4540    If `_pause` is True (default), then an automatic sleep will be performed
4541    after the function finshes executing. The duration is set by the global
4542    variable `PAUSE`.
4543
4544    ----------------------------------------------------------------------------
4545
4546    NOTE: `logScreenshot` is currently unsupported.
4547    """
4548    utf16surrogates: bytes = char.encode("utf-16be")
4549    codes: Sequence[int] = unpack(
4550        f">{len(utf16surrogates) // 2}H", utf16surrogates
4551    )
4552
4553    keybdFlags: int = _KEYEVENTF_UNICODE | _KEYEVENTF_KEYUP
4554
4555    input_structs: list[_INPUT] = [
4556        _create_keyboard_input(wVk=0, wScan=charcode, dwFlags=keybdFlags)
4557        for charcode in codes
4558    ]
4559    # Init event tracking
4560    expectedEvents: int = len(input_structs)
4561    insertedEvents: int = _send_input(input_structs)
4562
4563    return insertedEvents == expectedEvents
4564    # --------------------------------------------------------------------------
4565
4566
4567# ----- _helper_unicode_press_char ---------------------------------------------
4568def _helper_unicode_press_char(
4569    char: str,
4570    duration: float = 0.0,
4571    _pause: bool = True,
4572) -> bool:
4573    """
4574    Press `key`, wait for `duration` seconds, release `key`.
4575
4576    Return `True` if complete press was successful.
4577    """
4578    downed: bool = unicode_charDown(char, _pause=_pause)
4579    _sleep(duration)
4580    upped: bool = unicode_charUp(char, _pause=_pause)
4581    # Count key press as complete if key was "downed" and "upped"
4582    # successfully
4583    return bool(downed and upped)
4584    # --------------------------------------------------------------------------
4585
4586
4587# ----- unicode_press ----------------------------------------------------------
4588@_genericPyDirectInputChecks
4589def unicode_press(
4590    chars: str | Sequence[str],
4591    presses: int = 1,
4592    interval: float = 0.0,
4593    logScreenshot: None = None,
4594    _pause: bool = True,
4595    *,
4596    delay: float = 0.0,
4597    duration: float = 0.0,
4598) -> bool:
4599    """
4600    Press the sequence of `chars` for `presses` amount of times.
4601
4602    `chars` will be interpreted as a sequence of Unicode characters
4603    (independent from keyboard layout). Supports complete Unicode character set
4604    but may not be compatible with every application.
4605
4606    Explanation of time parameters (seconds as floating point numbers):
4607
4608    - `interval` is the time spent waiting between sequences. If `chars` is a
4609    str instance or single element list, then `interval` will be ignored.
4610    - `delay` is the time from one complete char (press+release) to the next
4611    one in the same sequence. If there is only a single char in a sequence,
4612    then `delay` will be ignored.
4613    - `duration` is the time spent on holding every char before releasing it
4614    again.
4615
4616    If `_pause` is True (default), then an automatic sleep will be performed
4617    after the function finshes executing. The duration is set by the global
4618    variable `PAUSE`.
4619    Be aware, that the global pause defined by the PAUSE `constant` only
4620    applies after every call to this function, not inbetween (no extra pause
4621    between pressing and releasing key, use the `duration` argument instead)!
4622
4623    ----------------------------------------------------------------------------
4624
4625    NOTE: `logScreenshot` is currently unsupported.
4626    """
4627    if isinstance(chars, str):
4628        chars = [chars]
4629
4630    # We need to press x keys y times, which comes out to x*y presses in total
4631    expectedPresses: int = presses * len(chars)
4632    completedPresses: int = 0
4633
4634    apply_interval: bool = False
4635    for _ in range(presses):
4636        if apply_interval:  # Don't delay first press
4637            _sleep(interval)
4638        apply_interval = True
4639
4640        apply_delay: bool = False
4641        for c in chars:
4642            if apply_delay:  # Don't delay first press
4643                _sleep(delay)
4644            apply_delay = True
4645
4646            completedPresses += _helper_unicode_press_char(
4647                c,
4648                duration,
4649                _pause=False,
4650            )
4651
4652    return completedPresses == expectedPresses
4653    # --------------------------------------------------------------------------
4654
4655
4656# ----- unicode_hold -----------------------------------------------------------
4657@contextmanager
4658@_genericPyDirectInputChecks
4659def unicode_hold(
4660    chars: str | Sequence[str],
4661    logScreenshot: None = None,
4662    _pause: bool = True,
4663    *,
4664    raise_on_failure: bool = False,
4665) -> Generator[None, None, None]:
4666    """
4667    Hold the sequence of "keys" corresponding to unicode characters in `chars`
4668    as long as the context manager is in scope (press upon entry,
4669    release upon exit).
4670
4671    `chars` will be interpreted as a sequence of Unicode characters
4672    (independet from keyboard layout). Supports complete Unicode character set
4673    but may not be compatible with every application.
4674
4675    Keys will be released in reverse order (LIFO), but still practically
4676    instantenous.
4677
4678    If `_pause` is True (default), then an automatic sleep will be performed
4679    after the function finshes executing. The duration is set by the global
4680    variable `PAUSE`.
4681    Be aware, that the global pause defined by the PAUSE `constant` only
4682    applies  after every call to this function, not inbetween (no pause between
4683    press and releasing key)!
4684
4685    If `raise_on_failure` is True, then `PriorInputFailedException` will be
4686    raised if not all keyboard inputs could be executed successfully.
4687
4688    ----------------------------------------------------------------------------
4689
4690    NOTE: `logScreenshot` is currently unsupported.
4691    """
4692    if isinstance(chars, str):
4693        chars = [chars]  # make single element into iterable
4694
4695    expectedPresses: int = len(chars)
4696    downed: int = 0
4697    upped: int = 0
4698
4699    try:
4700        for c in chars:
4701            downed += unicode_charDown(c, _pause=False)
4702        yield
4703    finally:
4704        for c in reversed(chars):
4705            upped += unicode_charUp(c, _pause=False)
4706        if raise_on_failure and not (expectedPresses == downed == upped):
4707            raise PriorInputFailedException
4708    # --------------------------------------------------------------------------
4709
4710
4711# ----- unicode_typewrite ------------------------------------------------------
4712@_genericPyDirectInputChecks
4713def unicode_typewrite(
4714    message: str,
4715    interval: float = 0.0,
4716    logScreenshot: None = None,
4717    _pause: bool = True,
4718    *,
4719    delay: float = 0.0,
4720    duration: float = 0.0,
4721) -> None:
4722    """
4723    Break down `message` into characters and press them one by one.
4724
4725    `message` will be interpreted as a sequence of Unicode characters
4726    (independet from keyboard layout). Supports complete Unicode character set
4727    but may not be compatible with every application.
4728
4729    Explanation of time parameters (seconds as floating point numbers):
4730
4731    - `interval` is the time spent waiting between sequences. If `message` is a
4732    single character string, then `interval` will be ignored.
4733    - `delay` is the time from one complete key (press+release) to the next one
4734    in the same sequence. If there is only a single key in a sequence, then
4735    `delay` will be ignored.
4736    - `duration` is the time spent on holding every key before releasing it
4737    again.
4738
4739    If `_pause` is True (default), then an automatic sleep will be performed
4740    after the function finshes executing. The duration is set by the global
4741    variable `PAUSE`.
4742    Be aware, that the global pause defined by the PAUSE `constant` only
4743    applies after every call to this function, not inbetween (no pause between
4744    press and releasing key)!
4745
4746    ----------------------------------------------------------------------------
4747
4748    NOTE: `logScreenshot` is currently unsupported.
4749    """
4750    apply_interval: bool = False
4751    for char in message:
4752        if apply_interval:
4753            _sleep(interval)  # sleep between iterations
4754        apply_interval = True
4755
4756        unicode_press(char, _pause=False, delay=delay, duration=duration)
4757    # --------------------------------------------------------------------------
4758
4759
4760# ----- unicode_typewrite alias ------------------------------------------------
4761unicode_write = unicode_typewrite
4762# ------------------------------------------------------------------------------
4763
4764
4765# ----- unicode_hotkey ---------------------------------------------------------
4766@_genericPyDirectInputChecks
4767def unicode_hotkey(
4768    *args: str,
4769    interval: float = 0.0,
4770    wait: float = 0.0,
4771    logScreenshot: None = None,
4772    _pause: bool = True,
4773) -> None:
4774    """
4775    Press down buttons in order they are specified as arguments,
4776    releasing them in reverse order.
4777
4778    This function makes little sense for Unicode characters and mainly exists
4779    for parity with the other, lower-level hotkey functions!
4780
4781    See `unicode_press()` for an alternative function that presses keys in
4782    series instead.
4783
4784    Use keyword-only argument `interval` to specify a delay between single
4785    keys when pressing and releasing and `wait` for delay between last press
4786    and first release.
4787
4788    If `_pause` is True (default), then an automatic sleep will be performed
4789    after the function finshes executing. The duration is set by the global
4790    variable `PAUSE`.
4791    Be aware, that the global pause defined by the PAUSE `constant` only
4792    applies after every call to this function, not inbetween (no pause between
4793    press and releasing key)!
4794
4795    ----------------------------------------------------------------------------
4796
4797    NOTE: `logScreenshot` is currently unsupported.
4798    """
4799    apply_interval: bool = False
4800    for char in args:
4801        if apply_interval:
4802            _sleep(interval)  # sleep between iterations
4803        apply_interval = True
4804
4805        unicode_charDown(char, _pause=False)
4806
4807    _sleep(wait)
4808
4809    apply_interval = False
4810    for char in reversed(args):
4811        if apply_interval:
4812            _sleep(interval)  # sleep between iterations
4813        apply_interval = True
4814
4815        unicode_charUp(char, _pause=False)
4816    # --------------------------------------------------------------------------
4817
4818
4819# ------------------------------------------------------------------------------
4820# Save current Enhanced Pointer Precsion setting during import
4821# unless disabled by environment variable.
4822# Since this is a safety feature, this import side-effect is enabled by default.
4823if not os.environ.get("PYDIRECTINPUT_SKIP_STORING_MOUSE_SETTINGS"):
4824    store_mouse_acceleration_settings()
4825# ------------------------------------------------------------------------------
windll: ctypes.LibraryLoader[ctypes.WinDLL] = <ctypes.LibraryLoader object>
FAILSAFE: bool = True

Stop execution if mouse is moved into one of FAILSAFE_POINTS. Change to disable failsafe behaviour.

FAILSAFE_POINTS: list[tuple[int, int]] = [(0, 0)]

List of coordinates that trigger failafe exception. (default: top left corner)

PAUSE: float | None = 0.01

Default pause interval in seconds if _pause argument isn't set to False. 1/100 second pause by default.

Set to None to disable automatic pauses entirely.

MINIMUM_SLEEP_IDEAL: float = 1e-06

Extremely small timer interval greater than 0 that still causes the system to sleep. This is the ideal value, the system may not be able to sleep for this short of a time. See MINIMUM_SLEEP_ACTUAL and calibrate_real_sleep_minimum.

MINIMUM_SLEEP_ACTUAL: float = 0.002

Actual time spent on sleeping with MINIMUM_SLEEP_IDEAL, rounded up for safety. Determined ahead of time by calibrate_real_sleep_minimum. The MINIMUM_SLEEP_ values may differ between systems. If you're unsure, run the calibration and correct this value after importing the module.

MOUSE_LEFT: str = 'left'

Name of left mouse button

MOUSE_MIDDLE: str = 'middle'

Name of middle mouse button

MOUSE_RIGHT: str = 'right'

Name of right mouse button

MOUSE_PRIMARY: str = 'primary'

Name of primary mouse button (left mouse button unless swapped)

MOUSE_SECONDARY: str = 'secondary'

Name of secondary mouse button (right mouse button unless swapped)

MOUSE_BUTTON4: str = 'mouse4'

Name of first additional mouse button (usually a side button)

MOUSE_X1: str = 'x1'

Name of first additional mouse button (usually a side button)

MOUSE_BUTTON5: str = 'mouse5'

Name of second additional mouse button (usually a side button)

MOUSE_X2: str = 'x2'

Name of second additional mouse button (usually a side button)

def calibrate_real_sleep_minimum(runs: int = 10, *, verbose: bool = False) -> None:
209def calibrate_real_sleep_minimum(
210    runs: int = 10, *, verbose: bool = False
211) -> None:
212    """
213    Measure your system's minimum sleep duration and calibrate
214    `MINIMUM_SLEEP_ACTUAL` accordingly.
215
216    Will try to sleep for `MINIMUM_SLEEP_IDEAL` seconds and measure actual time
217    difference. Repeat for `runs` amount of times, take the highest measurement
218    and round it up to the next higher value in the same order of magnitude.
219
220    Example: [0.001874, 0.001721, 0.001806] would round up to 0.002
221    """
222
223    def round_up_same_magnitude(x: float) -> float:
224        mag: float = 10 ** floor(log10(x))
225        return ceil(x / mag) * mag
226
227    def stopwatch(duration: float) -> float:
228        t1: int = _time_ns()
229        _sleep(duration)
230        t2: int = _time_ns()
231        return (t2 - t1) * 1e-9
232
233    if verbose:
234        print("Calibrating real sleep minimum...")
235
236    measurements = [stopwatch(MINIMUM_SLEEP_IDEAL) for _ in range(runs)]
237    if verbose:
238        print(f"Real measurements: {measurements}")
239
240    new_sleep_minimum = round_up_same_magnitude(max(measurements))
241    if verbose:
242        print(
243            "Rounding max measurement to next higher value in same order of "
244            f"magnitude: {new_sleep_minimum}"
245        )
246
247    global MINIMUM_SLEEP_ACTUAL
248    if verbose:
249        print(
250            f"Changing MINIMUM_SLEEP_ACTUAL from {MINIMUM_SLEEP_ACTUAL} to "
251            f"{new_sleep_minimum}"
252        )
253    MINIMUM_SLEEP_ACTUAL = (  # pyright: ignore[reportConstantRedefinition]
254        new_sleep_minimum
255    )
256    # --------------------------------------------------------------------------

Measure your system's minimum sleep duration and calibrate MINIMUM_SLEEP_ACTUAL accordingly.

Will try to sleep for MINIMUM_SLEEP_IDEAL seconds and measure actual time difference. Repeat for runs amount of times, take the highest measurement and round it up to the next higher value in the same order of magnitude.

Example: [0.001874, 0.001721, 0.001806] would round up to 0.002

def update_MOUSEEVENT_mappings() -> None:
614def update_MOUSEEVENT_mappings() -> None:
615    """
616    Update the MOUSEEVENT mappings if you change the name of the button name
617    constants.
618
619    This function MUST be called every time one of the `MOUSE_*` constants
620    has been changed!
621    """
622    _MOUSE_MAPPING_EVENTF.update(
623        {
624            MOUSE_LEFT: _MOUSEEVENTF_LEFT,
625            MOUSE_MIDDLE: _MOUSEEVENTF_MIDDLE,
626            MOUSE_RIGHT: _MOUSEEVENTF_RIGHT,
627            MOUSE_BUTTON4: _MOUSEEVENTF_X,
628            MOUSE_X1: _MOUSEEVENTF_X,
629            MOUSE_BUTTON5: _MOUSEEVENTF_X,
630            MOUSE_X2: _MOUSEEVENTF_X,
631        }
632    )
633    _MOUSE_MAPPING_DATA.update(
634        {
635            MOUSE_LEFT: 0,
636            MOUSE_MIDDLE: 0,
637            MOUSE_RIGHT: 0,
638            MOUSE_BUTTON4: _XBUTTON1,
639            MOUSE_X1: _XBUTTON1,
640            MOUSE_BUTTON5: _XBUTTON2,
641            MOUSE_X2: _XBUTTON2,
642        }
643    )

Update the MOUSEEVENT mappings if you change the name of the button name constants.

This function MUST be called every time one of the MOUSE_* constants has been changed!

class ScancodeSequence(list[int]):
1546class ScancodeSequence(List[int]):
1547    """
1548    A special class with the sole purpose of representing extended scancode
1549    sequences that should be grouped together in a single INPUT array.
1550
1551    Inserting non-scancode elements is illegal, but no runtime checks exist
1552    to verify correct input! Violations could lead to unpredictable runtime
1553    behaviour. You've been warned.
1554    """
1555
1556    pass
1557    # --------------------------------------------------------------------------

A special class with the sole purpose of representing extended scancode sequences that should be grouped together in a single INPUT array.

Inserting non-scancode elements is illegal, but no runtime checks exist to verify correct input! Violations could lead to unpredictable runtime behaviour. You've been warned.

ScancodeTypes: TypeAlias = 'int | ScancodeSequence'

Acceptable value types in KEYBOARD_MAPPING.

Accepts single standalone scancode integer or multiple scancode integers contained in a special class ScancodeSequence instance.

US_QWERTY_MAPPING: Final[dict[str, int | ScancodeSequence]] = {'escape': 1, 'esc': 1, 'f1': 59, 'f2': 60, 'f3': 61, 'f4': 62, 'f5': 63, 'f6': 64, 'f7': 65, 'f8': 66, 'f9': 67, 'f10': 68, 'f11': 87, 'f12': 88, 'printscreen': 84, 'prntscrn': 84, 'prtsc': 84, 'prtscr': 84, 'scrolllock': 70, 'ctrlbreak': 57414, 'pause': [57629, 69, 57757, 197], '`': 41, '1': 2, '2': 3, '3': 4, '4': 5, '5': 6, '6': 7, '7': 8, '8': 9, '9': 10, '0': 11, '-': 12, '=': 13, '~': 65577, '!': 65538, '@': 65539, '#': 65540, '$': 65541, '%': 65542, '^': 65543, '&': 65544, '*': 65545, '(': 65546, ')': 65547, '_': 65548, '+': 65549, 'backspace': 14, '\x08': 14, 'insert': 57426, 'home': 57415, 'pageup': 57417, 'pgup': 57417, 'pagedown': 57425, 'pgdn': 57425, 'numlock': 69, 'divide': 57397, 'multiply': 55, 'subtract': 74, 'add': 78, 'decimal': 83, 'numperiod': 83, 'numpadenter': 57372, 'numpad1': 79, 'numpad2': 80, 'numpad3': 81, 'numpad4': 75, 'numpad5': 76, 'numpad6': 77, 'numpad7': 71, 'numpad8': 72, 'numpad9': 73, 'num0': 82, 'num1': 79, 'num2': 80, 'num3': 81, 'num4': 75, 'num5': 76, 'num6': 77, 'num7': 71, 'num8': 72, 'num9': 73, 'clear': 76, 'tab': 15, '\t': 15, 'q': 16, 'w': 17, 'e': 18, 'r': 19, 't': 20, 'y': 21, 'u': 22, 'i': 23, 'o': 24, 'p': 25, '[': 26, ']': 27, '\\': 43, 'Q': 65552, 'W': 65553, 'E': 65554, 'R': 65555, 'T': 65556, 'Y': 65557, 'U': 65558, 'I': 65559, 'O': 65560, 'P': 65561, '{': 65562, '}': 65563, '|': 65579, 'del': 57427, 'delete': 57427, 'end': 57423, 'capslock': 58, 'a': 30, 's': 31, 'd': 32, 'f': 33, 'g': 34, 'h': 35, 'j': 36, 'k': 37, 'l': 38, ';': 39, "'": 40, 'A': 65566, 'S': 65567, 'D': 65568, 'F': 65569, 'G': 65570, 'H': 65571, 'J': 65572, 'K': 65573, 'L': 65574, ':': 65575, '"': 65576, 'enter': 28, 'return': 28, '\n': 28, 'shift': 42, 'shiftleft': 42, 'z': 44, 'x': 45, 'c': 46, 'v': 47, 'b': 48, 'n': 49, 'm': 50, ',': 51, '.': 52, '/': 53, 'Z': 65580, 'X': 65581, 'C': 65582, 'V': 65583, 'B': 65584, 'N': 65585, 'M': 65586, '<': 65587, '>': 65588, '?': 65589, 'shiftright': 54, 'ctrl': 29, 'ctrlleft': 29, 'win': 57435, 'super': 57435, 'winleft': 57435, 'alt': 56, 'altleft': 56, ' ': 57, 'space': 57, 'altright': 57400, 'winright': 57436, 'apps': 57437, 'context': 57437, 'contextmenu': 57437, 'ctrlright': 57373, 'up': 57416, 'left': 57419, 'down': 57424, 'right': 57421, 'help': 99, 'sleep': 57439, 'medianext': 57369, 'nexttrack': 57369, 'mediaprevious': 57360, 'prevtrack': 57360, 'mediastop': 57380, 'stop': 57380, 'mediaplay': 57378, 'mediapause': 57378, 'playpause': 57378, 'mute': 57376, 'volumemute': 57376, 'volumeup': 57392, 'volup': 57392, 'volumedown': 57390, 'voldown': 57390, 'media': 57453, 'launchmediaselect': 57453, 'email': 57452, 'launchmail': 57452, 'calculator': 57377, 'calc': 57377, 'launch1': 57451, 'launchapp1': 57451, 'launch2': 57377, 'launchapp2': 57377, 'browsersearch': 57445, 'browserhome': 57394, 'browserforward': 57449, 'browserback': 57450, 'browserstop': 57448, 'browserrefresh': 57447, 'browserfavorites': 57446, 'f13': 100, 'f14': 101, 'f15': 102, 'f16': 103, 'f17': 104, 'f18': 105, 'f19': 106, 'f20': 107, 'f21': 108, 'f22': 109, 'f23': 110, 'f24': 118}

Maps a string representation of keyboard keys to their corresponding hardware scan code. Based on standard US QWERTY-Layout.

Not intended to be changed at runtime!

If you want to change the keyboard mapping to better reflect your own keyboard layout, use KEYBOARD_MAPPING.update(your_dict) where your_dict is a dict that maps keynames to scancodes.

KEYBOARD_MAPPING: dict[str, int | ScancodeSequence] = {'escape': 1, 'esc': 1, 'f1': 59, 'f2': 60, 'f3': 61, 'f4': 62, 'f5': 63, 'f6': 64, 'f7': 65, 'f8': 66, 'f9': 67, 'f10': 68, 'f11': 87, 'f12': 88, 'printscreen': 84, 'prntscrn': 84, 'prtsc': 84, 'prtscr': 84, 'scrolllock': 70, 'ctrlbreak': 57414, 'pause': [57629, 69, 57757, 197], '`': 41, '1': 2, '2': 3, '3': 4, '4': 5, '5': 6, '6': 7, '7': 8, '8': 9, '9': 10, '0': 11, '-': 12, '=': 13, '~': 65577, '!': 65538, '@': 65539, '#': 65540, '$': 65541, '%': 65542, '^': 65543, '&': 65544, '*': 65545, '(': 65546, ')': 65547, '_': 65548, '+': 65549, 'backspace': 14, '\x08': 14, 'insert': 57426, 'home': 57415, 'pageup': 57417, 'pgup': 57417, 'pagedown': 57425, 'pgdn': 57425, 'numlock': 69, 'divide': 57397, 'multiply': 55, 'subtract': 74, 'add': 78, 'decimal': 83, 'numperiod': 83, 'numpadenter': 57372, 'numpad1': 79, 'numpad2': 80, 'numpad3': 81, 'numpad4': 75, 'numpad5': 76, 'numpad6': 77, 'numpad7': 71, 'numpad8': 72, 'numpad9': 73, 'num0': 82, 'num1': 79, 'num2': 80, 'num3': 81, 'num4': 75, 'num5': 76, 'num6': 77, 'num7': 71, 'num8': 72, 'num9': 73, 'clear': 76, 'tab': 15, '\t': 15, 'q': 16, 'w': 17, 'e': 18, 'r': 19, 't': 20, 'y': 21, 'u': 22, 'i': 23, 'o': 24, 'p': 25, '[': 26, ']': 27, '\\': 43, 'Q': 65552, 'W': 65553, 'E': 65554, 'R': 65555, 'T': 65556, 'Y': 65557, 'U': 65558, 'I': 65559, 'O': 65560, 'P': 65561, '{': 65562, '}': 65563, '|': 65579, 'del': 57427, 'delete': 57427, 'end': 57423, 'capslock': 58, 'a': 30, 's': 31, 'd': 32, 'f': 33, 'g': 34, 'h': 35, 'j': 36, 'k': 37, 'l': 38, ';': 39, "'": 40, 'A': 65566, 'S': 65567, 'D': 65568, 'F': 65569, 'G': 65570, 'H': 65571, 'J': 65572, 'K': 65573, 'L': 65574, ':': 65575, '"': 65576, 'enter': 28, 'return': 28, '\n': 28, 'shift': 42, 'shiftleft': 42, 'z': 44, 'x': 45, 'c': 46, 'v': 47, 'b': 48, 'n': 49, 'm': 50, ',': 51, '.': 52, '/': 53, 'Z': 65580, 'X': 65581, 'C': 65582, 'V': 65583, 'B': 65584, 'N': 65585, 'M': 65586, '<': 65587, '>': 65588, '?': 65589, 'shiftright': 54, 'ctrl': 29, 'ctrlleft': 29, 'win': 57435, 'super': 57435, 'winleft': 57435, 'alt': 56, 'altleft': 56, ' ': 57, 'space': 57, 'altright': 57400, 'winright': 57436, 'apps': 57437, 'context': 57437, 'contextmenu': 57437, 'ctrlright': 57373, 'up': 57416, 'left': 57419, 'down': 57424, 'right': 57421, 'help': 99, 'sleep': 57439, 'medianext': 57369, 'nexttrack': 57369, 'mediaprevious': 57360, 'prevtrack': 57360, 'mediastop': 57380, 'stop': 57380, 'mediaplay': 57378, 'mediapause': 57378, 'playpause': 57378, 'mute': 57376, 'volumemute': 57376, 'volumeup': 57392, 'volup': 57392, 'volumedown': 57390, 'voldown': 57390, 'media': 57453, 'launchmediaselect': 57453, 'email': 57452, 'launchmail': 57452, 'calculator': 57377, 'calc': 57377, 'launch1': 57451, 'launchapp1': 57451, 'launch2': 57377, 'launchapp2': 57377, 'browsersearch': 57445, 'browserhome': 57394, 'browserforward': 57449, 'browserback': 57450, 'browserstop': 57448, 'browserrefresh': 57447, 'browserfavorites': 57446, 'f13': 100, 'f14': 101, 'f15': 102, 'f16': 103, 'f17': 104, 'f18': 105, 'f19': 106, 'f20': 107, 'f21': 108, 'f22': 109, 'f23': 110, 'f24': 118}

Maps a string representation of keyboard keys to their corresponding hardware scan code. Based on standard US QWERTY-Layout by default.

If you want to change the keyboard mapping to better reflect your own keyboard layout, use KEYBOARD_MAPPING.update(your_dict), where your_dict is a dict that maps keynames to scancodes.

class FailSafeException(builtins.Exception):
1853class FailSafeException(Exception):
1854    """Raised when _failSafeCheck detects failsafe mouse position."""
1855
1856    pass

Raised when _failSafeCheck detects failsafe mouse position.

class PriorInputFailedException(builtins.Exception):
1859class PriorInputFailedException(Exception):
1860    """Raised in hold() context managers when raise_on_failure is set."""
1861
1862    pass
1863    # --------------------------------------------------------------------------

Raised in hold() context managers when raise_on_failure is set.

PathFunction: TypeAlias = 'Callable[[int, int, int, int], list[tuple[int, int]]]'
def position( x: int | float | None = None, y: int | float | None = None) -> tuple[int, int]:
2071def position(
2072    x: int | float | None = None, y: int | float | None = None
2073) -> tuple[int, int]:
2074    """
2075    Return a postion tuple `(x, y)`.
2076
2077    If x and/or y argument(s) are not given, use current mouse cursor coordinate
2078    instead.
2079    """
2080    cursor: _POINT = _get_cursor_pos()
2081    return (
2082        cursor.x if x is None else int(x),
2083        cursor.y if y is None else int(y),
2084    )
2085    # --------------------------------------------------------------------------

Return a postion tuple (x, y).

If x and/or y argument(s) are not given, use current mouse cursor coordinate instead.

def size() -> tuple[int, int]:
2089def size() -> tuple[int, int]:
2090    """
2091    Return the size of the primary display as tuple `(width, height)`.
2092    """
2093    return (
2094        _get_system_metrics(_SM_CXSCREEN),
2095        _get_system_metrics(_SM_CYSCREEN),
2096    )
2097    # --------------------------------------------------------------------------

Return the size of the primary display as tuple (width, height).

def virtual_size() -> tuple[int, int, int, int]:
2101def virtual_size() -> tuple[int, int, int, int]:
2102    """
2103    Return the the display size of the complete virtual monitor bounding box
2104    rectangle as tuple `(width, height, left_offset, top_offset)`.
2105
2106    On a single monitor system, this function is equal to `(*size(), 0, 0)`.
2107
2108    `left_offset` and `top_offset` are measured from the top left pixel of the
2109    primary monitor.
2110    """
2111    return (
2112        _get_system_metrics(_SM_CXVIRTUALSCREEN),
2113        _get_system_metrics(_SM_CYVIRTUALSCREEN),
2114        _get_system_metrics(_SM_XVIRTUALSCREEN),
2115        _get_system_metrics(_SM_YVIRTUALSCREEN),
2116    )
2117    # --------------------------------------------------------------------------

Return the the display size of the complete virtual monitor bounding box rectangle as tuple (width, height, left_offset, top_offset).

On a single monitor system, this function is equal to (*size(), 0, 0).

left_offset and top_offset are measured from the top left pixel of the primary monitor.

def on_primary_monitor(x: int | tuple[int, int] | None = None, y: int | None = None) -> bool:
2135def on_primary_monitor(
2136    x: int | tuple[int, int] | None = None,
2137    y: int | None = None,
2138) -> bool:
2139    """
2140    Returns whether the given xy coordinates are on the primary screen or not.
2141
2142    If x and/or y argument(s) are not given, current mouse cursor coordinates
2143    will be used instead.
2144    """
2145    _x: int | None
2146    _y: int | None
2147    if isinstance(x, Sequence):
2148        assert not isinstance(x, int)  # remove int annotation, mypy needs this
2149        if y is not None:
2150            raise ValueError(
2151                "onScreen() does not accept Sequence-types as first argument "
2152                "if a second argument is also provided!"
2153            )
2154        try:
2155            _x, _y = x[0], x[1]
2156        except IndexError as e:
2157            raise ValueError(
2158                "onScreen() does not accept single element sequences "
2159                "as first argument!"
2160            ) from e
2161    else:
2162        _x, _y = x, y
2163
2164    x, y = position(_x, _y)
2165    display_width: int
2166    display_height: int
2167    display_width, display_height = size()
2168
2169    return 0 <= x < display_width and 0 <= y < display_height

Returns whether the given xy coordinates are on the primary screen or not.

If x and/or y argument(s) are not given, current mouse cursor coordinates will be used instead.

def onScreen(x: int | tuple[int, int] | None = None, y: int | None = None) -> bool:
2135def on_primary_monitor(
2136    x: int | tuple[int, int] | None = None,
2137    y: int | None = None,
2138) -> bool:
2139    """
2140    Returns whether the given xy coordinates are on the primary screen or not.
2141
2142    If x and/or y argument(s) are not given, current mouse cursor coordinates
2143    will be used instead.
2144    """
2145    _x: int | None
2146    _y: int | None
2147    if isinstance(x, Sequence):
2148        assert not isinstance(x, int)  # remove int annotation, mypy needs this
2149        if y is not None:
2150            raise ValueError(
2151                "onScreen() does not accept Sequence-types as first argument "
2152                "if a second argument is also provided!"
2153            )
2154        try:
2155            _x, _y = x[0], x[1]
2156        except IndexError as e:
2157            raise ValueError(
2158                "onScreen() does not accept single element sequences "
2159                "as first argument!"
2160            ) from e
2161    else:
2162        _x, _y = x, y
2163
2164    x, y = position(_x, _y)
2165    display_width: int
2166    display_height: int
2167    display_width, display_height = size()
2168
2169    return 0 <= x < display_width and 0 <= y < display_height

Returns whether the given xy coordinates are on the primary screen or not.

If x and/or y argument(s) are not given, current mouse cursor coordinates will be used instead.

def valid_screen_coordinates(x: int | tuple[int, int] | None = None, y: int | None = None) -> bool:
2188def valid_screen_coordinates(
2189    x: int | tuple[int, int] | None = None, y: int | None = None
2190) -> bool:
2191    """
2192    Returns whether the given xy coordinates are on a real monitor or not.
2193
2194    If x and/or y argument(s) are not given, current mouse cursor coordinates
2195    will be used instead.
2196    """
2197    _x: int | None
2198    _y: int | None
2199    if isinstance(x, Sequence):
2200        assert not isinstance(x, int)  # remove int annotation, mypy needs this
2201        if y is not None:
2202            raise ValueError(
2203                "onScreen() does not accept Sequence-types as first argument "
2204                "if a second argument is also provided!"
2205            )
2206        try:
2207            _x, _y = x[0], x[1]
2208        except IndexError as e:
2209            raise ValueError(
2210                "onScreen() does not accept single element sequences "
2211                "as first argument!"
2212            ) from e
2213    else:
2214        _x, _y = x, y
2215
2216    x, y = position(_x, _y)
2217    return _monitor_from_point(x, y) is not None
2218    # --------------------------------------------------------------------------

Returns whether the given xy coordinates are on a real monitor or not.

If x and/or y argument(s) are not given, current mouse cursor coordinates will be used instead.

def store_mouse_acceleration_settings() -> None:
2355def store_mouse_acceleration_settings() -> None:
2356    """
2357    Manually save the current Windows Enhanced Pointer Precision setting so
2358    that it can be restored later with `restore_mouse_acceleration_settings()`.
2359    """
2360    precision: int
2361    _, _, precision = _get_mouse_parameters()
2362    speed: int = _get_mouse_speed()
2363    __MouseSpeedSettings.set_manual_mouse_settings(precision, speed)
2364    # --------------------------------------------------------------------------

Manually save the current Windows Enhanced Pointer Precision setting so that it can be restored later with restore_mouse_acceleration_settings().

def restore_mouse_acceleration_settings() -> None:
2368def restore_mouse_acceleration_settings() -> None:
2369    """
2370    Manually restore the current Windows Enhanced Pointer Precision setting to
2371    what it was beforehand when it was saved with
2372    `store_mouse_acceleration_settings()`.
2373    """
2374    precision: int | None
2375    speed: int | None
2376    precision, speed = __MouseSpeedSettings.get_manual_mouse_settings()
2377    if precision is None or speed is None:
2378        raise ValueError(
2379            "Can't restore Enhanced Pointer Precision setting! "
2380            "Setting was not saved beforehand!"
2381        )
2382    th1: int
2383    th2: int
2384    th1, th2, _ = _get_mouse_parameters()
2385    _set_mouse_parameters(th1, th2, precision)
2386    _set_mouse_speed(speed)
2387    # --------------------------------------------------------------------------

Manually restore the current Windows Enhanced Pointer Precision setting to what it was beforehand when it was saved with store_mouse_acceleration_settings().

def mouseDown( x: int | None = None, y: int | None = None, button: str = 'primary', duration: float = 0.0, tween: Callable[[float], float] | None = None, logScreenshot: bool = False, _pause: bool = True, *, relative: bool = False, virtual: bool = False, path_function: 'PathFunction | None' = None, attempt_pixel_perfect: bool = False, disable_mouse_acceleration: bool = False) -> None:
2396@_genericPyDirectInputChecks
2397def mouseDown(
2398    x: int | None = None,
2399    y: int | None = None,
2400    button: str = MOUSE_PRIMARY,
2401    duration: float = 0.0,
2402    tween: Callable[[float], float] | None = None,
2403    logScreenshot: bool = False,
2404    _pause: bool = True,
2405    *,
2406    relative: bool = False,
2407    virtual: bool = False,
2408    path_function: PathFunction | None = None,
2409    attempt_pixel_perfect: bool = False,
2410    disable_mouse_acceleration: bool = False,
2411) -> None:
2412    """
2413    Press down mouse button `button`.
2414
2415    If `x` or `y` are given and not None, then the mouse will move the
2416    indicated postion before pressing the button.
2417
2418    `button` is the name of the button to press. Use the public `MOUSE_*`
2419    constants to get valid argument values. (If you change the constants, then
2420    you will have to call `update_MOUSEEVENT_mappings()` to resync the lookup
2421    functions)
2422
2423    If `_pause` is True (default), then an automatic sleep will be performed
2424    after the function finshes executing. The duration is set by the global
2425    variable `PAUSE`.
2426
2427    `duration`, `tween`, `relative`, `virtual`, `path_function`,
2428    `attempt_pixel_perfect`, `disable_mouse_acceleration` are only relevant
2429    if x or y are given.
2430    See `moveTo()` for further information.
2431
2432    Raises `ValueError` if `button` is not a valid mouse button name.
2433
2434    ----------------------------------------------------------------------------
2435
2436    NOTE: `logScreenshot` is currently unsupported.
2437    """
2438    # TODO: bounding box check for valid position
2439    if x is not None or y is not None:
2440        moveTo(
2441            x,
2442            y,
2443            duration=duration,
2444            tween=tween,
2445            logScreenshot=logScreenshot,
2446            _pause=False,  # don't add an additional pause
2447            relative=relative,
2448            virtual=virtual,
2449            path_function=path_function,
2450            attempt_pixel_perfect=attempt_pixel_perfect,
2451            disable_mouse_acceleration=disable_mouse_acceleration,
2452        )
2453
2454    event_value: int | None = None
2455    mouseData: int
2456    event_value, mouseData = _get_mouse_struct_data(button, _MOUSE_PRESS)
2457
2458    if not event_value:
2459        raise ValueError(
2460            f"Invalid button argument! "
2461            f'Expected "{MOUSE_LEFT}", "{MOUSE_RIGHT}", "{MOUSE_MIDDLE}", '
2462            f'"{MOUSE_BUTTON4}", "{MOUSE_BUTTON5}", "{MOUSE_PRIMARY}" or '
2463            f'"{MOUSE_SECONDARY}"; got "{button}" instead!'
2464        )
2465
2466    _send_input(_create_mouse_input(mouseData=mouseData, dwFlags=event_value))
2467    # --------------------------------------------------------------------------

Press down mouse button button.

If x or y are given and not None, then the mouse will move the indicated postion before pressing the button.

button is the name of the button to press. Use the public MOUSE_* constants to get valid argument values. (If you change the constants, then you will have to call update_MOUSEEVENT_mappings() to resync the lookup functions)

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.

duration, tween, relative, virtual, path_function, attempt_pixel_perfect, disable_mouse_acceleration are only relevant if x or y are given. See moveTo() for further information.

Raises ValueError if button is not a valid mouse button name.


NOTE: logScreenshot is currently unsupported.

def mouseUp( x: int | None = None, y: int | None = None, button: str = 'primary', duration: float = 0.0, tween: Callable[[float], float] | None = None, logScreenshot: bool = False, _pause: bool = True, *, relative: bool = False, virtual: bool = False, path_function: 'PathFunction | None' = None, attempt_pixel_perfect: bool = False, disable_mouse_acceleration: bool = False) -> None:
2471@_genericPyDirectInputChecks
2472def mouseUp(
2473    x: int | None = None,
2474    y: int | None = None,
2475    button: str = MOUSE_PRIMARY,
2476    duration: float = 0.0,
2477    tween: Callable[[float], float] | None = None,
2478    logScreenshot: bool = False,
2479    _pause: bool = True,
2480    *,
2481    relative: bool = False,
2482    virtual: bool = False,
2483    path_function: PathFunction | None = None,
2484    attempt_pixel_perfect: bool = False,
2485    disable_mouse_acceleration: bool = False,
2486) -> None:
2487    """
2488    Lift up mouse button `button`.
2489
2490    If `x` or `y` are given and not None, then the mouse will move the
2491    indicated postion before lifting the button.
2492
2493    `button` is the name of the button to press. Use the public `MOUSE_*`
2494    constants to get valid argument values. (If you change the constants, then
2495    you will have to call `update_MOUSEEVENT_mappings()` to resync the lookup
2496    functions)
2497
2498    If `_pause` is True (default), then an automatic sleep will be performed
2499    after the function finshes executing. The duration is set by the global
2500    variable `PAUSE`.
2501
2502    `duration`, `tween`, `relative`, `virtual`, `path_function`,
2503    `attempt_pixel_perfect`, `disable_mouse_acceleration` are only relevant
2504    if x or y are given.
2505    See `moveTo()` for further information.
2506
2507    Raises `ValueError` if `button` is not a valid mouse button name.
2508
2509    ----------------------------------------------------------------------------
2510
2511    NOTE: `logScreenshot` is currently unsupported.
2512    """
2513    # TODO: bounding box check for valid position
2514    if x is not None or y is not None:
2515        moveTo(
2516            x,
2517            y,
2518            duration=duration,
2519            tween=tween,
2520            logScreenshot=logScreenshot,
2521            _pause=False,  # don't add an additional pause
2522            relative=relative,
2523            virtual=virtual,
2524            path_function=path_function,
2525            attempt_pixel_perfect=attempt_pixel_perfect,
2526            disable_mouse_acceleration=disable_mouse_acceleration,
2527        )
2528
2529    event_value: int | None = None
2530    mouseData: int
2531    event_value, mouseData = _get_mouse_struct_data(button, _MOUSE_RELEASE)
2532
2533    if not event_value:
2534        raise ValueError(
2535            "Invalid button argument! "
2536            f'Expected "{MOUSE_LEFT}", "{MOUSE_RIGHT}", "{MOUSE_MIDDLE}", '
2537            f'"{MOUSE_BUTTON4}", "{MOUSE_BUTTON5}", "{MOUSE_PRIMARY}" or '
2538            f'"{MOUSE_SECONDARY}"; got "{button}" instead!'
2539        )
2540
2541    _send_input(_create_mouse_input(mouseData=mouseData, dwFlags=event_value))
2542    # --------------------------------------------------------------------------

Lift up mouse button button.

If x or y are given and not None, then the mouse will move the indicated postion before lifting the button.

button is the name of the button to press. Use the public MOUSE_* constants to get valid argument values. (If you change the constants, then you will have to call update_MOUSEEVENT_mappings() to resync the lookup functions)

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.

duration, tween, relative, virtual, path_function, attempt_pixel_perfect, disable_mouse_acceleration are only relevant if x or y are given. See moveTo() for further information.

Raises ValueError if button is not a valid mouse button name.


NOTE: logScreenshot is currently unsupported.

def click( x: int | None = None, y: int | None = None, clicks: int = 1, interval: float = 0.0, button: str = 'primary', duration: float = 0.0, tween: Callable[[float], float] | None = None, logScreenshot: bool = False, _pause: bool = True, *, relative: bool = False, virtual: bool = False, path_function: 'PathFunction | None' = None, attempt_pixel_perfect: bool = False, disable_mouse_acceleration: bool = False) -> None:
2546@_genericPyDirectInputChecks
2547def click(
2548    x: int | None = None,
2549    y: int | None = None,
2550    clicks: int = 1,
2551    interval: float = 0.0,
2552    button: str = MOUSE_PRIMARY,
2553    duration: float = 0.0,
2554    tween: Callable[[float], float] | None = None,
2555    logScreenshot: bool = False,
2556    _pause: bool = True,
2557    *,
2558    relative: bool = False,
2559    virtual: bool = False,
2560    path_function: PathFunction | None = None,
2561    attempt_pixel_perfect: bool = False,
2562    disable_mouse_acceleration: bool = False,
2563) -> None:
2564    """
2565    Click mouse button `button` (combining press down and lift up).
2566
2567    If `x` or `y` are given and not None, then the mouse will move the
2568    indicated postion before clicking the button.
2569
2570    `button` is the name of the button to press. Use the public `MOUSE_*`
2571    constants to get valid argument values. (If you change the constants, then
2572    you will have to call `update_MOUSEEVENT_mappings()` to resync the lookup
2573    functions)
2574
2575    `clicks` is an integer that determines the amount of times the button will
2576    be clicked.
2577
2578    `interval` is the wait time in seconds between clicks.
2579
2580    If `_pause` is True (default), then an automatic sleep will be performed
2581    after the function finshes executing. The duration is set by the global
2582    variable `PAUSE`.
2583
2584    `duration`, `tween`, `relative`, `virtual`, `path_function`,
2585    `attempt_pixel_perfect`, `disable_mouse_acceleration` are only relevant
2586    if x or y are given.
2587    See `moveTo()` for further information.
2588
2589    Raises `ValueError` if `button` is not a valid mouse button name.
2590
2591    ----------------------------------------------------------------------------
2592
2593    NOTE: `logScreenshot` is currently unsupported.
2594    """
2595    # TODO: bounding box check for valid position
2596    if x is not None or y is not None:
2597        moveTo(
2598            x,
2599            y,
2600            duration=duration,
2601            tween=tween,
2602            logScreenshot=logScreenshot,
2603            _pause=False,  # don't add an additional pause
2604            relative=relative,
2605            virtual=virtual,
2606            path_function=path_function,
2607            attempt_pixel_perfect=attempt_pixel_perfect,
2608            disable_mouse_acceleration=disable_mouse_acceleration,
2609        )
2610
2611    event_value: int | None = None
2612    mouseData: int
2613    event_value, mouseData = _get_mouse_struct_data(button, _MOUSE_CLICK)
2614
2615    if not event_value:
2616        raise ValueError(
2617            f"Invalid button argument! "
2618            f'Expected "{MOUSE_LEFT}", "{MOUSE_RIGHT}", "{MOUSE_MIDDLE}", '
2619            f'"{MOUSE_BUTTON4}", "{MOUSE_BUTTON5}", "{MOUSE_PRIMARY}" or '
2620            f'"{MOUSE_SECONDARY}"; got "{button}" instead!'
2621        )
2622
2623    apply_interval: bool = False
2624    for _ in range(clicks):
2625        if apply_interval:  # Don't delay first press
2626            _sleep(interval)
2627        apply_interval = True
2628
2629        _send_input(
2630            _create_mouse_input(mouseData=mouseData, dwFlags=event_value)
2631        )
2632    # --------------------------------------------------------------------------

Click mouse button button (combining press down and lift up).

If x or y are given and not None, then the mouse will move the indicated postion before clicking the button.

button is the name of the button to press. Use the public MOUSE_* constants to get valid argument values. (If you change the constants, then you will have to call update_MOUSEEVENT_mappings() to resync the lookup functions)

clicks is an integer that determines the amount of times the button will be clicked.

interval is the wait time in seconds between clicks.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.

duration, tween, relative, virtual, path_function, attempt_pixel_perfect, disable_mouse_acceleration are only relevant if x or y are given. See moveTo() for further information.

Raises ValueError if button is not a valid mouse button name.


NOTE: logScreenshot is currently unsupported.

def leftClick( x: int | None = None, y: int | None = None, interval: float = 0.0, duration: float = 0.0, tween: Callable[[float], float] | None = None, logScreenshot: bool = False, _pause: bool = True, *, relative: bool = False, virtual: bool = False, path_function: 'PathFunction | None' = None, attempt_pixel_perfect: bool = False, disable_mouse_acceleration: bool = False) -> None:
2636def leftClick(
2637    x: int | None = None,
2638    y: int | None = None,
2639    interval: float = 0.0,
2640    duration: float = 0.0,
2641    tween: Callable[[float], float] | None = None,
2642    logScreenshot: bool = False,
2643    _pause: bool = True,
2644    *,
2645    relative: bool = False,
2646    virtual: bool = False,
2647    path_function: PathFunction | None = None,
2648    attempt_pixel_perfect: bool = False,
2649    disable_mouse_acceleration: bool = False,
2650) -> None:
2651    """
2652    Click Left Mouse button.
2653
2654    See `click()` for more information
2655    """
2656    click(
2657        x,
2658        y,
2659        clicks=1,
2660        interval=interval,
2661        button=MOUSE_LEFT,
2662        duration=duration,
2663        tween=tween,
2664        logScreenshot=logScreenshot,
2665        _pause=_pause,  # Keep _pause since this function has no input checks
2666        relative=relative,
2667        virtual=virtual,
2668        path_function=path_function,
2669        attempt_pixel_perfect=attempt_pixel_perfect,
2670        disable_mouse_acceleration=disable_mouse_acceleration,
2671    )
2672    # --------------------------------------------------------------------------

Click Left Mouse button.

See click() for more information

def rightClick( x: int | None = None, y: int | None = None, interval: float = 0.0, duration: float = 0.0, tween: Callable[[float], float] | None = None, logScreenshot: bool = False, _pause: bool = True, *, relative: bool = False, virtual: bool = False, path_function: 'PathFunction | None' = None, attempt_pixel_perfect: bool = False, disable_mouse_acceleration: bool = False) -> None:
2676def rightClick(
2677    x: int | None = None,
2678    y: int | None = None,
2679    interval: float = 0.0,
2680    duration: float = 0.0,
2681    tween: Callable[[float], float] | None = None,
2682    logScreenshot: bool = False,
2683    _pause: bool = True,
2684    *,
2685    relative: bool = False,
2686    virtual: bool = False,
2687    path_function: PathFunction | None = None,
2688    attempt_pixel_perfect: bool = False,
2689    disable_mouse_acceleration: bool = False,
2690) -> None:
2691    """
2692    Click Right Mouse button.
2693
2694    See `click()` for more information
2695    """
2696    click(
2697        x,
2698        y,
2699        clicks=1,
2700        interval=interval,
2701        button=MOUSE_RIGHT,
2702        duration=duration,
2703        tween=tween,
2704        logScreenshot=logScreenshot,
2705        _pause=_pause,  # Keep _pause since this function has no input checks
2706        relative=relative,
2707        virtual=virtual,
2708        path_function=path_function,
2709        attempt_pixel_perfect=attempt_pixel_perfect,
2710        disable_mouse_acceleration=disable_mouse_acceleration,
2711    )
2712    # --------------------------------------------------------------------------

Click Right Mouse button.

See click() for more information

def middleClick( x: int | None = None, y: int | None = None, interval: float = 0.0, duration: float = 0.0, tween: Callable[[float], float] | None = None, logScreenshot: bool = False, _pause: bool = True, *, relative: bool = False, virtual: bool = False, path_function: 'PathFunction | None' = None, attempt_pixel_perfect: bool = False, disable_mouse_acceleration: bool = False) -> None:
2716def middleClick(
2717    x: int | None = None,
2718    y: int | None = None,
2719    interval: float = 0.0,
2720    duration: float = 0.0,
2721    tween: Callable[[float], float] | None = None,
2722    logScreenshot: bool = False,
2723    _pause: bool = True,
2724    *,
2725    relative: bool = False,
2726    virtual: bool = False,
2727    path_function: PathFunction | None = None,
2728    attempt_pixel_perfect: bool = False,
2729    disable_mouse_acceleration: bool = False,
2730) -> None:
2731    """
2732    Click Middle Mouse button.
2733
2734    See `click()` for more information
2735    """
2736    click(
2737        x,
2738        y,
2739        clicks=1,
2740        interval=interval,
2741        button=MOUSE_MIDDLE,
2742        duration=duration,
2743        tween=tween,
2744        logScreenshot=logScreenshot,
2745        _pause=_pause,  # Keep _pause since this function has no input checks
2746        relative=relative,
2747        virtual=virtual,
2748        path_function=path_function,
2749        attempt_pixel_perfect=attempt_pixel_perfect,
2750        disable_mouse_acceleration=disable_mouse_acceleration,
2751    )
2752    # --------------------------------------------------------------------------

Click Middle Mouse button.

See click() for more information

def doubleClick( x: int | None = None, y: int | None = None, interval: float = 0.0, button: str = 'left', duration: float = 0.0, tween: Callable[[float], float] | None = None, logScreenshot: bool = False, _pause: bool = True, *, relative: bool = False, virtual: bool = False, path_function: 'PathFunction | None' = None, attempt_pixel_perfect: bool = False, disable_mouse_acceleration: bool = False) -> None:
2756def doubleClick(
2757    x: int | None = None,
2758    y: int | None = None,
2759    interval: float = 0.0,
2760    button: str = MOUSE_LEFT,
2761    duration: float = 0.0,
2762    tween: Callable[[float], float] | None = None,
2763    logScreenshot: bool = False,
2764    _pause: bool = True,
2765    *,
2766    relative: bool = False,
2767    virtual: bool = False,
2768    path_function: PathFunction | None = None,
2769    attempt_pixel_perfect: bool = False,
2770    disable_mouse_acceleration: bool = False,
2771) -> None:
2772    """
2773    Double click `button`.
2774
2775    See `click()` for more information
2776    """
2777    click(
2778        x,
2779        y,
2780        clicks=2,
2781        interval=interval,
2782        button=button,
2783        duration=duration,
2784        tween=tween,
2785        logScreenshot=logScreenshot,
2786        _pause=_pause,  # Keep _pause since this function has no input checks
2787        relative=relative,
2788        virtual=virtual,
2789        path_function=path_function,
2790        attempt_pixel_perfect=attempt_pixel_perfect,
2791        disable_mouse_acceleration=disable_mouse_acceleration,
2792    )
2793    # --------------------------------------------------------------------------

Double click button.

See click() for more information

def tripleClick( x: int | None = None, y: int | None = None, interval: float = 0.0, button: str = 'left', duration: float = 0.0, tween: Callable[[float], float] | None = None, logScreenshot: bool = False, _pause: bool = True, *, relative: bool = False, virtual: bool = False, path_function: 'PathFunction | None' = None, attempt_pixel_perfect: bool = False, disable_mouse_acceleration: bool = False) -> None:
2797def tripleClick(
2798    x: int | None = None,
2799    y: int | None = None,
2800    interval: float = 0.0,
2801    button: str = MOUSE_LEFT,
2802    duration: float = 0.0,
2803    tween: Callable[[float], float] | None = None,
2804    logScreenshot: bool = False,
2805    _pause: bool = True,
2806    *,
2807    relative: bool = False,
2808    virtual: bool = False,
2809    path_function: PathFunction | None = None,
2810    attempt_pixel_perfect: bool = False,
2811    disable_mouse_acceleration: bool = False,
2812) -> None:
2813    """
2814    Triple click `button`.
2815
2816    See `click()` for more information
2817    """
2818    click(
2819        x,
2820        y,
2821        clicks=3,
2822        interval=interval,
2823        button=button,
2824        duration=duration,
2825        tween=tween,
2826        logScreenshot=logScreenshot,
2827        _pause=_pause,  # Keep _pause since this function has no input checks
2828        relative=relative,
2829        virtual=virtual,
2830        path_function=path_function,
2831        attempt_pixel_perfect=attempt_pixel_perfect,
2832        disable_mouse_acceleration=disable_mouse_acceleration,
2833    )
2834    # --------------------------------------------------------------------------

Triple click button.

See click() for more information

def scroll( clicks: int = 0, x: Any = None, y: Any = None, logScreenshot: bool = False, _pause: bool = True, *, interval: float = 0.0) -> None:
2840@_genericPyDirectInputChecks
2841def scroll(
2842    clicks: int = 0,
2843    x: Any = None,  # x and y do absolutely nothing, still keeping the arguments
2844    y: Any = None,  # to stay consistent with PyAutoGUI.
2845    logScreenshot: bool = False,
2846    _pause: bool = True,
2847    *,
2848    interval: float = 0.0,
2849) -> None:
2850    """
2851    Vertically scroll mouse `clicks` number of times, waiting `interval`
2852    seconds between every scroll.
2853
2854    Negative values of `clicks` will scroll down, postive values will scroll
2855    up.
2856
2857    `x` and `y` are intentionally ignored and only exists to keep the call
2858    signature backwards-compatible with PyAutoGui.
2859    If you need to change the mouse position before scrolling use one of the
2860    `move()` functions.
2861
2862    If `_pause` is True (default), then an automatic sleep will be performed
2863    after the function finshes executing. The duration is set by the global
2864    variable `PAUSE`.
2865
2866    ----------------------------------------------------------------------------
2867
2868    NOTE: `logScreenshot` is currently unsupported.
2869    """
2870    direction: Literal[-1, 1]
2871    if clicks >= 0:
2872        direction = 1
2873    else:
2874        direction = -1
2875        clicks = abs(clicks)
2876
2877    apply_interval: bool = False
2878    for _ in range(clicks):
2879        if apply_interval:
2880            _sleep(interval)
2881        apply_interval = True
2882
2883        _send_input(
2884            _create_mouse_input(
2885                mouseData=(direction * _WHEEL_DELTA), dwFlags=_MOUSEEVENTF_WHEEL
2886            )
2887        )
2888    # --------------------------------------------------------------------------

Vertically scroll mouse clicks number of times, waiting interval seconds between every scroll.

Negative values of clicks will scroll down, postive values will scroll up.

x and y are intentionally ignored and only exists to keep the call signature backwards-compatible with PyAutoGui. If you need to change the mouse position before scrolling use one of the move() functions.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.


NOTE: logScreenshot is currently unsupported.

def hscroll( clicks: int = 0, x: Any = None, y: Any = None, logScreenshot: bool = False, _pause: bool = True, *, interval: float = 0.0) -> None:
2892@_genericPyDirectInputChecks
2893def hscroll(
2894    clicks: int = 0,
2895    x: Any = None,  # x and y do absolutely nothing, still keeping the arguments
2896    y: Any = None,  # to stay consistent with PyAutoGUI.
2897    logScreenshot: bool = False,
2898    _pause: bool = True,
2899    *,
2900    interval: float = 0.0,
2901) -> None:
2902    """
2903    Horizontally scroll mouse `clicks` number of times, waiting `interval`
2904    seconds between every scroll.
2905
2906    Negative values of `clicks` will scroll left, postive values will scroll
2907    right.
2908
2909    `x` and `y` are intentionally ignored and only exists to keep the call
2910    signature backwards-compatible with PyAutoGui.
2911    If you need to change the mouse position before scrolling use one of the
2912    `move()` functions.
2913
2914    If `_pause` is True (default), then an automatic sleep will be performed
2915    after the function finshes executing. The duration is set by the global
2916    variable `PAUSE`.
2917
2918    ----------------------------------------------------------------------------
2919
2920    NOTE: `logScreenshot` is currently unsupported.
2921    """
2922    direction: Literal[-1, 1]
2923    if clicks >= 0:
2924        direction = 1
2925    else:
2926        direction = -1
2927        clicks = abs(clicks)
2928
2929    apply_interval: bool = False
2930    for _ in range(clicks):
2931        if apply_interval:
2932            _sleep(interval)
2933        apply_interval = True
2934
2935        _send_input(
2936            _create_mouse_input(
2937                mouseData=(direction * _WHEEL_DELTA),
2938                dwFlags=_MOUSEEVENTF_HWHEEL,
2939            )
2940        )
2941    # --------------------------------------------------------------------------

Horizontally scroll mouse clicks number of times, waiting interval seconds between every scroll.

Negative values of clicks will scroll left, postive values will scroll right.

x and y are intentionally ignored and only exists to keep the call signature backwards-compatible with PyAutoGui. If you need to change the mouse position before scrolling use one of the move() functions.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.


NOTE: logScreenshot is currently unsupported.

def vscroll( clicks: int = 0, x: Any = None, y: Any = None, logScreenshot: bool = False, _pause: bool = True, *, interval: float = 0.0) -> None:
2840@_genericPyDirectInputChecks
2841def scroll(
2842    clicks: int = 0,
2843    x: Any = None,  # x and y do absolutely nothing, still keeping the arguments
2844    y: Any = None,  # to stay consistent with PyAutoGUI.
2845    logScreenshot: bool = False,
2846    _pause: bool = True,
2847    *,
2848    interval: float = 0.0,
2849) -> None:
2850    """
2851    Vertically scroll mouse `clicks` number of times, waiting `interval`
2852    seconds between every scroll.
2853
2854    Negative values of `clicks` will scroll down, postive values will scroll
2855    up.
2856
2857    `x` and `y` are intentionally ignored and only exists to keep the call
2858    signature backwards-compatible with PyAutoGui.
2859    If you need to change the mouse position before scrolling use one of the
2860    `move()` functions.
2861
2862    If `_pause` is True (default), then an automatic sleep will be performed
2863    after the function finshes executing. The duration is set by the global
2864    variable `PAUSE`.
2865
2866    ----------------------------------------------------------------------------
2867
2868    NOTE: `logScreenshot` is currently unsupported.
2869    """
2870    direction: Literal[-1, 1]
2871    if clicks >= 0:
2872        direction = 1
2873    else:
2874        direction = -1
2875        clicks = abs(clicks)
2876
2877    apply_interval: bool = False
2878    for _ in range(clicks):
2879        if apply_interval:
2880            _sleep(interval)
2881        apply_interval = True
2882
2883        _send_input(
2884            _create_mouse_input(
2885                mouseData=(direction * _WHEEL_DELTA), dwFlags=_MOUSEEVENTF_WHEEL
2886            )
2887        )
2888    # --------------------------------------------------------------------------

Vertically scroll mouse clicks number of times, waiting interval seconds between every scroll.

Negative values of clicks will scroll down, postive values will scroll up.

x and y are intentionally ignored and only exists to keep the call signature backwards-compatible with PyAutoGui. If you need to change the mouse position before scrolling use one of the move() functions.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.


NOTE: logScreenshot is currently unsupported.

def moveTo( x: int | None = None, y: int | None = None, duration: float = 0.0, tween: Callable[[float], float] | None = None, logScreenshot: bool = False, _pause: bool = True, relative: bool = False, *, virtual: bool = False, path_function: 'PathFunction | None' = None, attempt_pixel_perfect: bool = False, disable_mouse_acceleration: bool = False) -> None:
3354@_genericPyDirectInputChecks
3355def moveTo(
3356    x: int | None = None,
3357    y: int | None = None,
3358    duration: float = 0.0,
3359    tween: Callable[[float], float] | None = None,
3360    logScreenshot: bool = False,
3361    _pause: bool = True,
3362    relative: bool = False,
3363    *,
3364    virtual: bool = False,
3365    path_function: PathFunction | None = None,
3366    attempt_pixel_perfect: bool = False,
3367    disable_mouse_acceleration: bool = False,
3368) -> None:
3369    """
3370    Move the mouse to an absolute(*) postion indicated by the arguments of
3371    `x` and `y`. The coordinates 0,0 represent the top left pixel of the
3372    primary monitor.
3373
3374    If `duration` is floating point number greater than 0, then this function
3375    will automatically split the movement into microsteps instead of moving
3376    straight to the target position.
3377
3378    `tween` is a function that takes a floating point number between 0.0 and
3379    1.0 and returns another floating point number between 0.0 and 1.0. The
3380    returned number will be used to calculate the next position of the
3381    mouse between the start and the end position based on the current duration.
3382    The default tweening function is linear, which will move the mouse at a
3383    constant speed. See the `pytweening` package for more tweening functions.
3384    Be aware that tweening only controls to the timing of movement, not the
3385    path itself. If you need to non-linear movement (e.g. overshoot), then
3386    use the `path_function` argument.
3387
3388    `path_function` is a function that takes the start and end coordinates of
3389    the mouse movement (4 integers) and returns a list of coordinates (list of
3390    tuples, each containting 2 integers (x,y)) that the mouse will move 
3391    through. The default path function is Bresenham's line algorithm, which 
3392    will move the mouse in a straight line.
3393
3394    (*) `relative` parameter decides how the movement is executed:
3395
3396    -> `False`: Target postion is given and absolute movement is used.
3397
3398    -> `True`: Calculates target offset and uses relative movement API
3399    (can be inconsistent)
3400
3401    If `_pause` is True (default), then an automatic sleep will be performed
3402    after the function finshes executing. The duration is set by the global
3403    variable `PAUSE`.
3404
3405    Setting `virtual` to True (default: False) changes the way internal APIs
3406    handle coordinates and is intended for multi monitor systems. It should be
3407    pretty much unncessary even for multi monitor systems, since all the
3408    necessary internal calculations beyond the border of the primay monitor
3409    work without it.
3410
3411    The way that Windows calculates the target pixel coordinates internally
3412    unfortunately leads to inaccuracies and unreachable pixels, especially
3413    if the `virtual` option is used.
3414
3415    If you need the target position to be pixel perfect, you can try setting
3416    `attempt_pixel_perfect` to True, which will use tiny relative movements
3417    to correct the unreachable position.
3418
3419    Relative movement is influenced by mouse speed and Windows Enhanced Pointer
3420    Precision, which can be temporarily disabled by setting
3421    `disable_mouse_acceleration`.
3422
3423    ----------------------------------------------------------------------------
3424    Careful! Disabling mouse acceleration settings is MAYBE thread-safe,
3425    NOT multiprocessing-safe, and DEFINITELY NOT independent processses safe!
3426
3427    If you you start a relative movement while another is already in progress
3428    than the second movement could overwrite the first setting and disable
3429    Enhanced Pointer Precision and change mouse speed.
3430    There are some measures in place to try to mitigate that risk, such as an
3431    internal counter that only allows storing and restoring the acceleration
3432    settings as long as no other movement is currently in progress.
3433    Additionally, the acceleration settings can be manually saved and
3434    restored with `store_mouse_acceleration_settings()` and
3435    `restore_mouse_acceleration_settings()`. For your convenience, the
3436    store function is automatically called during import to save your current
3437    setting. You can then call the restore function at any time.
3438
3439    If all fails, the setting is not written permanently to your Windows
3440    settings, so it should restore itself upon reboot.
3441
3442    Bottom line: Don't use the `disable_mouse_acceleration` argument if you use
3443    this library in multiple threads / processes / programs at the same time!
3444
3445    ----------------------------------------------------------------------------
3446
3447    NOTE: `logScreenshot` is currently unsupported.
3448    """
3449    if relative:
3450        _relative_mouse_move(
3451            x=x,
3452            y=y,
3453            duration=duration,
3454            tween=tween,
3455            logScreenshot=logScreenshot,
3456            target_coords_relative=False,
3457            virtual=virtual,
3458            path_function=path_function,
3459            disable_mouse_acceleration=disable_mouse_acceleration,
3460        )
3461    else:
3462        _absolute_mouse_move(
3463            x=x,
3464            y=y,
3465            duration=duration,
3466            tween=tween,
3467            logScreenshot=logScreenshot,
3468            target_coords_relative=False,
3469            virtual=virtual,
3470            path_function=path_function,
3471            attempt_pixel_perfect=attempt_pixel_perfect,
3472            disable_mouse_acceleration=disable_mouse_acceleration,
3473        )
3474    # --------------------------------------------------------------------------

Move the mouse to an absolute(*) postion indicated by the arguments of x and y. The coordinates 0,0 represent the top left pixel of the primary monitor.

If duration is floating point number greater than 0, then this function will automatically split the movement into microsteps instead of moving straight to the target position.

tween is a function that takes a floating point number between 0.0 and 1.0 and returns another floating point number between 0.0 and 1.0. The returned number will be used to calculate the next position of the mouse between the start and the end position based on the current duration. The default tweening function is linear, which will move the mouse at a constant speed. See the pytweening package for more tweening functions. Be aware that tweening only controls to the timing of movement, not the path itself. If you need to non-linear movement (e.g. overshoot), then use the path_function argument.

path_function is a function that takes the start and end coordinates of the mouse movement (4 integers) and returns a list of coordinates (list of tuples, each containting 2 integers (x,y)) that the mouse will move through. The default path function is Bresenham's line algorithm, which will move the mouse in a straight line.

(*) relative parameter decides how the movement is executed:

-> False: Target postion is given and absolute movement is used.

-> True: Calculates target offset and uses relative movement API (can be inconsistent)

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.

Setting virtual to True (default: False) changes the way internal APIs handle coordinates and is intended for multi monitor systems. It should be pretty much unncessary even for multi monitor systems, since all the necessary internal calculations beyond the border of the primay monitor work without it.

The way that Windows calculates the target pixel coordinates internally unfortunately leads to inaccuracies and unreachable pixels, especially if the virtual option is used.

If you need the target position to be pixel perfect, you can try setting attempt_pixel_perfect to True, which will use tiny relative movements to correct the unreachable position.

Relative movement is influenced by mouse speed and Windows Enhanced Pointer Precision, which can be temporarily disabled by setting disable_mouse_acceleration.


Careful! Disabling mouse acceleration settings is MAYBE thread-safe, NOT multiprocessing-safe, and DEFINITELY NOT independent processses safe!

If you you start a relative movement while another is already in progress than the second movement could overwrite the first setting and disable Enhanced Pointer Precision and change mouse speed. There are some measures in place to try to mitigate that risk, such as an internal counter that only allows storing and restoring the acceleration settings as long as no other movement is currently in progress. Additionally, the acceleration settings can be manually saved and restored with store_mouse_acceleration_settings() and restore_mouse_acceleration_settings(). For your convenience, the store function is automatically called during import to save your current setting. You can then call the restore function at any time.

If all fails, the setting is not written permanently to your Windows settings, so it should restore itself upon reboot.

Bottom line: Don't use the disable_mouse_acceleration argument if you use this library in multiple threads / processes / programs at the same time!


NOTE: logScreenshot is currently unsupported.

def moveRel( xOffset: int | None = None, yOffset: int | None = None, duration: float = 0.0, tween: Callable[[float], float] | None = None, logScreenshot: bool = False, _pause: bool = True, relative: bool = False, *, virtual: bool = False, path_function: 'PathFunction | None' = None, attempt_pixel_perfect: bool = False, disable_mouse_acceleration: bool = False) -> None:
3478@_genericPyDirectInputChecks
3479def moveRel(
3480    xOffset: int | None = None,
3481    yOffset: int | None = None,
3482    duration: float = 0.0,
3483    tween: Callable[[float], float] | None = None,
3484    logScreenshot: bool = False,
3485    _pause: bool = True,
3486    relative: bool = False,
3487    *,
3488    virtual: bool = False,
3489    path_function: PathFunction | None = None,
3490    attempt_pixel_perfect: bool = False,
3491    disable_mouse_acceleration: bool = False,
3492) -> None:
3493    """
3494    Move the mouse a relative amount determined by `xOffset` and `yOffset`.
3495
3496    If `duration` is floating point number greater than 0, then this function
3497    will automatically split the movement into microsteps instead of moving the
3498    complete distance instantly.
3499
3500    `tween` is a function that takes a floating point number between 0.0 and
3501    1.0 and returns another floating point number between 0.0 and 1.0. The
3502    returned number will be used to calculate the next position of the
3503    mouse between the start and the end position based on the current duration.
3504    The default tweening function is linear, which will move the mouse at a
3505    constant speed. See the `pytweening` package for more tweening functions.
3506    Be aware that tweening only controls to the timing of movement, not the
3507    path itself. If you need to non-linear movement (e.g. overshoot), then
3508    use the `path_function` argument.
3509
3510    `path_function` is a function that takes the start and end coordinates of
3511    the mouse movement (4 integers) and returns a list of coordinates (list of
3512    tuples, each containting 2 integers (x,y)) that the mouse will move 
3513    through. The default path function is Bresenham's line algorithm, which 
3514    will move the mouse in a straight line.
3515
3516    `relative` parameter decides how the movement is executed:
3517
3518    -> `False`: Target postion is calculated and absolute movement is used.
3519
3520    -> `True`: Target offset is given and relative movement API is used
3521    (can be inconsistent)
3522
3523    The inconsistency issue can be solved by disabling Enhanced Pointer
3524    Precision and set Mouse speed to 10 in Windows mouse settings. Since users
3525    may not want to permanently change their input settings just for this
3526    library, the `disable_mouse_acceleration` argument can be used to
3527    temporarily disable Enhanced Pointer Precision and fix mouse speed at 10
3528    and restore it after the mouse movement.
3529
3530    If `_pause` is True (default), then an automatic sleep will be performed
3531    after the function finshes executing. The duration is set by the global
3532    variable `PAUSE`.
3533
3534    Setting `virtual` to True (default: False) changes the way internal APIs
3535    handle coordinates and is intended for multi monitor systems. It should be
3536    pretty much unncessary even for multi monitor systems, since all the
3537    necessary internal calculations beyond the border of the primay monitor
3538    work without it.
3539
3540    The way that Windows calculates the target pixel coordinates internally
3541    unfortunately leads to inaccuracies and unreachable pixels, especially
3542    if the `virtual` option is used.
3543
3544    If you need the target position to be pixel perfect, you can try setting
3545    `attempt_pixel_perfect` to True, which will use tiny relative movements
3546    to correct the unreachable position.
3547
3548    ----------------------------------------------------------------------------
3549    Careful! Disabling mouse acceleration settings is MAYBE thread-safe,
3550    NOT multiprocessing-safe, and DEFINITELY NOT independent processses safe!
3551
3552    If you you start a relative movement while another is already in progress
3553    than the second movement could overwrite the first setting and disable
3554    Enhanced Pointer Precision and change mouse speed.
3555    There are some measures in place to try to mitigate that risk, such as an
3556    internal counter that only allows storing and restoring the acceleration
3557    settings as long as no other movement is currently in progress.
3558    Additionally, the acceleration settings can be manually saved and
3559    restored with `store_mouse_acceleration_settings()` and
3560    `restore_mouse_acceleration_settings()`. For your convinnience, the
3561    store function is automatically called during import to save your current
3562    setting. You can then call the restore function at any time.
3563
3564    If all fails, the setting is not written permanently to your Windows
3565    settings, so it should restore itself upon reboot.
3566
3567    Bottom line: Don't use the `disable_mouse_acceleration` argument if you use
3568    this library in multiple threads / processes / programs at the same time!
3569
3570    ----------------------------------------------------------------------------
3571
3572    NOTE: `logScreenshot` is are currently unsupported.
3573    """
3574    if relative:
3575        _relative_mouse_move(
3576            x=xOffset,
3577            y=yOffset,
3578            duration=duration,
3579            tween=tween,
3580            logScreenshot=logScreenshot,
3581            target_coords_relative=True,
3582            virtual=virtual,
3583            path_function=path_function,
3584            disable_mouse_acceleration=disable_mouse_acceleration,
3585        )
3586    else:
3587        _absolute_mouse_move(
3588            x=xOffset,
3589            y=yOffset,
3590            duration=duration,
3591            tween=tween,
3592            logScreenshot=logScreenshot,
3593            target_coords_relative=True,
3594            virtual=virtual,
3595            path_function=path_function,
3596            attempt_pixel_perfect=attempt_pixel_perfect,
3597            disable_mouse_acceleration=disable_mouse_acceleration,
3598        )
3599    # --------------------------------------------------------------------------

Move the mouse a relative amount determined by xOffset and yOffset.

If duration is floating point number greater than 0, then this function will automatically split the movement into microsteps instead of moving the complete distance instantly.

tween is a function that takes a floating point number between 0.0 and 1.0 and returns another floating point number between 0.0 and 1.0. The returned number will be used to calculate the next position of the mouse between the start and the end position based on the current duration. The default tweening function is linear, which will move the mouse at a constant speed. See the pytweening package for more tweening functions. Be aware that tweening only controls to the timing of movement, not the path itself. If you need to non-linear movement (e.g. overshoot), then use the path_function argument.

path_function is a function that takes the start and end coordinates of the mouse movement (4 integers) and returns a list of coordinates (list of tuples, each containting 2 integers (x,y)) that the mouse will move through. The default path function is Bresenham's line algorithm, which will move the mouse in a straight line.

relative parameter decides how the movement is executed:

-> False: Target postion is calculated and absolute movement is used.

-> True: Target offset is given and relative movement API is used (can be inconsistent)

The inconsistency issue can be solved by disabling Enhanced Pointer Precision and set Mouse speed to 10 in Windows mouse settings. Since users may not want to permanently change their input settings just for this library, the disable_mouse_acceleration argument can be used to temporarily disable Enhanced Pointer Precision and fix mouse speed at 10 and restore it after the mouse movement.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.

Setting virtual to True (default: False) changes the way internal APIs handle coordinates and is intended for multi monitor systems. It should be pretty much unncessary even for multi monitor systems, since all the necessary internal calculations beyond the border of the primay monitor work without it.

The way that Windows calculates the target pixel coordinates internally unfortunately leads to inaccuracies and unreachable pixels, especially if the virtual option is used.

If you need the target position to be pixel perfect, you can try setting attempt_pixel_perfect to True, which will use tiny relative movements to correct the unreachable position.


Careful! Disabling mouse acceleration settings is MAYBE thread-safe, NOT multiprocessing-safe, and DEFINITELY NOT independent processses safe!

If you you start a relative movement while another is already in progress than the second movement could overwrite the first setting and disable Enhanced Pointer Precision and change mouse speed. There are some measures in place to try to mitigate that risk, such as an internal counter that only allows storing and restoring the acceleration settings as long as no other movement is currently in progress. Additionally, the acceleration settings can be manually saved and restored with store_mouse_acceleration_settings() and restore_mouse_acceleration_settings(). For your convinnience, the store function is automatically called during import to save your current setting. You can then call the restore function at any time.

If all fails, the setting is not written permanently to your Windows settings, so it should restore itself upon reboot.

Bottom line: Don't use the disable_mouse_acceleration argument if you use this library in multiple threads / processes / programs at the same time!


NOTE: logScreenshot is are currently unsupported.

def move( xOffset: int | None = None, yOffset: int | None = None, duration: float = 0.0, tween: Callable[[float], float] | None = None, logScreenshot: bool = False, _pause: bool = True, relative: bool = False, *, virtual: bool = False, path_function: 'PathFunction | None' = None, attempt_pixel_perfect: bool = False, disable_mouse_acceleration: bool = False) -> None:
3478@_genericPyDirectInputChecks
3479def moveRel(
3480    xOffset: int | None = None,
3481    yOffset: int | None = None,
3482    duration: float = 0.0,
3483    tween: Callable[[float], float] | None = None,
3484    logScreenshot: bool = False,
3485    _pause: bool = True,
3486    relative: bool = False,
3487    *,
3488    virtual: bool = False,
3489    path_function: PathFunction | None = None,
3490    attempt_pixel_perfect: bool = False,
3491    disable_mouse_acceleration: bool = False,
3492) -> None:
3493    """
3494    Move the mouse a relative amount determined by `xOffset` and `yOffset`.
3495
3496    If `duration` is floating point number greater than 0, then this function
3497    will automatically split the movement into microsteps instead of moving the
3498    complete distance instantly.
3499
3500    `tween` is a function that takes a floating point number between 0.0 and
3501    1.0 and returns another floating point number between 0.0 and 1.0. The
3502    returned number will be used to calculate the next position of the
3503    mouse between the start and the end position based on the current duration.
3504    The default tweening function is linear, which will move the mouse at a
3505    constant speed. See the `pytweening` package for more tweening functions.
3506    Be aware that tweening only controls to the timing of movement, not the
3507    path itself. If you need to non-linear movement (e.g. overshoot), then
3508    use the `path_function` argument.
3509
3510    `path_function` is a function that takes the start and end coordinates of
3511    the mouse movement (4 integers) and returns a list of coordinates (list of
3512    tuples, each containting 2 integers (x,y)) that the mouse will move 
3513    through. The default path function is Bresenham's line algorithm, which 
3514    will move the mouse in a straight line.
3515
3516    `relative` parameter decides how the movement is executed:
3517
3518    -> `False`: Target postion is calculated and absolute movement is used.
3519
3520    -> `True`: Target offset is given and relative movement API is used
3521    (can be inconsistent)
3522
3523    The inconsistency issue can be solved by disabling Enhanced Pointer
3524    Precision and set Mouse speed to 10 in Windows mouse settings. Since users
3525    may not want to permanently change their input settings just for this
3526    library, the `disable_mouse_acceleration` argument can be used to
3527    temporarily disable Enhanced Pointer Precision and fix mouse speed at 10
3528    and restore it after the mouse movement.
3529
3530    If `_pause` is True (default), then an automatic sleep will be performed
3531    after the function finshes executing. The duration is set by the global
3532    variable `PAUSE`.
3533
3534    Setting `virtual` to True (default: False) changes the way internal APIs
3535    handle coordinates and is intended for multi monitor systems. It should be
3536    pretty much unncessary even for multi monitor systems, since all the
3537    necessary internal calculations beyond the border of the primay monitor
3538    work without it.
3539
3540    The way that Windows calculates the target pixel coordinates internally
3541    unfortunately leads to inaccuracies and unreachable pixels, especially
3542    if the `virtual` option is used.
3543
3544    If you need the target position to be pixel perfect, you can try setting
3545    `attempt_pixel_perfect` to True, which will use tiny relative movements
3546    to correct the unreachable position.
3547
3548    ----------------------------------------------------------------------------
3549    Careful! Disabling mouse acceleration settings is MAYBE thread-safe,
3550    NOT multiprocessing-safe, and DEFINITELY NOT independent processses safe!
3551
3552    If you you start a relative movement while another is already in progress
3553    than the second movement could overwrite the first setting and disable
3554    Enhanced Pointer Precision and change mouse speed.
3555    There are some measures in place to try to mitigate that risk, such as an
3556    internal counter that only allows storing and restoring the acceleration
3557    settings as long as no other movement is currently in progress.
3558    Additionally, the acceleration settings can be manually saved and
3559    restored with `store_mouse_acceleration_settings()` and
3560    `restore_mouse_acceleration_settings()`. For your convinnience, the
3561    store function is automatically called during import to save your current
3562    setting. You can then call the restore function at any time.
3563
3564    If all fails, the setting is not written permanently to your Windows
3565    settings, so it should restore itself upon reboot.
3566
3567    Bottom line: Don't use the `disable_mouse_acceleration` argument if you use
3568    this library in multiple threads / processes / programs at the same time!
3569
3570    ----------------------------------------------------------------------------
3571
3572    NOTE: `logScreenshot` is are currently unsupported.
3573    """
3574    if relative:
3575        _relative_mouse_move(
3576            x=xOffset,
3577            y=yOffset,
3578            duration=duration,
3579            tween=tween,
3580            logScreenshot=logScreenshot,
3581            target_coords_relative=True,
3582            virtual=virtual,
3583            path_function=path_function,
3584            disable_mouse_acceleration=disable_mouse_acceleration,
3585        )
3586    else:
3587        _absolute_mouse_move(
3588            x=xOffset,
3589            y=yOffset,
3590            duration=duration,
3591            tween=tween,
3592            logScreenshot=logScreenshot,
3593            target_coords_relative=True,
3594            virtual=virtual,
3595            path_function=path_function,
3596            attempt_pixel_perfect=attempt_pixel_perfect,
3597            disable_mouse_acceleration=disable_mouse_acceleration,
3598        )
3599    # --------------------------------------------------------------------------

Move the mouse a relative amount determined by xOffset and yOffset.

If duration is floating point number greater than 0, then this function will automatically split the movement into microsteps instead of moving the complete distance instantly.

tween is a function that takes a floating point number between 0.0 and 1.0 and returns another floating point number between 0.0 and 1.0. The returned number will be used to calculate the next position of the mouse between the start and the end position based on the current duration. The default tweening function is linear, which will move the mouse at a constant speed. See the pytweening package for more tweening functions. Be aware that tweening only controls to the timing of movement, not the path itself. If you need to non-linear movement (e.g. overshoot), then use the path_function argument.

path_function is a function that takes the start and end coordinates of the mouse movement (4 integers) and returns a list of coordinates (list of tuples, each containting 2 integers (x,y)) that the mouse will move through. The default path function is Bresenham's line algorithm, which will move the mouse in a straight line.

relative parameter decides how the movement is executed:

-> False: Target postion is calculated and absolute movement is used.

-> True: Target offset is given and relative movement API is used (can be inconsistent)

The inconsistency issue can be solved by disabling Enhanced Pointer Precision and set Mouse speed to 10 in Windows mouse settings. Since users may not want to permanently change their input settings just for this library, the disable_mouse_acceleration argument can be used to temporarily disable Enhanced Pointer Precision and fix mouse speed at 10 and restore it after the mouse movement.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.

Setting virtual to True (default: False) changes the way internal APIs handle coordinates and is intended for multi monitor systems. It should be pretty much unncessary even for multi monitor systems, since all the necessary internal calculations beyond the border of the primay monitor work without it.

The way that Windows calculates the target pixel coordinates internally unfortunately leads to inaccuracies and unreachable pixels, especially if the virtual option is used.

If you need the target position to be pixel perfect, you can try setting attempt_pixel_perfect to True, which will use tiny relative movements to correct the unreachable position.


Careful! Disabling mouse acceleration settings is MAYBE thread-safe, NOT multiprocessing-safe, and DEFINITELY NOT independent processses safe!

If you you start a relative movement while another is already in progress than the second movement could overwrite the first setting and disable Enhanced Pointer Precision and change mouse speed. There are some measures in place to try to mitigate that risk, such as an internal counter that only allows storing and restoring the acceleration settings as long as no other movement is currently in progress. Additionally, the acceleration settings can be manually saved and restored with store_mouse_acceleration_settings() and restore_mouse_acceleration_settings(). For your convinnience, the store function is automatically called during import to save your current setting. You can then call the restore function at any time.

If all fails, the setting is not written permanently to your Windows settings, so it should restore itself upon reboot.

Bottom line: Don't use the disable_mouse_acceleration argument if you use this library in multiple threads / processes / programs at the same time!


NOTE: logScreenshot is are currently unsupported.

def dragTo( x: int | None = None, y: int | None = None, duration: float = 0.0, tween: Callable[[float], float] | None = None, button: str | None = None, logScreenshot: bool = False, _pause: bool = True, mouseDownUp: bool = True, *, relative: bool = False, virtual: bool = False, path_function: 'PathFunction | None' = None, attempt_pixel_perfect: bool = False, disable_mouse_acceleration: bool = False) -> None:
3609@_genericPyDirectInputChecks
3610def dragTo(
3611    x: int | None = None,
3612    y: int | None = None,
3613    duration: float = 0.0,
3614    tween: Callable[[float], float] | None = None,
3615    button: str | None = None,
3616    logScreenshot: bool = False,
3617    _pause: bool = True,
3618    mouseDownUp: bool = True,
3619    *,
3620    relative: bool = False,
3621    virtual: bool = False,
3622    path_function: PathFunction | None = None,
3623    attempt_pixel_perfect: bool = False,
3624    disable_mouse_acceleration: bool = False,
3625) -> None:
3626    """
3627    Press and hold a mouse button while moving to the target coordinates.
3628
3629    See `moveTo` for more information on most arguments.
3630
3631    `button` is a string that is one of the following constants:
3632    MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE, MOUSE_BUTTON4, MOUSE_BUTTON5,
3633    MOUSE_PRIMARY (default), MOUSE_SECONDARY.
3634
3635    If `mouseDownUp` (default: True) is manually set to False, then this
3636    function is basically the same as `moveTo`. Only exists to match PyAutoGUI.
3637
3638    If `_pause` is True (default), then an automatic sleep will be performed
3639    after the function finshes executing. The duration is set by the global
3640    variable `PAUSE`.
3641
3642    ----------------------------------------------------------------------------
3643
3644    NOTE: `logScreenshot` is currently unsupported.
3645    """
3646    # TODO: bounding box check for valid position
3647    if button is None:
3648        button = MOUSE_PRIMARY
3649    if mouseDownUp:
3650        mouseDown(button=button, _pause=False, virtual=virtual)
3651    moveTo(
3652        x,
3653        y,
3654        duration=duration,
3655        tween=tween,
3656        logScreenshot=logScreenshot,
3657        _pause=False,  # don't add an additional pause
3658        relative=relative,
3659        virtual=virtual,
3660        path_function=path_function,
3661        attempt_pixel_perfect=attempt_pixel_perfect,
3662        disable_mouse_acceleration=disable_mouse_acceleration,
3663    )
3664    if mouseDownUp:
3665        mouseUp(button=button, _pause=False, virtual=virtual)
3666    # --------------------------------------------------------------------------

Press and hold a mouse button while moving to the target coordinates.

See moveTo for more information on most arguments.

button is a string that is one of the following constants: MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE, MOUSE_BUTTON4, MOUSE_BUTTON5, MOUSE_PRIMARY (default), MOUSE_SECONDARY.

If mouseDownUp (default: True) is manually set to False, then this function is basically the same as moveTo. Only exists to match PyAutoGUI.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.


NOTE: logScreenshot is currently unsupported.

def dragRel( xOffset: int | None = None, yOffset: int | None = None, duration: float = 0.0, tween: Callable[[float], float] | None = None, button: str | None = None, logScreenshot: bool = False, _pause: bool = True, mouseDownUp: bool = True, *, relative: bool = False, virtual: bool = False, path_function: 'PathFunction | None' = None, disable_mouse_acceleration: bool = False) -> None:
3670@_genericPyDirectInputChecks
3671def dragRel(
3672    xOffset: int | None = None,
3673    yOffset: int | None = None,
3674    duration: float = 0.0,
3675    tween: Callable[[float], float] | None = None,
3676    button: str | None = None,
3677    logScreenshot: bool = False,
3678    _pause: bool = True,
3679    mouseDownUp: bool = True,
3680    *,
3681    relative: bool = False,
3682    virtual: bool = False,
3683    path_function: PathFunction | None = None,
3684    disable_mouse_acceleration: bool = False,
3685) -> None:
3686    """
3687    Press and hold a mouse button while moving a relative distance
3688
3689    See `moveRel` for more information on most arguments.
3690
3691    `button` is a string that is one of the following constants:
3692    MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE, MOUSE_BUTTON4, MOUSE_BUTTON5,
3693    MOUSE_PRIMARY (default), MOUSE_SECONDARY.
3694
3695    If `mouseDownUp` (default: True) is manually set to False, then this
3696    function is basically the same as `moveTo`. Only exists to match PyAutoGUI.
3697
3698    If `_pause` is True (default), then an automatic sleep will be performed
3699    after the function finshes executing. The duration is set by the global
3700    variable `PAUSE`.
3701
3702    ----------------------------------------------------------------------------
3703
3704    NOTE: `logScreenshot` is currently unsupported.
3705    """
3706    # TODO: bounding box check for valid position
3707    if button is None:
3708        button = MOUSE_PRIMARY
3709    if mouseDownUp:
3710        mouseDown(button=button, _pause=False, virtual=virtual)
3711    moveRel(
3712        xOffset,
3713        yOffset,
3714        duration=duration,
3715        tween=tween,
3716        logScreenshot=logScreenshot,
3717        _pause=False,  # don't add an additional pause
3718        relative=relative,
3719        virtual=virtual,
3720        path_function=path_function,
3721        disable_mouse_acceleration=disable_mouse_acceleration,
3722    )
3723    if mouseDownUp:
3724        mouseUp(button=button, _pause=False, virtual=virtual)
3725    # --------------------------------------------------------------------------

Press and hold a mouse button while moving a relative distance

See moveRel for more information on most arguments.

button is a string that is one of the following constants: MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE, MOUSE_BUTTON4, MOUSE_BUTTON5, MOUSE_PRIMARY (default), MOUSE_SECONDARY.

If mouseDownUp (default: True) is manually set to False, then this function is basically the same as moveTo. Only exists to match PyAutoGUI.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.


NOTE: logScreenshot is currently unsupported.

def drag( xOffset: int | None = None, yOffset: int | None = None, duration: float = 0.0, tween: Callable[[float], float] | None = None, button: str | None = None, logScreenshot: bool = False, _pause: bool = True, mouseDownUp: bool = True, *, relative: bool = False, virtual: bool = False, path_function: 'PathFunction | None' = None, disable_mouse_acceleration: bool = False) -> None:
3670@_genericPyDirectInputChecks
3671def dragRel(
3672    xOffset: int | None = None,
3673    yOffset: int | None = None,
3674    duration: float = 0.0,
3675    tween: Callable[[float], float] | None = None,
3676    button: str | None = None,
3677    logScreenshot: bool = False,
3678    _pause: bool = True,
3679    mouseDownUp: bool = True,
3680    *,
3681    relative: bool = False,
3682    virtual: bool = False,
3683    path_function: PathFunction | None = None,
3684    disable_mouse_acceleration: bool = False,
3685) -> None:
3686    """
3687    Press and hold a mouse button while moving a relative distance
3688
3689    See `moveRel` for more information on most arguments.
3690
3691    `button` is a string that is one of the following constants:
3692    MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE, MOUSE_BUTTON4, MOUSE_BUTTON5,
3693    MOUSE_PRIMARY (default), MOUSE_SECONDARY.
3694
3695    If `mouseDownUp` (default: True) is manually set to False, then this
3696    function is basically the same as `moveTo`. Only exists to match PyAutoGUI.
3697
3698    If `_pause` is True (default), then an automatic sleep will be performed
3699    after the function finshes executing. The duration is set by the global
3700    variable `PAUSE`.
3701
3702    ----------------------------------------------------------------------------
3703
3704    NOTE: `logScreenshot` is currently unsupported.
3705    """
3706    # TODO: bounding box check for valid position
3707    if button is None:
3708        button = MOUSE_PRIMARY
3709    if mouseDownUp:
3710        mouseDown(button=button, _pause=False, virtual=virtual)
3711    moveRel(
3712        xOffset,
3713        yOffset,
3714        duration=duration,
3715        tween=tween,
3716        logScreenshot=logScreenshot,
3717        _pause=False,  # don't add an additional pause
3718        relative=relative,
3719        virtual=virtual,
3720        path_function=path_function,
3721        disable_mouse_acceleration=disable_mouse_acceleration,
3722    )
3723    if mouseDownUp:
3724        mouseUp(button=button, _pause=False, virtual=virtual)
3725    # --------------------------------------------------------------------------

Press and hold a mouse button while moving a relative distance

See moveRel for more information on most arguments.

button is a string that is one of the following constants: MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE, MOUSE_BUTTON4, MOUSE_BUTTON5, MOUSE_PRIMARY (default), MOUSE_SECONDARY.

If mouseDownUp (default: True) is manually set to False, then this function is basically the same as moveTo. Only exists to match PyAutoGUI.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.


NOTE: logScreenshot is currently unsupported.

def is_valid_key(key: str) -> bool:
3739def is_valid_key(key: str) -> bool:
3740    """
3741    Returns true if key name `key` can be translated into a valid scan code.
3742    """
3743    return key in KEYBOARD_MAPPING

Returns true if key name key can be translated into a valid scan code.

def isValidKey(key: str) -> bool:
3739def is_valid_key(key: str) -> bool:
3740    """
3741    Returns true if key name `key` can be translated into a valid scan code.
3742    """
3743    return key in KEYBOARD_MAPPING

Returns true if key name key can be translated into a valid scan code.

def scancode_keyDown( scancodes: int | ScancodeSequence, logScreenshot: None = None, _pause: bool = True, *, auto_shift: bool = False) -> bool:
3754@_genericPyDirectInputChecks
3755def scancode_keyDown(
3756    scancodes: ScancodeTypes,
3757    logScreenshot: None = None,
3758    _pause: bool = True,
3759    *,
3760    auto_shift: bool = False,
3761) -> bool:
3762    """
3763    Press down key corresponding to `scancodes`.
3764
3765    The actually pressed key will depend on your system keyboard layout.
3766    Limits the available character set but should provide the best
3767    compatibility.
3768
3769    If `_pause` is True (default), then an automatic sleep will be performed
3770    after the function finshes executing. The duration is set by the global
3771    variable `PAUSE`.
3772
3773    `auto_shift` is used internally by higher level functions to automatically
3774    press the shift key before supported scancodes (indicitated by a special
3775    bit outside the regular scancode range, while it technically can be used,
3776    it's not intended for public access).
3777
3778    ----------------------------------------------------------------------------
3779
3780    NOTE: `logScreenshot` is currently unsupported.
3781    """
3782    scancodes_sequence: ScancodeSequence
3783    if isinstance(scancodes, int):
3784        scancodes_sequence = ScancodeSequence([scancodes])
3785    else:
3786        scancodes_sequence = scancodes
3787
3788    keybdFlags: int = _KEYEVENTF_SCANCODE
3789    input_structs: list[_INPUT] = []
3790    extendedFlag: int
3791
3792    # Init event tracking
3793    insertedEvents: int = 0
3794    expectedEvents: int = 0
3795
3796    for scancode in scancodes_sequence:
3797        if auto_shift and scancode & _OFFSET_SHIFTKEY:
3798            input_structs += [
3799                _create_keyboard_input(
3800                    wScan=_SHIFT_SCANCODE, dwFlags=keybdFlags
3801                )
3802            ]
3803            expectedEvents += 1
3804
3805        scancode = scancode & 0xFFFF
3806
3807        extendedFlag = _KEYEVENTF_EXTENDEDKEY if scancode >= 0xE000 else 0
3808        input_structs += [
3809            _create_keyboard_input(
3810                wScan=scancode, dwFlags=keybdFlags | extendedFlag
3811            )
3812        ]
3813        expectedEvents += 1
3814
3815    insertedEvents += _send_input(input_structs)
3816
3817    # SendInput returns the number of event successfully inserted into
3818    # input stream
3819    # https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput#return-value
3820    return insertedEvents == expectedEvents
3821    # --------------------------------------------------------------------------

Press down key corresponding to scancodes.

The actually pressed key will depend on your system keyboard layout. Limits the available character set but should provide the best compatibility.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.

auto_shift is used internally by higher level functions to automatically press the shift key before supported scancodes (indicitated by a special bit outside the regular scancode range, while it technically can be used, it's not intended for public access).


NOTE: logScreenshot is currently unsupported.

def scancode_keyUp( scancodes: int | ScancodeSequence, logScreenshot: None = None, _pause: bool = True, *, auto_shift: bool = False) -> bool:
3825@_genericPyDirectInputChecks
3826def scancode_keyUp(
3827    scancodes: ScancodeTypes,
3828    logScreenshot: None = None,
3829    _pause: bool = True,
3830    *,
3831    auto_shift: bool = False,
3832) -> bool:
3833    """
3834    Release key corresponding to `scancodes`.
3835
3836    The actually pressed key will depend on your system keyboard layout.
3837    Limits the available character set but should provide the best
3838    compatibility.
3839
3840    If `_pause` is True (default), then an automatic sleep will be performed
3841    after the function finshes executing. The duration is set by the global
3842    variable `PAUSE`.
3843
3844    `auto_shift` is used internally by higher level functions to automatically
3845    press the shift key before supported scancodes (indicitated by a special
3846    bit outside the regular scancode range, while it technically can be used,
3847    it's not intended for public access).
3848
3849    ----------------------------------------------------------------------------
3850
3851    NOTE: `logScreenshot` is currently unsupported.
3852    """
3853    scancodes_sequence: ScancodeSequence
3854    if isinstance(scancodes, int):
3855        scancodes_sequence = ScancodeSequence([scancodes])
3856    else:
3857        scancodes_sequence = scancodes
3858
3859    keybdFlags: int = _KEYEVENTF_SCANCODE | _KEYEVENTF_KEYUP
3860    input_structs: list[_INPUT] = []
3861    extendedFlag: int
3862
3863    # Init event tracking
3864    insertedEvents: int = 0
3865    expectedEvents: int = 0
3866
3867    for scancode in scancodes_sequence:
3868        if auto_shift and scancode & _OFFSET_SHIFTKEY:
3869            input_structs += [
3870                _create_keyboard_input(
3871                    wScan=_SHIFT_SCANCODE, dwFlags=keybdFlags
3872                )
3873            ]
3874            expectedEvents += 1
3875
3876        scancode = scancode & 0xFFFF
3877
3878        extendedFlag = _KEYEVENTF_EXTENDEDKEY if scancode >= 0xE000 else 0
3879        input_structs += [
3880            _create_keyboard_input(
3881                wScan=scancode & 0xFFFF, dwFlags=keybdFlags | extendedFlag
3882            )
3883        ]
3884        expectedEvents += 1
3885
3886    insertedEvents += _send_input(input_structs)
3887    return insertedEvents == expectedEvents
3888    # --------------------------------------------------------------------------

Release key corresponding to scancodes.

The actually pressed key will depend on your system keyboard layout. Limits the available character set but should provide the best compatibility.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.

auto_shift is used internally by higher level functions to automatically press the shift key before supported scancodes (indicitated by a special bit outside the regular scancode range, while it technically can be used, it's not intended for public access).


NOTE: logScreenshot is currently unsupported.

def scancode_press( scancodes: 'ScancodeTypes | Sequence[ScancodeTypes]', presses: int = 1, interval: float = 0.0, logScreenshot: None = None, _pause: bool = True, *, auto_shift: bool = False, delay: float = 0.0, duration: float = 0.0) -> bool:
3918@_genericPyDirectInputChecks
3919def scancode_press(
3920    scancodes: ScancodeTypes | Sequence[ScancodeTypes],
3921    presses: int = 1,
3922    interval: float = 0.0,
3923    logScreenshot: None = None,
3924    _pause: bool = True,
3925    *,
3926    auto_shift: bool = False,
3927    delay: float = 0.0,
3928    duration: float = 0.0,
3929) -> bool:
3930    """
3931    Press the sequence of `keys` for `presses` amount of times.
3932
3933    The actually pressed key will depend on your system keyboard layout.
3934    Limits the available character set but should provide the best
3935    compatibility.
3936
3937    Explanation of time parameters (seconds as floating point numbers):
3938
3939    - `interval` is the time spent waiting between sequences. If `keys` is a
3940    str instance or single element list, then `interval` will be ignored.
3941    - `delay` is the time from one complete key (press+release) to the next one
3942    in the same sequence. If there is only a single key in a sequence, then
3943    `delay` will be ignored.
3944    - `duration` is the time spent on holding every key before releasing it
3945    again.
3946
3947    If `_pause` is True (default), then an automatic sleep will be performed
3948    after the function finshes executing. The duration is set by the global
3949    variable `PAUSE`.
3950    Be aware, that the global pause defined by the PAUSE `constant` only
3951    applies after every call to this function, not inbetween (no extra pause
3952    between pressing and releasing key, use the `duration` argument instead)!
3953
3954    `auto_shift` is used internally by higher level functions to automatically
3955    press the shift key before supported scancodes (indicitated by a special
3956    bit outside the regular scancode range, while it technically can be used,
3957    it's not intended for public access).
3958
3959    ----------------------------------------------------------------------------
3960
3961    NOTE: `logScreenshot` is currently unsupported.
3962    """
3963    scancodes_sequence: Sequence[ScancodeTypes]
3964    if isinstance(scancodes, int):
3965        scancodes_sequence = [ScancodeSequence([scancodes])]
3966    elif isinstance(scancodes, ScancodeSequence):
3967        scancodes_sequence = [scancodes]
3968    else:
3969        scancodes_sequence = scancodes
3970
3971    # We need to press x keys y times, which comes out to x*y presses in total
3972    expectedPresses: int = presses * len(scancodes_sequence)
3973    completedPresses: int = 0
3974
3975    apply_interval: bool = False
3976    for _ in range(presses):
3977        if apply_interval:  # Don't delay first press
3978            _sleep(interval)
3979        apply_interval = True
3980
3981        apply_delay: bool = False
3982        for c in scancodes_sequence:
3983            if apply_delay:  # Don't delay first press
3984                _sleep(delay)
3985            apply_delay = True
3986
3987            completedPresses += _helper_scancode_press(
3988                c, duration, _pause=False, auto_shift=auto_shift
3989            )
3990
3991    return completedPresses == expectedPresses
3992    # --------------------------------------------------------------------------

Press the sequence of keys for presses amount of times.

The actually pressed key will depend on your system keyboard layout. Limits the available character set but should provide the best compatibility.

Explanation of time parameters (seconds as floating point numbers):

  • interval is the time spent waiting between sequences. If keys is a str instance or single element list, then interval will be ignored.
  • delay is the time from one complete key (press+release) to the next one in the same sequence. If there is only a single key in a sequence, then delay will be ignored.
  • duration is the time spent on holding every key before releasing it again.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE. Be aware, that the global pause defined by the PAUSE constant only applies after every call to this function, not inbetween (no extra pause between pressing and releasing key, use the duration argument instead)!

auto_shift is used internally by higher level functions to automatically press the shift key before supported scancodes (indicitated by a special bit outside the regular scancode range, while it technically can be used, it's not intended for public access).


NOTE: logScreenshot is currently unsupported.

@contextmanager
def scancode_hold( scancodes: 'ScancodeTypes | Sequence[ScancodeTypes]', logScreenshot: None = None, _pause: bool = True, *, auto_shift: bool = False, raise_on_failure: bool = False) -> Generator[None, None, None]:
3996@contextmanager
3997@_genericPyDirectInputChecks
3998def scancode_hold(
3999    scancodes: ScancodeTypes | Sequence[ScancodeTypes],
4000    logScreenshot: None = None,
4001    _pause: bool = True,
4002    *,
4003    auto_shift: bool = False,
4004    raise_on_failure: bool = False,
4005) -> Generator[None, None, None]:
4006    """
4007    Hold the sequence of keys corresponding to `scancodes` as long as the
4008    context manager is in scope (press upon entry, release upon exit).
4009
4010    Keys will be released in reverse order (LIFO), but still practically
4011    instantenous.
4012
4013    The actually pressed key will depend on your system keyboard layout.
4014    Limits the available character set but should provide the best
4015    compatibility.
4016
4017    If `_pause` is True (default), then an automatic sleep will be performed
4018    after the function finshes executing. The duration is set by the global
4019    variable `PAUSE`.
4020    Be aware, that the global pause defined by the PAUSE `constant` only
4021    applies after every call to this function, not inbetween (no pause between
4022    press and releasing key)!
4023
4024    `auto_shift` is used internally by higher level functions to automatically
4025    press the shift key before supported scancodes (indicitated by a special
4026    bit outside the regular scancode range, while it technically can be used,
4027    it's not intended for public access).
4028
4029    If `raise_on_failure` is True, then `PriorInputFailedException` will be
4030    raised if not all keyboard inputs could be executed successfully.
4031
4032    ----------------------------------------------------------------------------
4033
4034    NOTE: `logScreenshot` is currently unsupported.
4035    """
4036    scancodes_sequence: Sequence[ScancodeTypes]
4037    if isinstance(scancodes, int):
4038        scancodes_sequence = [ScancodeSequence([scancodes])]
4039    elif isinstance(scancodes, ScancodeSequence):
4040        scancodes_sequence = [scancodes]
4041    else:
4042        scancodes_sequence = scancodes
4043
4044    expectedPresses: int = len(scancodes_sequence)
4045    downed: int = 0
4046    upped: int = 0
4047
4048    try:
4049        for c in scancodes_sequence:
4050            downed += scancode_keyDown(c, _pause=False, auto_shift=auto_shift)
4051        yield
4052    finally:
4053        for c in reversed(scancodes_sequence):
4054            upped += scancode_keyUp(c, _pause=False, auto_shift=auto_shift)
4055        if raise_on_failure and not (expectedPresses == downed == upped):
4056            raise PriorInputFailedException
4057    # --------------------------------------------------------------------------

Hold the sequence of keys corresponding to scancodes as long as the context manager is in scope (press upon entry, release upon exit).

Keys will be released in reverse order (LIFO), but still practically instantenous.

The actually pressed key will depend on your system keyboard layout. Limits the available character set but should provide the best compatibility.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE. Be aware, that the global pause defined by the PAUSE constant only applies after every call to this function, not inbetween (no pause between press and releasing key)!

auto_shift is used internally by higher level functions to automatically press the shift key before supported scancodes (indicitated by a special bit outside the regular scancode range, while it technically can be used, it's not intended for public access).

If raise_on_failure is True, then PriorInputFailedException will be raised if not all keyboard inputs could be executed successfully.


NOTE: logScreenshot is currently unsupported.

def scancode_hotkey( *args: int | ScancodeSequence, interval: float = 0.0, wait: float = 0.0, logScreenshot: None = None, _pause: bool = True, auto_shift: bool = True) -> bool:
4061@_genericPyDirectInputChecks
4062def scancode_hotkey(
4063    *args: ScancodeTypes,
4064    interval: float = 0.0,
4065    wait: float = 0.0,
4066    logScreenshot: None = None,
4067    _pause: bool = True,
4068    auto_shift: bool = True,
4069) -> bool:
4070    """
4071    Press down buttons in order they are specified as arguments,
4072    releasing them in reverse order, e.g. 0x1D, 0x2E will first press
4073    Control, then C and release C before releasing Control.
4074
4075    Use keyword-only argument `interval` to specify a delay between single
4076    keys when pressing and releasing and `wait` for delay between last press
4077    and first release.
4078
4079    If `_pause` is True (default), then an automatic sleep will be performed
4080    after the function finshes executing. The duration is set by the global
4081    variable `PAUSE`.
4082    Be aware, that the global pause defined by the PAUSE `constant` only
4083    applies after every call to this function, not inbetween (no pause between
4084    press and releasing key)!
4085
4086    `auto_shift` is used internally by higher level functions to automatically
4087    press the shift key before supported scancodes (indicitated by a special
4088    bit outside the regular scancode range, while it technically can be used,
4089    it's not intended for public access).
4090
4091    ----------------------------------------------------------------------------
4092
4093    NOTE: `logScreenshot` is currently unsupported.
4094    """
4095    expectedPresses: int = len(args)
4096    downed: int = 0
4097    upped: int = 0
4098
4099    apply_interval: bool = False
4100    for code in args:
4101        if apply_interval:
4102            _sleep(interval)  # sleep between iterations
4103        apply_interval = True
4104
4105        downed += scancode_keyDown(code, _pause=False, auto_shift=auto_shift)
4106
4107    _sleep(wait)
4108
4109    apply_interval = False
4110    for code in reversed(args):
4111        if apply_interval:
4112            _sleep(interval)  # sleep between iterations
4113        apply_interval = True
4114
4115        upped += scancode_keyUp(code, _pause=False, auto_shift=auto_shift)
4116
4117    return expectedPresses == downed == upped
4118    # --------------------------------------------------------------------------

Press down buttons in order they are specified as arguments, releasing them in reverse order, e.g. 0x1D, 0x2E will first press Control, then C and release C before releasing Control.

Use keyword-only argument interval to specify a delay between single keys when pressing and releasing and wait for delay between last press and first release.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE. Be aware, that the global pause defined by the PAUSE constant only applies after every call to this function, not inbetween (no pause between press and releasing key)!

auto_shift is used internally by higher level functions to automatically press the shift key before supported scancodes (indicitated by a special bit outside the regular scancode range, while it technically can be used, it's not intended for public access).


NOTE: logScreenshot is currently unsupported.

def keyDown( key: str, logScreenshot: None = None, _pause: bool = True, *, auto_shift: bool = False) -> bool:
4126def keyDown(
4127    key: str,
4128    logScreenshot: None = None,
4129    _pause: bool = True,
4130    *,
4131    auto_shift: bool = False,
4132) -> bool:
4133    """
4134    Press down key corresponding to key name `key`.
4135
4136    `key` will be interpreted as a keyboard key (US QWERTY).
4137    The actually pressed key will depend on your system keyboard layout.
4138    Limits the available character set but should provide the best
4139    compatibility.
4140
4141    If `_pause` is True (default), then an automatic sleep will be performed
4142    after the function finshes executing. The duration is set by the global
4143    variable `PAUSE`.
4144
4145    If `auto_shift` is True, then "shifted" characters like upper case letters
4146    and the symbols on the number row automatically insert a Shift scancode
4147    into the input sequence.
4148
4149    ----------------------------------------------------------------------------
4150
4151    NOTE: `logScreenshot` is currently unsupported.
4152    """
4153    scancode: ScancodeTypes | None = KEYBOARD_MAPPING.get(key)
4154    if scancode is None:
4155        return False
4156    return scancode_keyDown(
4157        scancode, logScreenshot, _pause=_pause, auto_shift=auto_shift
4158    )
4159    # --------------------------------------------------------------------------

Press down key corresponding to key name key.

key will be interpreted as a keyboard key (US QWERTY). The actually pressed key will depend on your system keyboard layout. Limits the available character set but should provide the best compatibility.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.

If auto_shift is True, then "shifted" characters like upper case letters and the symbols on the number row automatically insert a Shift scancode into the input sequence.


NOTE: logScreenshot is currently unsupported.

def keyUp( key: str, logScreenshot: None = None, _pause: bool = True, *, auto_shift: bool = False) -> bool:
4164def keyUp(
4165    key: str,
4166    logScreenshot: None = None,
4167    _pause: bool = True,
4168    *,
4169    auto_shift: bool = False,
4170) -> bool:
4171    """
4172    Lift up key corresponding to key name `key`.
4173
4174    `key` will be interpreted as a keyboard key (US QWERTY).
4175    The actually lifted key will depend on your system keyboard layout.
4176    Limits the available character set but should provide the best
4177    compatibility.
4178
4179    If `_pause` is True (default), then an automatic sleep will be performed
4180    after the function finshes executing. The duration is set by the global
4181    variable `PAUSE`.
4182
4183    If `auto_shift` is True, then "shifted" characters like upper case letters
4184    and the symbols on the number row automatically insert a Shift scancode
4185    into the input sequence.
4186
4187    ----------------------------------------------------------------------------
4188
4189    NOTE: `logScreenshot` is currently unsupported.
4190    """
4191    scancode: ScancodeTypes | None = KEYBOARD_MAPPING.get(key)
4192    if scancode is None:
4193        return False
4194    return scancode_keyUp(
4195        scancode, logScreenshot, _pause=_pause, auto_shift=auto_shift
4196    )
4197    # --------------------------------------------------------------------------

Lift up key corresponding to key name key.

key will be interpreted as a keyboard key (US QWERTY). The actually lifted key will depend on your system keyboard layout. Limits the available character set but should provide the best compatibility.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.

If auto_shift is True, then "shifted" characters like upper case letters and the symbols on the number row automatically insert a Shift scancode into the input sequence.


NOTE: logScreenshot is currently unsupported.

def press( keys: str | Sequence[str], presses: int = 1, interval: float = 0.0, logScreenshot: None = None, _pause: bool = True, *, auto_shift: bool = False, delay: float = 0.0, duration: float = 0.0) -> bool:
4223@_genericPyDirectInputChecks
4224def press(
4225    keys: str | Sequence[str],
4226    presses: int = 1,
4227    interval: float = 0.0,
4228    logScreenshot: None = None,
4229    _pause: bool = True,
4230    *,
4231    auto_shift: bool = False,
4232    delay: float = 0.0,
4233    duration: float = 0.0,
4234) -> bool:
4235    """
4236    Press the sequence of `keys` for `presses` amount of times.
4237
4238    `keys` will be interpreted as sequence of keyboard keys (US QWERTY).
4239    The actually pressed key will depend on your system keyboard layout.
4240    Limits the available character set but should provide the best
4241    compatibility.
4242
4243    Explanation of time parameters (seconds as floating point numbers):
4244
4245    - `interval` is the time spent waiting between sequences. If `keys` is a
4246    str instance, single element list or presses equals 1 (the default),
4247    then `interval` will be ignored.
4248    - `delay` is the time from one complete key (press+release) to the next one
4249    in the same sequence. If there is only a single key in a sequence, then
4250    `delay` will be ignored.
4251    - `duration` is the time spent on holding every key before releasing it
4252    again.
4253
4254    If `_pause` is True (default), then an automatic sleep will be performed
4255    after the function finshes executing. The duration is set by the global
4256    variable `PAUSE`.
4257    Be aware, that the global pause defined by the `PAUSE` var only applies
4258    after every call to this function, not inbetween (no extra pause between
4259    pressing and releasing key, use the `duration` argument instead)!
4260
4261    If `auto_shift` is True, then "shifted" characters like upper case letters
4262    and the symbols on the number row automatically insert a Shift scancode
4263    into the input sequence.
4264
4265    ----------------------------------------------------------------------------
4266
4267    NOTE: `logScreenshot` is currently unsupported.
4268    """
4269    if isinstance(keys, str):
4270        keys = [keys]  # If keys is 'enter', convert it to ['enter'].
4271    keys = [_normalize_key(key, auto_shift=auto_shift) for key in keys]
4272
4273    # We need to press x keys y times, which comes out to x*y presses in total
4274    expectedPresses: int = presses * len(keys)
4275    completedPresses: int = 0
4276
4277    apply_interval: bool = False
4278    for _ in range(presses):
4279        if apply_interval:  # Don't delay first press
4280            _sleep(interval)
4281        apply_interval = True
4282
4283        apply_delay: bool = False
4284        for k in keys:
4285            if apply_delay:  # Don't delay first press
4286                _sleep(delay)
4287            apply_delay = True
4288
4289            completedPresses += _helper_press(
4290                k, duration, _pause=False, auto_shift=auto_shift
4291            )
4292
4293    return completedPresses == expectedPresses
4294    # --------------------------------------------------------------------------

Press the sequence of keys for presses amount of times.

keys will be interpreted as sequence of keyboard keys (US QWERTY). The actually pressed key will depend on your system keyboard layout. Limits the available character set but should provide the best compatibility.

Explanation of time parameters (seconds as floating point numbers):

  • interval is the time spent waiting between sequences. If keys is a str instance, single element list or presses equals 1 (the default), then interval will be ignored.
  • delay is the time from one complete key (press+release) to the next one in the same sequence. If there is only a single key in a sequence, then delay will be ignored.
  • duration is the time spent on holding every key before releasing it again.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE. Be aware, that the global pause defined by the PAUSE var only applies after every call to this function, not inbetween (no extra pause between pressing and releasing key, use the duration argument instead)!

If auto_shift is True, then "shifted" characters like upper case letters and the symbols on the number row automatically insert a Shift scancode into the input sequence.


NOTE: logScreenshot is currently unsupported.

@contextmanager
def hold( keys: str | Sequence[str], logScreenshot: None = None, _pause: bool = True, *, auto_shift: bool = False, raise_on_failure: bool = False) -> Generator[None, None, None]:
4298@contextmanager
4299@_genericPyDirectInputChecks
4300def hold(
4301    keys: str | Sequence[str],
4302    logScreenshot: None = None,
4303    _pause: bool = True,
4304    *,
4305    auto_shift: bool = False,
4306    raise_on_failure: bool = False,
4307) -> Generator[None, None, None]:
4308    """
4309    Hold the sequence of keys corresponding to key names in `keys` as long as
4310    the context manager is in scope (press upon entry, release upon exit).
4311
4312    Keys will be released in reverse order (LIFO), but still practically
4313    instantenous.
4314
4315    `key` will be interpreted as a keyboard key (US QWERTY).
4316    The actually pressed key will depend on your system keyboard layout.
4317    Limits the available character set but should provide the best
4318    compatibility.
4319
4320    If `_pause` is True (default), then an automatic sleep will be performed
4321    after the function finshes executing. The duration is set by the global
4322    variable `PAUSE`.
4323    Be aware, that the global pause defined by the PAUSE `constant` only
4324    applies after every call to this function, not inbetween (no pause between
4325    press and releasing key)!
4326
4327    `auto_shift` is used internally by higher level functions to automatically
4328    press the shift key before supported scancodes (indicitated by a special
4329    bit outside the regular scancode range, while it technically can be used,
4330    it's not intended for public access).
4331
4332    If `raise_on_failure` is True, then `PriorInputFailedException` will be
4333    raised if not all keyboard inputs could be executed successfully.
4334
4335    ----------------------------------------------------------------------------
4336
4337    NOTE: `logScreenshot` is currently unsupported.
4338    """
4339    if isinstance(keys, str):
4340        keys = [keys]  # make single element into iterable
4341    keys = [_normalize_key(key, auto_shift=auto_shift) for key in keys]
4342
4343    expectedPresses: int = len(keys)
4344    downed: int = 0
4345    upped: int = 0
4346
4347    try:
4348        for k in keys:
4349            downed += keyDown(k, auto_shift=auto_shift)
4350        yield
4351    finally:
4352        for k in reversed(keys):
4353            upped += keyUp(k, auto_shift=auto_shift)
4354        if raise_on_failure and not (expectedPresses == downed == upped):
4355            raise PriorInputFailedException
4356    # --------------------------------------------------------------------------

Hold the sequence of keys corresponding to key names in keys as long as the context manager is in scope (press upon entry, release upon exit).

Keys will be released in reverse order (LIFO), but still practically instantenous.

key will be interpreted as a keyboard key (US QWERTY). The actually pressed key will depend on your system keyboard layout. Limits the available character set but should provide the best compatibility.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE. Be aware, that the global pause defined by the PAUSE constant only applies after every call to this function, not inbetween (no pause between press and releasing key)!

auto_shift is used internally by higher level functions to automatically press the shift key before supported scancodes (indicitated by a special bit outside the regular scancode range, while it technically can be used, it's not intended for public access).

If raise_on_failure is True, then PriorInputFailedException will be raised if not all keyboard inputs could be executed successfully.


NOTE: logScreenshot is currently unsupported.

def typewrite( message: str, interval: float = 0.0, logScreenshot: None = None, _pause: bool = True, *, auto_shift: bool = False, delay: float = 0.0, duration: float = 0.0) -> None:
4360@_genericPyDirectInputChecks
4361def typewrite(
4362    message: str,
4363    interval: float = 0.0,
4364    logScreenshot: None = None,
4365    _pause: bool = True,
4366    *,
4367    auto_shift: bool = False,
4368    delay: float = 0.0,
4369    duration: float = 0.0,
4370) -> None:
4371    """
4372    Break down `message` into a single character key sequence and press each
4373    key one by one.
4374
4375    `message` will be interpreted as sequence of keyboard keys (US QWERTY).
4376    The actually pressed keys will depend on your system keyboard layout.
4377    Limits the available character set but should provide the best
4378    compatibility.
4379
4380    Explanation of time parameters (seconds as floating point numbers):
4381
4382    - `interval` is the time spent waiting between sequences. If `message` is a
4383    single character string, then `interval` will be ignored.
4384    - `delay` is the time from one complete key (press+release) to the next one
4385    in the same sequence. If there is only a single key in a sequence, then
4386    `delay` will be ignored.
4387    - `duration` is the time spent on holding every key before releasing it
4388    again.
4389
4390    If `_pause` is True (default), then an automatic sleep will be performed
4391    after the function finshes executing. The duration is set by the global
4392    variable `PAUSE`.
4393    Be aware, that the global pause defined by the PAUSE `constant` only
4394    applies after every call to this function, not inbetween (no pause between
4395    press and releasing key)!
4396
4397    `auto_shift` is used internally by higher level functions to automatically
4398    press the shift key before supported scancodes (indicitated by a special
4399    bit outside the regular scancode range, while it technically can be used,
4400    it's not intended for public access).
4401
4402    ----------------------------------------------------------------------------
4403
4404    NOTE: `logScreenshot` is currently unsupported.
4405    """
4406
4407    apply_interval: bool = False
4408    for key in message:
4409        if apply_interval:  # Don't delay first press
4410            _sleep(interval)
4411        apply_interval = True
4412
4413        press(
4414            key,
4415            _pause=False,
4416            auto_shift=auto_shift,
4417            delay=delay,
4418            duration=duration,
4419        )
4420    # --------------------------------------------------------------------------

Break down message into a single character key sequence and press each key one by one.

message will be interpreted as sequence of keyboard keys (US QWERTY). The actually pressed keys will depend on your system keyboard layout. Limits the available character set but should provide the best compatibility.

Explanation of time parameters (seconds as floating point numbers):

  • interval is the time spent waiting between sequences. If message is a single character string, then interval will be ignored.
  • delay is the time from one complete key (press+release) to the next one in the same sequence. If there is only a single key in a sequence, then delay will be ignored.
  • duration is the time spent on holding every key before releasing it again.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE. Be aware, that the global pause defined by the PAUSE constant only applies after every call to this function, not inbetween (no pause between press and releasing key)!

auto_shift is used internally by higher level functions to automatically press the shift key before supported scancodes (indicitated by a special bit outside the regular scancode range, while it technically can be used, it's not intended for public access).


NOTE: logScreenshot is currently unsupported.

def write( message: str, interval: float = 0.0, logScreenshot: None = None, _pause: bool = True, *, auto_shift: bool = False, delay: float = 0.0, duration: float = 0.0) -> None:
4360@_genericPyDirectInputChecks
4361def typewrite(
4362    message: str,
4363    interval: float = 0.0,
4364    logScreenshot: None = None,
4365    _pause: bool = True,
4366    *,
4367    auto_shift: bool = False,
4368    delay: float = 0.0,
4369    duration: float = 0.0,
4370) -> None:
4371    """
4372    Break down `message` into a single character key sequence and press each
4373    key one by one.
4374
4375    `message` will be interpreted as sequence of keyboard keys (US QWERTY).
4376    The actually pressed keys will depend on your system keyboard layout.
4377    Limits the available character set but should provide the best
4378    compatibility.
4379
4380    Explanation of time parameters (seconds as floating point numbers):
4381
4382    - `interval` is the time spent waiting between sequences. If `message` is a
4383    single character string, then `interval` will be ignored.
4384    - `delay` is the time from one complete key (press+release) to the next one
4385    in the same sequence. If there is only a single key in a sequence, then
4386    `delay` will be ignored.
4387    - `duration` is the time spent on holding every key before releasing it
4388    again.
4389
4390    If `_pause` is True (default), then an automatic sleep will be performed
4391    after the function finshes executing. The duration is set by the global
4392    variable `PAUSE`.
4393    Be aware, that the global pause defined by the PAUSE `constant` only
4394    applies after every call to this function, not inbetween (no pause between
4395    press and releasing key)!
4396
4397    `auto_shift` is used internally by higher level functions to automatically
4398    press the shift key before supported scancodes (indicitated by a special
4399    bit outside the regular scancode range, while it technically can be used,
4400    it's not intended for public access).
4401
4402    ----------------------------------------------------------------------------
4403
4404    NOTE: `logScreenshot` is currently unsupported.
4405    """
4406
4407    apply_interval: bool = False
4408    for key in message:
4409        if apply_interval:  # Don't delay first press
4410            _sleep(interval)
4411        apply_interval = True
4412
4413        press(
4414            key,
4415            _pause=False,
4416            auto_shift=auto_shift,
4417            delay=delay,
4418            duration=duration,
4419        )
4420    # --------------------------------------------------------------------------

Break down message into a single character key sequence and press each key one by one.

message will be interpreted as sequence of keyboard keys (US QWERTY). The actually pressed keys will depend on your system keyboard layout. Limits the available character set but should provide the best compatibility.

Explanation of time parameters (seconds as floating point numbers):

  • interval is the time spent waiting between sequences. If message is a single character string, then interval will be ignored.
  • delay is the time from one complete key (press+release) to the next one in the same sequence. If there is only a single key in a sequence, then delay will be ignored.
  • duration is the time spent on holding every key before releasing it again.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE. Be aware, that the global pause defined by the PAUSE constant only applies after every call to this function, not inbetween (no pause between press and releasing key)!

auto_shift is used internally by higher level functions to automatically press the shift key before supported scancodes (indicitated by a special bit outside the regular scancode range, while it technically can be used, it's not intended for public access).


NOTE: logScreenshot is currently unsupported.

def hotkey( *args: str, interval: float = 0.0, wait: float = 0.0, logScreenshot: None = None, _pause: bool = True, auto_shift: bool = True) -> None:
4431@_genericPyDirectInputChecks
4432def hotkey(
4433    *args: str,
4434    interval: float = 0.0,
4435    wait: float = 0.0,
4436    logScreenshot: None = None,
4437    _pause: bool = True,
4438    auto_shift: bool = True,
4439) -> None:
4440    """
4441    Press down buttons in order they are specified as arguments,
4442    releasing them in reverse order, e.g. 'ctrl', 'c' will first press
4443    Control, then C and release C before releasing Control.
4444
4445    Use keyword-only argument `interval` to specify a delay between single
4446    keys when pressing and releasing and `wait` for delay between last press
4447    and first release.
4448
4449    If `_pause` is True (default), then an automatic sleep will be performed
4450    after the function finshes executing. The duration is set by the global
4451    variable `PAUSE`.
4452    Be aware, that the global pause defined by the PAUSE `constant` only
4453    applies after every call to this function, not inbetween (no pause between
4454    press and releasing key)!
4455
4456    `auto_shift` is used internally by higher level functions to automatically
4457    press the shift key before supported scancodes (indicitated by a special
4458    bit outside the regular scancode range, while it technically can be used,
4459    it's not intended for public access).
4460
4461    ----------------------------------------------------------------------------
4462
4463    NOTE: `logScreenshot` is currently unsupported.
4464    """
4465    apply_interval: bool = False
4466    for key in args:
4467        if apply_interval:
4468            _sleep(interval)  # sleep between iterations
4469        apply_interval = True
4470
4471        keyDown(key, _pause=False, auto_shift=auto_shift)
4472
4473    _sleep(wait)
4474
4475    apply_interval = False
4476    for key in reversed(args):
4477        if apply_interval:
4478            _sleep(interval)  # sleep between iterations
4479        apply_interval = True
4480
4481        keyUp(key, _pause=False, auto_shift=auto_shift)
4482    # --------------------------------------------------------------------------

Press down buttons in order they are specified as arguments, releasing them in reverse order, e.g. 'ctrl', 'c' will first press Control, then C and release C before releasing Control.

Use keyword-only argument interval to specify a delay between single keys when pressing and releasing and wait for delay between last press and first release.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE. Be aware, that the global pause defined by the PAUSE constant only applies after every call to this function, not inbetween (no pause between press and releasing key)!

auto_shift is used internally by higher level functions to automatically press the shift key before supported scancodes (indicitated by a special bit outside the regular scancode range, while it technically can be used, it's not intended for public access).


NOTE: logScreenshot is currently unsupported.

def unicode_charDown(char: str, logScreenshot: None = None, _pause: bool = True) -> bool:
4489@_genericPyDirectInputChecks
4490def unicode_charDown(
4491    char: str, logScreenshot: None = None, _pause: bool = True
4492) -> bool:
4493    """
4494    Send Unicode character(s) `char` to currently focused application as
4495    WM_KEYDOWN message.
4496
4497    `char` will be interpreted as a string of Unicode characters
4498    (independet from keyboard layout). Supports complete Unicode character set
4499    but may not be compatible with every application.
4500
4501    If `_pause` is True (default), then an automatic sleep will be performed
4502    after the function finshes executing. The duration is set by the global
4503    variable `PAUSE`.
4504
4505    ----------------------------------------------------------------------------
4506
4507    NOTE: `logScreenshot` is currently unsupported.
4508    """
4509    utf16surrogates: bytes = char.encode("utf-16be")
4510    codes: Sequence[int] = unpack(
4511        f">{len(utf16surrogates) // 2}H", utf16surrogates
4512    )
4513
4514    keybdFlags: int = _KEYEVENTF_UNICODE
4515
4516    input_structs: list[_INPUT] = [
4517        _create_keyboard_input(wVk=0, wScan=charcode, dwFlags=keybdFlags)
4518        for charcode in codes
4519    ]
4520    # Init event tracking
4521    expectedEvents: int = len(input_structs)
4522    insertedEvents: int = _send_input(input_structs)
4523
4524    return insertedEvents == expectedEvents
4525    # --------------------------------------------------------------------------

Send Unicode character(s) char to currently focused application as WM_KEYDOWN message.

char will be interpreted as a string of Unicode characters (independet from keyboard layout). Supports complete Unicode character set but may not be compatible with every application.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.


NOTE: logScreenshot is currently unsupported.

def unicode_charUp(char: str, logScreenshot: None = None, _pause: bool = True) -> bool:
4529@_genericPyDirectInputChecks
4530def unicode_charUp(
4531    char: str, logScreenshot: None = None, _pause: bool = True
4532) -> bool:
4533    """
4534    Send Unicode character(s) `char` to currently focused application as
4535    WM_KEYUP message.
4536
4537    `char` will be interpreted as a string of Unicode characters
4538    (independet from keyboard layout). Supports complete Unicode character set
4539    but may not be compatible with every application.
4540
4541    If `_pause` is True (default), then an automatic sleep will be performed
4542    after the function finshes executing. The duration is set by the global
4543    variable `PAUSE`.
4544
4545    ----------------------------------------------------------------------------
4546
4547    NOTE: `logScreenshot` is currently unsupported.
4548    """
4549    utf16surrogates: bytes = char.encode("utf-16be")
4550    codes: Sequence[int] = unpack(
4551        f">{len(utf16surrogates) // 2}H", utf16surrogates
4552    )
4553
4554    keybdFlags: int = _KEYEVENTF_UNICODE | _KEYEVENTF_KEYUP
4555
4556    input_structs: list[_INPUT] = [
4557        _create_keyboard_input(wVk=0, wScan=charcode, dwFlags=keybdFlags)
4558        for charcode in codes
4559    ]
4560    # Init event tracking
4561    expectedEvents: int = len(input_structs)
4562    insertedEvents: int = _send_input(input_structs)
4563
4564    return insertedEvents == expectedEvents
4565    # --------------------------------------------------------------------------

Send Unicode character(s) char to currently focused application as WM_KEYUP message.

char will be interpreted as a string of Unicode characters (independet from keyboard layout). Supports complete Unicode character set but may not be compatible with every application.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE.


NOTE: logScreenshot is currently unsupported.

def unicode_press( chars: str | Sequence[str], presses: int = 1, interval: float = 0.0, logScreenshot: None = None, _pause: bool = True, *, delay: float = 0.0, duration: float = 0.0) -> bool:
4589@_genericPyDirectInputChecks
4590def unicode_press(
4591    chars: str | Sequence[str],
4592    presses: int = 1,
4593    interval: float = 0.0,
4594    logScreenshot: None = None,
4595    _pause: bool = True,
4596    *,
4597    delay: float = 0.0,
4598    duration: float = 0.0,
4599) -> bool:
4600    """
4601    Press the sequence of `chars` for `presses` amount of times.
4602
4603    `chars` will be interpreted as a sequence of Unicode characters
4604    (independent from keyboard layout). Supports complete Unicode character set
4605    but may not be compatible with every application.
4606
4607    Explanation of time parameters (seconds as floating point numbers):
4608
4609    - `interval` is the time spent waiting between sequences. If `chars` is a
4610    str instance or single element list, then `interval` will be ignored.
4611    - `delay` is the time from one complete char (press+release) to the next
4612    one in the same sequence. If there is only a single char in a sequence,
4613    then `delay` will be ignored.
4614    - `duration` is the time spent on holding every char before releasing it
4615    again.
4616
4617    If `_pause` is True (default), then an automatic sleep will be performed
4618    after the function finshes executing. The duration is set by the global
4619    variable `PAUSE`.
4620    Be aware, that the global pause defined by the PAUSE `constant` only
4621    applies after every call to this function, not inbetween (no extra pause
4622    between pressing and releasing key, use the `duration` argument instead)!
4623
4624    ----------------------------------------------------------------------------
4625
4626    NOTE: `logScreenshot` is currently unsupported.
4627    """
4628    if isinstance(chars, str):
4629        chars = [chars]
4630
4631    # We need to press x keys y times, which comes out to x*y presses in total
4632    expectedPresses: int = presses * len(chars)
4633    completedPresses: int = 0
4634
4635    apply_interval: bool = False
4636    for _ in range(presses):
4637        if apply_interval:  # Don't delay first press
4638            _sleep(interval)
4639        apply_interval = True
4640
4641        apply_delay: bool = False
4642        for c in chars:
4643            if apply_delay:  # Don't delay first press
4644                _sleep(delay)
4645            apply_delay = True
4646
4647            completedPresses += _helper_unicode_press_char(
4648                c,
4649                duration,
4650                _pause=False,
4651            )
4652
4653    return completedPresses == expectedPresses
4654    # --------------------------------------------------------------------------

Press the sequence of chars for presses amount of times.

chars will be interpreted as a sequence of Unicode characters (independent from keyboard layout). Supports complete Unicode character set but may not be compatible with every application.

Explanation of time parameters (seconds as floating point numbers):

  • interval is the time spent waiting between sequences. If chars is a str instance or single element list, then interval will be ignored.
  • delay is the time from one complete char (press+release) to the next one in the same sequence. If there is only a single char in a sequence, then delay will be ignored.
  • duration is the time spent on holding every char before releasing it again.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE. Be aware, that the global pause defined by the PAUSE constant only applies after every call to this function, not inbetween (no extra pause between pressing and releasing key, use the duration argument instead)!


NOTE: logScreenshot is currently unsupported.

@contextmanager
def unicode_hold( chars: str | Sequence[str], logScreenshot: None = None, _pause: bool = True, *, raise_on_failure: bool = False) -> Generator[None, None, None]:
4658@contextmanager
4659@_genericPyDirectInputChecks
4660def unicode_hold(
4661    chars: str | Sequence[str],
4662    logScreenshot: None = None,
4663    _pause: bool = True,
4664    *,
4665    raise_on_failure: bool = False,
4666) -> Generator[None, None, None]:
4667    """
4668    Hold the sequence of "keys" corresponding to unicode characters in `chars`
4669    as long as the context manager is in scope (press upon entry,
4670    release upon exit).
4671
4672    `chars` will be interpreted as a sequence of Unicode characters
4673    (independet from keyboard layout). Supports complete Unicode character set
4674    but may not be compatible with every application.
4675
4676    Keys will be released in reverse order (LIFO), but still practically
4677    instantenous.
4678
4679    If `_pause` is True (default), then an automatic sleep will be performed
4680    after the function finshes executing. The duration is set by the global
4681    variable `PAUSE`.
4682    Be aware, that the global pause defined by the PAUSE `constant` only
4683    applies  after every call to this function, not inbetween (no pause between
4684    press and releasing key)!
4685
4686    If `raise_on_failure` is True, then `PriorInputFailedException` will be
4687    raised if not all keyboard inputs could be executed successfully.
4688
4689    ----------------------------------------------------------------------------
4690
4691    NOTE: `logScreenshot` is currently unsupported.
4692    """
4693    if isinstance(chars, str):
4694        chars = [chars]  # make single element into iterable
4695
4696    expectedPresses: int = len(chars)
4697    downed: int = 0
4698    upped: int = 0
4699
4700    try:
4701        for c in chars:
4702            downed += unicode_charDown(c, _pause=False)
4703        yield
4704    finally:
4705        for c in reversed(chars):
4706            upped += unicode_charUp(c, _pause=False)
4707        if raise_on_failure and not (expectedPresses == downed == upped):
4708            raise PriorInputFailedException
4709    # --------------------------------------------------------------------------

Hold the sequence of "keys" corresponding to unicode characters in chars as long as the context manager is in scope (press upon entry, release upon exit).

chars will be interpreted as a sequence of Unicode characters (independet from keyboard layout). Supports complete Unicode character set but may not be compatible with every application.

Keys will be released in reverse order (LIFO), but still practically instantenous.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE. Be aware, that the global pause defined by the PAUSE constant only applies after every call to this function, not inbetween (no pause between press and releasing key)!

If raise_on_failure is True, then PriorInputFailedException will be raised if not all keyboard inputs could be executed successfully.


NOTE: logScreenshot is currently unsupported.

def unicode_typewrite( message: str, interval: float = 0.0, logScreenshot: None = None, _pause: bool = True, *, delay: float = 0.0, duration: float = 0.0) -> None:
4713@_genericPyDirectInputChecks
4714def unicode_typewrite(
4715    message: str,
4716    interval: float = 0.0,
4717    logScreenshot: None = None,
4718    _pause: bool = True,
4719    *,
4720    delay: float = 0.0,
4721    duration: float = 0.0,
4722) -> None:
4723    """
4724    Break down `message` into characters and press them one by one.
4725
4726    `message` will be interpreted as a sequence of Unicode characters
4727    (independet from keyboard layout). Supports complete Unicode character set
4728    but may not be compatible with every application.
4729
4730    Explanation of time parameters (seconds as floating point numbers):
4731
4732    - `interval` is the time spent waiting between sequences. If `message` is a
4733    single character string, then `interval` will be ignored.
4734    - `delay` is the time from one complete key (press+release) to the next one
4735    in the same sequence. If there is only a single key in a sequence, then
4736    `delay` will be ignored.
4737    - `duration` is the time spent on holding every key before releasing it
4738    again.
4739
4740    If `_pause` is True (default), then an automatic sleep will be performed
4741    after the function finshes executing. The duration is set by the global
4742    variable `PAUSE`.
4743    Be aware, that the global pause defined by the PAUSE `constant` only
4744    applies after every call to this function, not inbetween (no pause between
4745    press and releasing key)!
4746
4747    ----------------------------------------------------------------------------
4748
4749    NOTE: `logScreenshot` is currently unsupported.
4750    """
4751    apply_interval: bool = False
4752    for char in message:
4753        if apply_interval:
4754            _sleep(interval)  # sleep between iterations
4755        apply_interval = True
4756
4757        unicode_press(char, _pause=False, delay=delay, duration=duration)
4758    # --------------------------------------------------------------------------

Break down message into characters and press them one by one.

message will be interpreted as a sequence of Unicode characters (independet from keyboard layout). Supports complete Unicode character set but may not be compatible with every application.

Explanation of time parameters (seconds as floating point numbers):

  • interval is the time spent waiting between sequences. If message is a single character string, then interval will be ignored.
  • delay is the time from one complete key (press+release) to the next one in the same sequence. If there is only a single key in a sequence, then delay will be ignored.
  • duration is the time spent on holding every key before releasing it again.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE. Be aware, that the global pause defined by the PAUSE constant only applies after every call to this function, not inbetween (no pause between press and releasing key)!


NOTE: logScreenshot is currently unsupported.

def unicode_write( message: str, interval: float = 0.0, logScreenshot: None = None, _pause: bool = True, *, delay: float = 0.0, duration: float = 0.0) -> None:
4713@_genericPyDirectInputChecks
4714def unicode_typewrite(
4715    message: str,
4716    interval: float = 0.0,
4717    logScreenshot: None = None,
4718    _pause: bool = True,
4719    *,
4720    delay: float = 0.0,
4721    duration: float = 0.0,
4722) -> None:
4723    """
4724    Break down `message` into characters and press them one by one.
4725
4726    `message` will be interpreted as a sequence of Unicode characters
4727    (independet from keyboard layout). Supports complete Unicode character set
4728    but may not be compatible with every application.
4729
4730    Explanation of time parameters (seconds as floating point numbers):
4731
4732    - `interval` is the time spent waiting between sequences. If `message` is a
4733    single character string, then `interval` will be ignored.
4734    - `delay` is the time from one complete key (press+release) to the next one
4735    in the same sequence. If there is only a single key in a sequence, then
4736    `delay` will be ignored.
4737    - `duration` is the time spent on holding every key before releasing it
4738    again.
4739
4740    If `_pause` is True (default), then an automatic sleep will be performed
4741    after the function finshes executing. The duration is set by the global
4742    variable `PAUSE`.
4743    Be aware, that the global pause defined by the PAUSE `constant` only
4744    applies after every call to this function, not inbetween (no pause between
4745    press and releasing key)!
4746
4747    ----------------------------------------------------------------------------
4748
4749    NOTE: `logScreenshot` is currently unsupported.
4750    """
4751    apply_interval: bool = False
4752    for char in message:
4753        if apply_interval:
4754            _sleep(interval)  # sleep between iterations
4755        apply_interval = True
4756
4757        unicode_press(char, _pause=False, delay=delay, duration=duration)
4758    # --------------------------------------------------------------------------

Break down message into characters and press them one by one.

message will be interpreted as a sequence of Unicode characters (independet from keyboard layout). Supports complete Unicode character set but may not be compatible with every application.

Explanation of time parameters (seconds as floating point numbers):

  • interval is the time spent waiting between sequences. If message is a single character string, then interval will be ignored.
  • delay is the time from one complete key (press+release) to the next one in the same sequence. If there is only a single key in a sequence, then delay will be ignored.
  • duration is the time spent on holding every key before releasing it again.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE. Be aware, that the global pause defined by the PAUSE constant only applies after every call to this function, not inbetween (no pause between press and releasing key)!


NOTE: logScreenshot is currently unsupported.

def unicode_hotkey( *args: str, interval: float = 0.0, wait: float = 0.0, logScreenshot: None = None, _pause: bool = True) -> None:
4767@_genericPyDirectInputChecks
4768def unicode_hotkey(
4769    *args: str,
4770    interval: float = 0.0,
4771    wait: float = 0.0,
4772    logScreenshot: None = None,
4773    _pause: bool = True,
4774) -> None:
4775    """
4776    Press down buttons in order they are specified as arguments,
4777    releasing them in reverse order.
4778
4779    This function makes little sense for Unicode characters and mainly exists
4780    for parity with the other, lower-level hotkey functions!
4781
4782    See `unicode_press()` for an alternative function that presses keys in
4783    series instead.
4784
4785    Use keyword-only argument `interval` to specify a delay between single
4786    keys when pressing and releasing and `wait` for delay between last press
4787    and first release.
4788
4789    If `_pause` is True (default), then an automatic sleep will be performed
4790    after the function finshes executing. The duration is set by the global
4791    variable `PAUSE`.
4792    Be aware, that the global pause defined by the PAUSE `constant` only
4793    applies after every call to this function, not inbetween (no pause between
4794    press and releasing key)!
4795
4796    ----------------------------------------------------------------------------
4797
4798    NOTE: `logScreenshot` is currently unsupported.
4799    """
4800    apply_interval: bool = False
4801    for char in args:
4802        if apply_interval:
4803            _sleep(interval)  # sleep between iterations
4804        apply_interval = True
4805
4806        unicode_charDown(char, _pause=False)
4807
4808    _sleep(wait)
4809
4810    apply_interval = False
4811    for char in reversed(args):
4812        if apply_interval:
4813            _sleep(interval)  # sleep between iterations
4814        apply_interval = True
4815
4816        unicode_charUp(char, _pause=False)
4817    # --------------------------------------------------------------------------

Press down buttons in order they are specified as arguments, releasing them in reverse order.

This function makes little sense for Unicode characters and mainly exists for parity with the other, lower-level hotkey functions!

See unicode_press() for an alternative function that presses keys in series instead.

Use keyword-only argument interval to specify a delay between single keys when pressing and releasing and wait for delay between last press and first release.

If _pause is True (default), then an automatic sleep will be performed after the function finshes executing. The duration is set by the global variable PAUSE. Be aware, that the global pause defined by the PAUSE constant only applies after every call to this function, not inbetween (no pause between press and releasing key)!


NOTE: logScreenshot is currently unsupported.