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, uCode: UINT | int, uMapType: UINT | int
1078    ) -> int:  # UINT
1079        ...
1080
1081
1082_MapVirtualKeyW: _MapVirtualKeyWType = hint_cast(
1083    _MapVirtualKeyWType, _user32.MapVirtualKeyW
1084)
1085"""
1086----- MapVirtualKeyW function (winuser.h) -----
1087
1088Translates (maps) a virtual-key code into a scan code or character value, or
1089translates a scan code into a virtual-key code.
1090
1091To specify a handle to the keyboard layout to use for translating the specified
1092code, use the MapVirtualKeyEx function.
1093
1094https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mapvirtualkeyw
1095
1096----- Parameters -----
1097
1098[in] uCode
1099
1100Type: UINT
1101
1102The virtual key code[1] or scan code for a key. How this value is interpreted
1103depends on the value of the uMapType parameter.
1104
1105Starting with Windows Vista, the high byte of the uCode value can contain
1106either 0xe0 or 0xe1 to specify the extended scan code.
1107
1108[in] uMapType
1109
1110Type: UINT
1111
1112The translation to be performed. The value of this parameter depends on the
1113value of the uCode parameter. (See _MAPVK_VK_TO_* constants)
1114
1115----- Return value -----
1116
1117Type: UINT
1118
1119The return value is either a scan code, a virtual-key code, or a character
1120value, depending on the value of uCode and uMapType. If there is no
1121translation, the return value is zero.
1122
1123----- Remarks -----
1124
1125An application can use MapVirtualKey to translate scan codes to the
1126virtual-key code constants VK_SHIFT, VK_CONTROL, and VK_MENU, and vice versa.
1127These translations do not distinguish between the left and right instances
1128of the SHIFT, CTRL, or ALT keys.
1129
1130An application can get the scan code corresponding to the left or right
1131instance of one of these keys by calling MapVirtualKey with uCode set to
1132one of the following virtual-key code constants:
1133
1134    VK_LSHIFT
1135    VK_RSHIFT
1136    VK_LCONTROL
1137    VK_RCONTROL
1138    VK_LMENU
1139    VK_RMENU
1140
1141These left- and right-distinguishing constants are available to an application
1142only through the GetKeyboardState[2], SetKeyboardState[3], GetAsyncKeyState[4],
1143GetKeyState, MapVirtualKey, and MapVirtualKeyEx functions. For list complete
1144table of virtual key codes, see Virtual Key Codes[1].
1145
1146[1] https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
1147
1148[2] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeyboardstate
1149
1150[3] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setkeyboardstate
1151
1152[4] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getasynckeystate
1153
1154[5] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeystate
1155"""
1156_MapVirtualKeyW.argtypes = UINT, UINT
1157_MapVirtualKeyW.restype = UINT
1158
1159
1160def _map_virtual_key(
1161    uCode: int, uMapType: Literal[0, 1, 2, 3, 4]  # See _MAPVK_* constants
1162) -> int:
1163    """
1164    Abstraction layer over MapVirtualKeyW (winuser.h)
1165
1166    Accepted values for uMapType are:
1167    - _MAPVK_VK_TO_VSC = 0
1168    - _MAPVK_VSC_TO_VK = 1
1169    - _MAPVK_VK_TO_CHAR = 2
1170    - _MAPVK_VSC_TO_VK_EX = 3
1171    - _MAPVK_VK_TO_VSC_EX = 4
1172
1173    See https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mapvirtualkeyw
1174    """
1175    return _MapVirtualKeyW(UINT(uCode), UINT(uMapType))
1176    # --------------------------------------------------------------------------
1177
1178
1179# ----- GetSystemMetrics Declaration -------------------------------------------
1180class _GetSystemMetricsType(Protocol):
1181    argtypes: tuple[type[INT]]
1182    restype: type[INT]
1183
1184    def __call__(
1185        self,
1186        nIndex: INT | int,
1187    ) -> int:  # c_int
1188        ...
1189
1190
1191_GetSystemMetrics: _GetSystemMetricsType = hint_cast(
1192    _GetSystemMetricsType, _user32.GetSystemMetrics
1193)
1194"""
1195----- GetSystemMetrics function (winuser.h) -----
1196
1197Retrieves the specified system metric or system configuration setting.
1198
1199Note that all dimensions retrieved by GetSystemMetrics are in pixels.
1200
1201https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetrics
1202
1203----- Parameters -----
1204
1205[in] nIndex
1206
1207Type: int
1208
1209The system metric or configuration setting to be retrieved. This parameter can
1210be one of the following values. Note that all SM_CX* values are widths and all
1211SM_CY* values are heights. Also note that all settings designed to return
1212Boolean data represent TRUE as any nonzero value, and FALSE as a zero value.
1213(See _SM_* constants)
1214
1215----- Return value -----
1216
1217Type: int
1218
1219If the function succeeds, the return value is the requested system metric or
1220configuration setting.
1221
1222If the function fails, the return value is 0. GetLastError does not provide
1223extended error information.
1224
1225----- Remarks -----
1226
1227System metrics can vary from display to display.
1228
1229GetSystemMetrics(SM_CMONITORS) counts only visible display monitors. This is
1230different from EnumDisplayMonitors[1], which enumerates both visible display
1231monitors and invisible pseudo-monitors that are associated with mirroring
1232drivers. An invisible pseudo-monitor is associated with a pseudo-device used
1233to mirror application drawing for remoting or other purposes.
1234
1235This API is not DPI aware, and should not be used if the calling thread is
1236per-monitor DPI aware. For the DPI-aware version of this API, see
1237GetSystemMetricsForDPI[2]. For more information on DPI awareness, see the
1238Windows High DPI documentation[3].
1239
1240[1] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors
1241
1242[2] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetricsfordpi
1243
1244[3] https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows
1245"""  # noqa (URL too long)
1246_GetSystemMetrics.argtypes = (INT,)
1247_GetSystemMetrics.restype = INT
1248
1249
1250def _get_system_metrics(nIndex: int) -> int:
1251    """
1252    Abstraction layer over GetSystemMetrics (winuser.h)
1253
1254    See the _SM_* constants for accepted values for the nIndex argument.
1255
1256    See https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetrics
1257    """
1258    return _GetSystemMetrics(nIndex)
1259    # --------------------------------------------------------------------------
1260
1261
1262# ----- MonitorFromPoint Declaration -----------------------------------------------
1263class _MonitorFromPointType(Protocol):
1264    argtypes: tuple[type[_POINT], type[DWORD]]
1265    restype: type[HMONITOR]
1266
1267    def __call__(
1268        self,
1269        pt: _POINT,
1270        dwFlags: DWORD | int,
1271    ) -> int | None:  # HMONITOR
1272        ...
1273
1274
1275_MonitorFromPoint: _MonitorFromPointType = hint_cast(
1276    _MonitorFromPointType, _user32.MonitorFromPoint
1277)
1278"""
1279----- MonitorFromPoint function (winuser.h) -----
1280
1281Retrieves the position of the mouse cursor, in screen coordinates.
1282
1283https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfrompoint
1284
1285----- Parameters -----
1286
1287[in] pt
1288
1289Type: POINT
1290
1291A POINT structure that specifies the point of interest in
1292virtual-screen coordinates.
1293
1294[in] dwFlags
1295
1296Type: DWORD
1297
1298Determines the function's return value if the point is not contained within
1299any display monitor.
1300
1301This parameter can be one of the following values.
1302
1303MONITOR_DEFAULTTONULL      0x00000000   Returns NULL.
1304MONITOR_DEFAULTTOPRIMARY   0x00000001   Returns a handle to the primary
1305                                        display monitor.
1306MONITOR_DEFAULTTONEAREST   0x00000002   Returns a handle to the display monitor
1307                                        that is nearest to the point.
1308
1309----- Return value -----
1310
1311If the point is contained by a display monitor, the return value is an
1312HMONITOR handle to that display monitor.
1313
1314If the point is not contained by a display monitor, the return value depends
1315on the value of dwFlags.
1316"""
1317_MonitorFromPoint.argtypes = (_POINT, DWORD)
1318_MonitorFromPoint.restype = HMONITOR
1319
1320
1321def _monitor_from_point(x: int, y: int) -> int | None:
1322    """
1323    Abstraction layer over MonitorFromPoint (winuser.h)
1324
1325    See https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfrompoint
1326    """
1327    pt = _POINT()
1328    pt.x, pt.y = x, y
1329    return _MonitorFromPoint(pt, _MONITOR_DEFAULTTONULL)
1330    # --------------------------------------------------------------------------
1331
1332
1333# ----- GetCursorPos Declaration -----------------------------------------------
1334class _GetCursorPosType(Protocol):
1335    argtypes: tuple[type[_POINTER_TYPE[_POINT]]]
1336    restype: type[BOOL]
1337
1338    def __call__(
1339        self,
1340        lpPoint: _POINTER_TYPE[_POINT] | _POINT,
1341    ) -> bool:  # BOOL
1342        ...
1343
1344
1345_GetCursorPos: _GetCursorPosType = hint_cast(
1346    _GetCursorPosType,
1347    _user32.GetCursorPos
1348)
1349"""
1350----- GetCursorPos function (winuser.h) -----
1351
1352Retrieves the position of the mouse cursor, in screen coordinates.
1353
1354https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorpos
1355
1356----- Parameters -----
1357
1358[out] lpPoint
1359
1360Type: LPPOINT
1361
1362A pointer to a POINT structure that receives the screen coordinates of the
1363cursor.
1364
1365----- Return value -----
1366
1367Type: BOOL
1368
1369Returns nonzero if successful or zero otherwise. To get extended error
1370information, call GetLastError.
1371
1372----- Remarks -----
1373
1374The cursor position is always specified in screen coordinates and is not
1375affected by the mapping mode of the window that contains the cursor.
1376
1377The calling process must have WINSTA_READATTRIBUTES access to the window
1378station.
1379
1380The input desktop must be the current desktop when you call GetCursorPos. Call
1381OpenInputDesktop[1] to determine whether the current desktop is the input
1382desktop. If it is not, call SetThreadDesktop[2] with the HDESK returned by
1383OpenInputDesktop to switch to that desktop.
1384
1385[1] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-openinputdesktop
1386
1387[2] https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setthreaddesktop
1388"""
1389_GetCursorPos.argtypes = (POINTER(_POINT),)
1390_GetCursorPos.restype = BOOL
1391
1392
1393def _get_cursor_pos() -> _POINT:
1394    """
1395    Abstraction layer over GetCursorPos (winuser.h)
1396
1397    See https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorpos
1398    """
1399    cursor = _POINT()
1400    # cursor will be automatically be referenced by pointer
1401    _GetCursorPos(cursor)
1402    return cursor
1403    # --------------------------------------------------------------------------
1404
1405
1406# ----- SystemParametersInfoW Declaration --------------------------------------
1407class _SystemParametersInfoW_Type(Protocol):
1408    argtypes: tuple[type[UINT], type[UINT], type[LPCVOID], type[UINT]]
1409    restype: type[BOOL]
1410
1411    def __call__(
1412        self,
1413        uiAction: UINT | int,
1414        uiParam: UINT | int,
1415        pvParam: LPCVOID | Any,
1416        fWinIni: UINT | int,
1417    ) -> bool:  # BOOL
1418        ...
1419
1420
1421_SystemParametersInfoW: _SystemParametersInfoW_Type = hint_cast(
1422    _SystemParametersInfoW_Type, _user32.SystemParametersInfoW
1423)
1424"""
1425----- SystemParametersInfoW function (winuser.h) -----
1426
1427Retrieves or sets the value of one of the system-wide parameters. This
1428function can also update the user profile while setting a parameter.
1429
1430https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow
1431
1432"""
1433_SystemParametersInfoW.argtypes = UINT, UINT, LPCVOID, UINT
1434_SystemParametersInfoW.restype = BOOL
1435
1436
1437# ----- Get system settings for mouse movement ---------------------------------
1438def _get_mouse_parameters() -> tuple[int, int, int]:
1439    """
1440    Query system parameters for user's mouse settings.
1441
1442    Abstraction layer over SystemParametersInfoW (winuser.h)
1443
1444    https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow
1445
1446    Information on _SPI_GETMOUSE
1447
1448    Retrieves the two mouse threshold values and the mouse acceleration. The
1449    pvParam parameter must point to an array of three integers that receives
1450    these values. See mouse_event[1] for further information.
1451
1452    https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event
1453    """
1454    pvParam: Array[UINT] = (UINT * 3)()
1455    _SystemParametersInfoW(_SPI_GETMOUSE, 0, pointer(pvParam), 0)
1456    return (pvParam[0], pvParam[1], pvParam[2])
1457    # --------------------------------------------------------------------------
1458
1459
1460# ----- Set system settings for mouse movement ---------------------------------
1461def _set_mouse_parameters(
1462    threshold1: int, threshold2: int, enhanced_pointer_precision: int
1463) -> bool:
1464    """
1465    Set system parameters for user's mouse settings.
1466
1467    Abstraction layer over SystemParametersInfoW (winuser.h)
1468
1469    https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow
1470
1471    Information on _SPI_SETMOUSE
1472
1473    Sets the two mouse threshold values and the mouse acceleration.
1474    The pvParam parameter must point to an array of three integers that
1475    specifies these values.
1476    See mouse_event[1] for further information.
1477
1478    https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event
1479    """
1480    pvParam: Final[Array[UINT]] = (UINT * 3)(
1481        threshold1, threshold2, enhanced_pointer_precision
1482    )
1483    # leave last parameter as 0 to make changes non-permanent and restore
1484    # themselves upon reboot if something goes wrong and the wrong setting
1485    # was overwritten.
1486    return _SystemParametersInfoW(_SPI_SETMOUSE, 0, pointer(pvParam), 0)
1487    # --------------------------------------------------------------------------
1488
1489
1490# ----- Get system settings for mouse speed ------------------------------------
1491def _get_mouse_speed() -> int:
1492    """
1493    Query system parameters for user's mouse settings.
1494
1495    Abstraction layer over SystemParametersInfoW (winuser.h)
1496
1497    https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow
1498
1499    Information on SPI_GETMOUSESPEED
1500
1501    Retrieves the current mouse speed. The mouse speed determines how far the
1502    pointer will move based on the distance the mouse moves. The pvParam
1503    parameter must point to an integer that receives a value which ranges
1504    between 1 (slowest) and 20 (fastest). A value of 10 is the default. The
1505    value can be set by an end-user using the mouse control panel application
1506    or by an application using SPI_SETMOUSESPEED.
1507    """
1508    pvParam: Array[UINT] = (UINT * 1)()
1509    _SystemParametersInfoW(_SPI_GETMOUSESPEED, 0, pointer(pvParam), 0)
1510    return pvParam[0]
1511    # --------------------------------------------------------------------------
1512
1513
1514# ----- Set system settings for mouse movement ---------------------------------
1515def _set_mouse_speed(mouse_speed: int) -> bool:
1516    """
1517    Set system parameters for user's mouse settings.
1518
1519    Abstraction layer over SystemParametersInfoW (winuser.h)
1520
1521    https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow
1522
1523    Information on SPI_SETMOUSESPEED
1524
1525    Sets the current mouse speed. The pvParam parameter is an integer between
1526    1 (slowest) and 20 (fastest). A value of 10 is the default. This value is
1527    typically set using the mouse control panel application.
1528    """
1529    pvParam: Final[Array[UINT]] = (UINT * 1)(mouse_speed)
1530    # leave last parameter as 0 to make changes non-permanent and restore
1531    # themselves upon reboot if something goes wrong and the wrong setting
1532    # was overwritten.
1533    return _SystemParametersInfoW(_SPI_SETMOUSESPEED, 0, pointer(pvParam), 0)
1534    # --------------------------------------------------------------------------
1535
1536
1537# ==============================================================================
1538# ===== Keyboard Scan Code Mappings ============================================
1539# ==============================================================================
1540
1541
1542# ----- Special class for key extended scancode sequences ----------------------
1543class ScancodeSequence(List[int]):
1544    """
1545    A special class with the sole purpose of representing extended scancode
1546    sequences that should be grouped together in a single INPUT array.
1547
1548    Inserting non-scancode elements is illegal, but no runtime checks exist
1549    to verify correct input! Violations could lead to unpredictable runtime
1550    behaviour. You've been warned.
1551    """
1552
1553    pass
1554    # --------------------------------------------------------------------------
1555
1556
1557# ----- TypeAlias for KEYBOARD_MAPPING values ----------------------------------
1558ScancodeTypes: TypeAlias = "int | ScancodeSequence"  # TODO 3.10: remove quotes
1559"""
1560Acceptable value types in KEYBOARD_MAPPING.
1561
1562Accepts single standalone scancode integer or multiple scancode integers
1563contained in a special class ScancodeSequence instance.
1564"""
1565# ------------------------------------------------------------------------------
1566
1567
1568# ----- Offsets for values in KEYBOARD_MAPPING ---------------------------------
1569_OFFSET_EXTENDEDKEY: Final = 0xE000
1570_OFFSET_SHIFTKEY: Final = 0x10000
1571# ------------------------------------------------------------------------------
1572
1573
1574# ----- KEYBOARD_MAPPING -------------------------------------------------------
1575_SHIFT_SCANCODE: Final = 0x2A  # Used in auto-shifting
1576# should be keyboard MAKE scancodes ( <0x80 )
1577# some keys require the use of EXTENDEDKEY flags, they
1578US_QWERTY_MAPPING: Final[dict[str, ScancodeTypes]] = {
1579    "escape": 0x01,
1580    "esc": 0x01,
1581    "f1": 0x3B,
1582    "f2": 0x3C,
1583    "f3": 0x3D,
1584    "f4": 0x3E,
1585    "f5": 0x3F,
1586    "f6": 0x40,
1587    "f7": 0x41,
1588    "f8": 0x42,
1589    "f9": 0x43,
1590    "f10": 0x44,
1591    "f11": 0x57,
1592    "f12": 0x58,
1593    "printscreen": 0x54,  # same result as ScancodeSequence([0xE02A, 0xE037])
1594    "prntscrn": 0x54,  # same result as ScancodeSequence([0xE02A, 0xE037])
1595    "prtsc": 0x54,  # same result as ScancodeSequence([0xE02A, 0xE037])
1596    "prtscr": 0x54,  # same result as ScancodeSequence([0xE02A, 0xE037])
1597    "scrolllock": 0x46,
1598    "ctrlbreak": 0x46 + _OFFSET_EXTENDEDKEY,
1599    "pause": ScancodeSequence([0xE11D, 0x45, 0xE19D, 0xC5]),
1600    "`": 0x29,
1601    "1": 0x02,
1602    "2": 0x03,
1603    "3": 0x04,
1604    "4": 0x05,
1605    "5": 0x06,
1606    "6": 0x07,
1607    "7": 0x08,
1608    "8": 0x09,
1609    "9": 0x0A,
1610    "0": 0x0B,
1611    "-": 0x0C,
1612    "=": 0x0D,
1613    "~": 0x29 + _OFFSET_SHIFTKEY,
1614    "!": 0x02 + _OFFSET_SHIFTKEY,
1615    "@": 0x03 + _OFFSET_SHIFTKEY,
1616    "#": 0x04 + _OFFSET_SHIFTKEY,
1617    "$": 0x05 + _OFFSET_SHIFTKEY,
1618    "%": 0x06 + _OFFSET_SHIFTKEY,
1619    "^": 0x07 + _OFFSET_SHIFTKEY,
1620    "&": 0x08 + _OFFSET_SHIFTKEY,
1621    "*": 0x09 + _OFFSET_SHIFTKEY,
1622    "(": 0x0A + _OFFSET_SHIFTKEY,
1623    ")": 0x0B + _OFFSET_SHIFTKEY,
1624    "_": 0x0C + _OFFSET_SHIFTKEY,
1625    "+": 0x0D + _OFFSET_SHIFTKEY,
1626    "backspace": 0x0E,
1627    "\b": 0x0E,
1628    "insert": 0x52 + _OFFSET_EXTENDEDKEY,
1629    "home": 0x47 + _OFFSET_EXTENDEDKEY,
1630    "pageup": 0x49 + _OFFSET_EXTENDEDKEY,
1631    "pgup": 0x49 + _OFFSET_EXTENDEDKEY,
1632    "pagedown": 0x51 + _OFFSET_EXTENDEDKEY,
1633    "pgdn": 0x51 + _OFFSET_EXTENDEDKEY,
1634    # numpad
1635    "numlock": 0x45,
1636    "divide": 0x35 + _OFFSET_EXTENDEDKEY,
1637    "multiply": 0x37,
1638    "subtract": 0x4A,
1639    "add": 0x4E,
1640    "decimal": 0x53,
1641    "numperiod": 0x53,
1642    "numpadenter": 0x1C + _OFFSET_EXTENDEDKEY,
1643    "numpad1": 0x4F,
1644    "numpad2": 0x50,
1645    "numpad3": 0x51,
1646    "numpad4": 0x4B,
1647    "numpad5": 0x4C,
1648    "numpad6": 0x4D,
1649    "numpad7": 0x47,
1650    "numpad8": 0x48,
1651    "numpad9": 0x49,
1652    "num0": 0x52,
1653    "num1": 0x4F,
1654    "num2": 0x50,
1655    "num3": 0x51,
1656    "num4": 0x4B,
1657    "num5": 0x4C,
1658    "num6": 0x4D,
1659    "num7": 0x47,
1660    "num8": 0x48,
1661    "num9": 0x49,
1662    "num0": 0x52,
1663    "clear": 0x4C,  # name from pyautogui
1664    # end numpad
1665    "tab": 0x0F,
1666    "\t": 0x0F,
1667    "q": 0x10,
1668    "w": 0x11,
1669    "e": 0x12,
1670    "r": 0x13,
1671    "t": 0x14,
1672    "y": 0x15,
1673    "u": 0x16,
1674    "i": 0x17,
1675    "o": 0x18,
1676    "p": 0x19,
1677    "[": 0x1A,
1678    "]": 0x1B,
1679    "\\": 0x2B,
1680    "Q": 0x10 + _OFFSET_SHIFTKEY,
1681    "W": 0x11 + _OFFSET_SHIFTKEY,
1682    "E": 0x12 + _OFFSET_SHIFTKEY,
1683    "R": 0x13 + _OFFSET_SHIFTKEY,
1684    "T": 0x14 + _OFFSET_SHIFTKEY,
1685    "Y": 0x15 + _OFFSET_SHIFTKEY,
1686    "U": 0x16 + _OFFSET_SHIFTKEY,
1687    "I": 0x17 + _OFFSET_SHIFTKEY,
1688    "O": 0x18 + _OFFSET_SHIFTKEY,
1689    "P": 0x19 + _OFFSET_SHIFTKEY,
1690    "{": 0x1A + _OFFSET_SHIFTKEY,
1691    "}": 0x1B + _OFFSET_SHIFTKEY,
1692    "|": 0x2B + _OFFSET_SHIFTKEY,
1693    "del": 0x53 + _OFFSET_EXTENDEDKEY,
1694    "delete": 0x53 + _OFFSET_EXTENDEDKEY,
1695    "end": 0x4F + _OFFSET_EXTENDEDKEY,
1696    "capslock": 0x3A,
1697    "a": 0x1E,
1698    "s": 0x1F,
1699    "d": 0x20,
1700    "f": 0x21,
1701    "g": 0x22,
1702    "h": 0x23,
1703    "j": 0x24,
1704    "k": 0x25,
1705    "l": 0x26,
1706    ";": 0x27,
1707    "'": 0x28,
1708    "A": 0x1E + _OFFSET_SHIFTKEY,
1709    "S": 0x1F + _OFFSET_SHIFTKEY,
1710    "D": 0x20 + _OFFSET_SHIFTKEY,
1711    "F": 0x21 + _OFFSET_SHIFTKEY,
1712    "G": 0x22 + _OFFSET_SHIFTKEY,
1713    "H": 0x23 + _OFFSET_SHIFTKEY,
1714    "J": 0x24 + _OFFSET_SHIFTKEY,
1715    "K": 0x25 + _OFFSET_SHIFTKEY,
1716    "L": 0x26 + _OFFSET_SHIFTKEY,
1717    ":": 0x27 + _OFFSET_SHIFTKEY,
1718    '"': 0x28 + _OFFSET_SHIFTKEY,
1719    "enter": 0x1C,
1720    "return": 0x1C,
1721    "\n": 0x1C,
1722    "shift": _SHIFT_SCANCODE,
1723    "shiftleft": _SHIFT_SCANCODE,
1724    "z": 0x2C,
1725    "x": 0x2D,
1726    "c": 0x2E,
1727    "v": 0x2F,
1728    "b": 0x30,
1729    "n": 0x31,
1730    "m": 0x32,
1731    ",": 0x33,
1732    ".": 0x34,
1733    "/": 0x35,
1734    "Z": 0x2C + _OFFSET_SHIFTKEY,
1735    "X": 0x2D + _OFFSET_SHIFTKEY,
1736    "C": 0x2E + _OFFSET_SHIFTKEY,
1737    "V": 0x2F + _OFFSET_SHIFTKEY,
1738    "B": 0x30 + _OFFSET_SHIFTKEY,
1739    "N": 0x31 + _OFFSET_SHIFTKEY,
1740    "M": 0x32 + _OFFSET_SHIFTKEY,
1741    "<": 0x33 + _OFFSET_SHIFTKEY,
1742    ">": 0x34 + _OFFSET_SHIFTKEY,
1743    "?": 0x35 + _OFFSET_SHIFTKEY,
1744    "shiftright": 0x36,
1745    "ctrl": 0x1D,
1746    "ctrlleft": 0x1D,
1747    "win": 0x5B + _OFFSET_EXTENDEDKEY,
1748    "super": 0x5B + _OFFSET_EXTENDEDKEY,  # name from pyautogui
1749    "winleft": 0x5B + _OFFSET_EXTENDEDKEY,
1750    "alt": 0x38,
1751    "altleft": 0x38,
1752    " ": 0x39,
1753    "space": 0x39,
1754    "altright": 0x38 + _OFFSET_EXTENDEDKEY,
1755    "winright": 0x5C + _OFFSET_EXTENDEDKEY,
1756    "apps": 0x5D + _OFFSET_EXTENDEDKEY,
1757    "context": 0x5D + _OFFSET_EXTENDEDKEY,
1758    "contextmenu": 0x5D + _OFFSET_EXTENDEDKEY,
1759    "ctrlright": 0x1D + _OFFSET_EXTENDEDKEY,
1760    # Originally from learncodebygaming/pydirectinput:
1761    # arrow key scancodes can be different depending on the hardware,
1762    # so I think the best solution is to look it up based on the virtual key
1763    # https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mapvirtualkeya
1764    "up": _map_virtual_key(0x26, _MAPVK_VK_TO_VSC) + _OFFSET_EXTENDEDKEY,
1765    "left": _map_virtual_key(0x25, _MAPVK_VK_TO_VSC) + _OFFSET_EXTENDEDKEY,
1766    "down": _map_virtual_key(0x28, _MAPVK_VK_TO_VSC) + _OFFSET_EXTENDEDKEY,
1767    "right": _map_virtual_key(0x27, _MAPVK_VK_TO_VSC) + _OFFSET_EXTENDEDKEY,
1768    # While forking the original repository and working on the code,
1769    # I'm starting to doubt this still holds true.
1770    # As far as I can see, arrow keys are just the Numpad scancodes for Num
1771    # 2, 4, 6, and 8 with EXTENDEDKEY flag.
1772    # In fact, looking up the virtual key codes will just return the very same
1773    # scancodes the Numpad keys occupy.
1774    "help": 0x63,
1775    "sleep": 0x5F + _OFFSET_EXTENDEDKEY,
1776    "medianext": 0x19 + _OFFSET_EXTENDEDKEY,
1777    "nexttrack": 0x19 + _OFFSET_EXTENDEDKEY,
1778    "mediaprevious": 0x10 + _OFFSET_EXTENDEDKEY,
1779    "prevtrack": 0x10 + _OFFSET_EXTENDEDKEY,
1780    "mediastop": 0x24 + _OFFSET_EXTENDEDKEY,
1781    "stop": 0x24 + _OFFSET_EXTENDEDKEY,
1782    "mediaplay": 0x22 + _OFFSET_EXTENDEDKEY,
1783    "mediapause": 0x22 + _OFFSET_EXTENDEDKEY,
1784    "playpause": 0x22 + _OFFSET_EXTENDEDKEY,
1785    "mute": 0x20 + _OFFSET_EXTENDEDKEY,
1786    "volumemute": 0x20 + _OFFSET_EXTENDEDKEY,
1787    "volumeup": 0x30 + _OFFSET_EXTENDEDKEY,
1788    "volup": 0x30 + _OFFSET_EXTENDEDKEY,
1789    "volumedown": 0x2E + _OFFSET_EXTENDEDKEY,
1790    "voldown": 0x2E + _OFFSET_EXTENDEDKEY,
1791    "media": 0x6D + _OFFSET_EXTENDEDKEY,
1792    "launchmediaselect": 0x6D + _OFFSET_EXTENDEDKEY,
1793    "email": 0x6C + _OFFSET_EXTENDEDKEY,
1794    "launchmail": 0x6C + _OFFSET_EXTENDEDKEY,
1795    "calculator": 0x21 + _OFFSET_EXTENDEDKEY,
1796    "calc": 0x21 + _OFFSET_EXTENDEDKEY,
1797    "launch1": 0x6B + _OFFSET_EXTENDEDKEY,
1798    "launchapp1": 0x6B + _OFFSET_EXTENDEDKEY,
1799    "launch2": 0x21 + _OFFSET_EXTENDEDKEY,
1800    "launchapp2": 0x21 + _OFFSET_EXTENDEDKEY,
1801    "browsersearch": 0x65 + _OFFSET_EXTENDEDKEY,
1802    "browserhome": 0x32 + _OFFSET_EXTENDEDKEY,
1803    "browserforward": 0x69 + _OFFSET_EXTENDEDKEY,
1804    "browserback": 0x6A + _OFFSET_EXTENDEDKEY,
1805    "browserstop": 0x68 + _OFFSET_EXTENDEDKEY,
1806    "browserrefresh": 0x67 + _OFFSET_EXTENDEDKEY,
1807    "browserfavorites": 0x66 + _OFFSET_EXTENDEDKEY,
1808    "f13": 0x64,
1809    "f14": 0x65,
1810    "f15": 0x66,
1811    "f16": 0x67,
1812    "f17": 0x68,
1813    "f18": 0x69,
1814    "f19": 0x6A,
1815    "f20": 0x6B,
1816    "f21": 0x6C,
1817    "f22": 0x6D,
1818    "f23": 0x6E,
1819    "f24": 0x76,
1820}
1821"""
1822Maps a string representation of keyboard keys to their corresponding hardware
1823scan code. Based on standard US QWERTY-Layout.
1824
1825Not intended to be changed at runtime!
1826
1827If you want to change the keyboard mapping to better reflect your own keyboard
1828layout, use `KEYBOARD_MAPPING.update(your_dict)` where `your_dict` is a dict
1829that maps keynames to scancodes.
1830"""
1831
1832KEYBOARD_MAPPING: dict[str, ScancodeTypes] = {}
1833"""
1834Maps a string representation of keyboard keys to their corresponding hardware
1835scan code. Based on standard US QWERTY-Layout by default.
1836
1837If you want to change the keyboard mapping to better reflect your own keyboard
1838layout, use `KEYBOARD_MAPPING.update(your_dict)`, where `your_dict` is a dict
1839that maps keynames to scancodes.
1840"""
1841KEYBOARD_MAPPING.update(US_QWERTY_MAPPING)  # use US QWERTY by default
1842# ------------------------------------------------------------------------------
1843
1844
1845# ==============================================================================
1846# ===== Fail Safe and Pause implementation =====================================
1847# ==============================================================================
1848
1849
1850# ----- Exceptions -------------------------------------------------------------
1851class FailSafeException(Exception):
1852    """Raised when _failSafeCheck detects failsafe mouse position."""
1853
1854    pass
1855
1856
1857class PriorInputFailedException(Exception):
1858    """Raised in hold() context managers when raise_on_failure is set."""
1859
1860    pass
1861    # --------------------------------------------------------------------------
1862
1863
1864# ----- Failsafe Check ---------------------------------------------------------
1865def _failSafeCheck() -> None:
1866    """
1867    Check if mouse has been moved into one of the defined failsafe points,
1868    indicated by global var `FAILSAFE_POINTS`, and raise `FailSafeException`
1869    if that's the case.
1870
1871    Set global var `FAILSAFE` to False to stop raising exceptions.
1872    """
1873    if FAILSAFE and tuple(position()) in FAILSAFE_POINTS:
1874        raise FailSafeException(
1875            "PyDirectInput fail-safe triggered from mouse moving to a corner "
1876            "of the screen. "
1877            "To disable this fail-safe, set pydirectinput.FAILSAFE to False. "
1878            "DISABLING FAIL-SAFE IS NOT RECOMMENDED."
1879        )
1880    # --------------------------------------------------------------------------
1881
1882
1883# ----- handle pause for generic input checks ----------------------------------
1884def _handlePause(_pause: Any) -> None:
1885    """
1886    Pause the default amount of time if `_pause=True` in function arguments.
1887    """
1888    if _pause and PAUSE:
1889        _sleep(PAUSE)
1890    # --------------------------------------------------------------------------
1891
1892
1893# ----- generic input check decorator ------------------------------------------
1894_PS = ParamSpec("_PS")  # param spec
1895_RT = TypeVar("_RT")  # return type
1896
1897
1898# direct copy of _genericPyAutoGUIChecks()
1899def _genericPyDirectInputChecks(
1900    wrappedFunction: Callable[_PS, _RT]
1901) -> Callable[_PS, _RT]:
1902    """
1903    Decorator for wrapping input functions.
1904
1905    Performs failsafe checking and inserts artifical delay after input
1906    functions have been executed unless disabled.
1907
1908    The delay amount is set by global var `PAUSE`.
1909    """
1910
1911    @functools.wraps(wrappedFunction)
1912    def wrapper(*args: _PS.args, **kwargs: _PS.kwargs) -> _RT:
1913        returnVal: _RT
1914        if PAUSE:  # Skip _pause checks if PAUSE has been globally disabled.
1915            _pause: Any
1916            if "_pause" in kwargs:  # Fast track, low cost lookup.
1917                _pause = kwargs["_pause"]
1918            else:  # Slow track, inspect.getcallargs() is expensive.
1919                funcArgs: dict[str, Any] = inspect.getcallargs(
1920                    wrappedFunction, *args, **kwargs
1921                )
1922                _pause = funcArgs.get("_pause")
1923            _failSafeCheck()
1924            returnVal = wrappedFunction(*args, **kwargs)
1925            _handlePause(_pause)
1926        else:
1927            _failSafeCheck()
1928            returnVal = wrappedFunction(*args, **kwargs)
1929        return returnVal
1930
1931    return wrapper
1932    # --------------------------------------------------------------------------
1933
1934
1935# ==============================================================================
1936# ===== Helper Functions =======================================================
1937# ==============================================================================
1938# --------------------------------------------------------------------------
1939def _calc_normalized_screen_coord(pixel_coord: int, display_total: int) -> int:
1940    """
1941    Convert a pixel coordinate to normalized Windows screen coordinate value
1942    (range 0 - 65535) by taking the average of two neighboring pixels.
1943    """
1944    # formula from this strange (probably machine translated) article
1945    # 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/
1946    # win_coord = (x * 65536 + width - 1) // width
1947    # This alone is not enough, but we can do better by taking the average of
1948    # this pixel plus the next pixel.
1949    # In my testing this perfectly reflected the real pixel that SendInput
1950    # moves to, down to the pixels that Windows itself can't resolve.
1951    this_pixel: Final[int] = (
1952        pixel_coord * 65536 + display_total - 1
1953    ) // display_total
1954    next_pixel: Final[int] = (
1955        (pixel_coord + 1) * 65536 + display_total - 1
1956    ) // display_total
1957    return (this_pixel + next_pixel) // 2
1958    # --------------------------------------------------------------------------
1959
1960
1961# ----- translate pixels to normalized Windows coordinates ---------------------
1962def _to_windows_coordinates(
1963    x: int = 0, y: int = 0, *, virtual: bool = False
1964) -> tuple[int, int]:
1965    """
1966    Convert x,y coordinates to normalized windows coordinates and return as
1967    tuple (x, y).
1968    """
1969    display_width: int
1970    display_height: int
1971    offset_left: int
1972    offset_top: int
1973
1974    if virtual:
1975        display_width, display_height, offset_left, offset_top = virtual_size()
1976    else:
1977        display_width, display_height = size()
1978        offset_left, offset_top = 0, 0
1979
1980    windows_x: int = _calc_normalized_screen_coord(
1981        x - offset_left, display_width
1982    )
1983    windows_y: int = _calc_normalized_screen_coord(
1984        y - offset_top, display_height
1985    )
1986
1987    return windows_x, windows_y
1988    # --------------------------------------------------------------------------
1989
1990
1991# ----- line coordinates based on Bresenham's line algorithm -------------------
1992def _bresenham(x0: int, y0: int, x1: int, y1: int) -> list[tuple[int, int]]:
1993    """
1994    Return a list of coordinates on the line from (x0, y0) to (x1, y1).
1995    Start and end point are included in the result.
1996
1997    Based on Bresenham's line algorithm.
1998    Using Petr Viktorin's implementation (MIT license)
1999    https://github.com/encukou/bresenham
2000    """
2001
2002    def gen() -> Generator[tuple[int, int], None, None]:
2003        """
2004        Yield integer coordinates on the line from (x0, y0) to (x1, y1).
2005
2006        Input coordinates should be integers.
2007
2008        The result will contain both the start and the end point.
2009        """
2010        dx: int = x1 - x0
2011        dy: int = y1 - y0
2012
2013        xsign: Literal[1, -1] = 1 if dx > 0 else -1
2014        ysign: Literal[1, -1] = 1 if dy > 0 else -1
2015
2016        dx = abs(dx)
2017        dy = abs(dy)
2018
2019        xx: Literal[1, 0, -1]
2020        xy: Literal[1, 0, -1]
2021        yx: Literal[1, 0, -1]
2022        yy: Literal[1, 0, -1]
2023
2024        if dx > dy:
2025            xx, xy, yx, yy = xsign, 0, 0, ysign
2026        else:
2027            dx, dy = dy, dx
2028            xx, xy, yx, yy = 0, ysign, xsign, 0
2029
2030        d: int = 2 * dy - dx
2031        y = 0
2032        for x in range(dx + 1):
2033            yield x0 + x * xx + y * yx, y0 + x * xy + y * yy
2034            if d >= 0:
2035                y += 1
2036                d -= 2 * dx
2037            d += 2 * dy
2038
2039    return list(gen())
2040    # --------------------------------------------------------------------------
2041
2042
2043# ----- path function type alias -----------------------------------------------
2044PathFunction: TypeAlias = (
2045    "Callable[[int, int, int, int], list[tuple[int, int]]]"
2046)
2047# ------------------------------------------------------------------------------
2048
2049
2050# ----- linear tweening function -----------------------------------------------
2051def _linear(n: float) -> float:
2052    """
2053    Linear tweening function that clamps n between 0.0 and 1.0.
2054    Otherwise returns n unchanged.
2055    """
2056    if n >= 1.0:
2057        return 1.0
2058    if n <= 0.0:
2059        return 0.0
2060    return n
2061    # --------------------------------------------------------------------------
2062
2063
2064# ----- get mouse position -----------------------------------------------------
2065def position(
2066    x: int | float | None = None, y: int | float | None = None
2067) -> tuple[int, int]:
2068    """
2069    Return a postion tuple `(x, y)`.
2070
2071    If x and/or y argument(s) are not given, use current mouse cursor coordinate
2072    instead.
2073    """
2074    cursor: _POINT = _get_cursor_pos()
2075    return (
2076        cursor.x if x is None else int(x),
2077        cursor.y if y is None else int(y),
2078    )
2079    # --------------------------------------------------------------------------
2080
2081
2082# ----- get primary screen resolution ------------------------------------------
2083def size() -> tuple[int, int]:
2084    """
2085    Return the size of the primary display as tuple `(width, height)`.
2086    """
2087    return (
2088        _get_system_metrics(_SM_CXSCREEN),
2089        _get_system_metrics(_SM_CYSCREEN),
2090    )
2091    # --------------------------------------------------------------------------
2092
2093
2094# ----- get resolution of multi monitor bounding box ---------------------------
2095def virtual_size() -> tuple[int, int, int, int]:
2096    """
2097    Return the the display size of the complete virtual monitor bounding box
2098    rectangle as tuple `(width, height, left_offset, top_offset)`.
2099
2100    On a single monitor system, this function is equal to `(*size(), 0, 0)`.
2101
2102    `left_offset` and `top_offset` are measured from the top left pixel of the
2103    primary monitor.
2104    """
2105    return (
2106        _get_system_metrics(_SM_CXVIRTUALSCREEN),
2107        _get_system_metrics(_SM_CYVIRTUALSCREEN),
2108        _get_system_metrics(_SM_XVIRTUALSCREEN),
2109        _get_system_metrics(_SM_YVIRTUALSCREEN),
2110    )
2111    # --------------------------------------------------------------------------
2112
2113
2114# ----- are coordinates on primary monitor -------------------------------------
2115@overload
2116def on_primary_monitor(x: int | None = None, y: int | None = None) -> bool:
2117    ...
2118
2119
2120@overload
2121def on_primary_monitor(x: tuple[int, int], y: None = None) -> bool:
2122    ...
2123
2124
2125def on_primary_monitor(
2126    x: int | tuple[int, int] | None = None, y: int | None = None
2127) -> bool:
2128    """
2129    Returns whether the given xy coordinates are on the primary screen or not.
2130
2131    If x and/or y argument(s) are not given, current mouse cursor coordinates
2132    will be used instead.
2133    """
2134    if isinstance(x, Sequence):
2135        assert not isinstance(x, int)  # remove int annotation, mypy needs this
2136        if y is not None:
2137            raise ValueError(
2138                "onScreen() does not accept Sequence-types as first argument "
2139                "if a second argument is also provided!"
2140            )
2141        try:
2142            x, y = x[0], x[1]
2143        except IndexError as e:
2144            raise ValueError(
2145                "onScreen() does not accept single element sequences "
2146                "as first argument!"
2147            ) from e
2148
2149    x, y = position(x, y)
2150    display_width: int
2151    display_height: int
2152    display_width, display_height = size()
2153
2154    return 0 <= x < display_width and 0 <= y < display_height
2155
2156
2157onScreen = on_primary_monitor
2158# ------------------------------------------------------------------------------
2159
2160
2161# ----- are coordinates on any monitor -----------------------------------------
2162@overload
2163def valid_screen_coordinates(
2164    x: int | None = None,
2165    y: int | None = None,
2166) -> bool:
2167    ...
2168
2169
2170@overload
2171def valid_screen_coordinates(x: tuple[int, int], y: None = None) -> bool:
2172    ...
2173
2174
2175def valid_screen_coordinates(
2176    x: int | tuple[int, int] | None = None, y: int | None = None
2177) -> bool:
2178    """
2179    Returns whether the given xy coordinates are on a real monitor or not.
2180
2181    If x and/or y argument(s) are not given, current mouse cursor coordinates
2182    will be used instead.
2183    """
2184    if isinstance(x, Sequence):
2185        assert not isinstance(x, int)  # remove int annotation, mypy needs this
2186        if y is not None:
2187            raise ValueError(
2188                "onScreen() does not accept Sequence-types as first argument "
2189                "if a second argument is also provided!"
2190            )
2191        try:
2192            x, y = x[0], x[1]
2193        except IndexError as e:
2194            raise ValueError(
2195                "onScreen() does not accept single element sequences "
2196                "as first argument!"
2197            ) from e
2198
2199    x, y = position(x, y)
2200    return _monitor_from_point(x, y) is not None
2201    # --------------------------------------------------------------------------
2202
2203
2204# ----- lookup function for MOUSEINPUT data ------------------------------------
2205def _get_mouse_struct_data(
2206    button: str, method: Literal[0, 1, 2]
2207) -> tuple[int | None, int]:
2208    """
2209    Translate a button string to INPUT struct data.
2210
2211    Automatically detect the correct button if `MOUSE_PRIMARY` or
2212    `MOUSE_SECONDARY` are given as the button argument.
2213    """
2214    if not (0 <= method <= 2):
2215        raise ValueError(f"method index {method} is not a valid argument!")
2216
2217    buttons_swapped: bool
2218    if button == MOUSE_PRIMARY:
2219        buttons_swapped = _get_system_metrics(_SM_SWAPBUTTON) != 0
2220        button = MOUSE_RIGHT if buttons_swapped else MOUSE_LEFT
2221    elif button == MOUSE_SECONDARY:
2222        buttons_swapped = _get_system_metrics(_SM_SWAPBUTTON) != 0
2223        button = MOUSE_LEFT if buttons_swapped else MOUSE_RIGHT
2224
2225    event_value: int | None
2226    event_value = _MOUSE_MAPPING_EVENTF.get(button, (None, None, None))[method]
2227    mouseData: int = _MOUSE_MAPPING_DATA.get(button, 0)
2228
2229    return event_value, mouseData
2230    # --------------------------------------------------------------------------
2231
2232
2233# ----- normalize key name to lower case if not shifiting ----------------------
2234def _normalize_key(key: str, *, auto_shift: bool = False) -> str:
2235    """
2236    return a lowercase representation of `key` if key is longer than one
2237    character or automatic shifting is disabled (default).
2238    """
2239    return key.lower() if (len(key) > 1 or not auto_shift) else key
2240    # --------------------------------------------------------------------------
2241
2242
2243# ------------------------------------------------------------------------------
2244# ----- Mouse acceleration and Ehanced Pointer Precision storage singleton -----
2245class __MouseSpeedSettings:
2246    """
2247    Allows controlled storage of Windows Enhanced Pointer Precision and mouse
2248    speed settings.
2249    """
2250
2251    __context_manager_epp: ClassVar[int | None] = None
2252    __context_manager_speed: ClassVar[int | None] = None
2253    __context_manager_count: ClassVar[int] = 0
2254    __context_manager_lock: ClassVar[Lock] = Lock()
2255    __manual_store_epp: ClassVar[int | None] = None
2256    __manual_store_speed: ClassVar[int | None] = None
2257    __manual_lock: ClassVar[Lock] = Lock()
2258    # --------------------------------------------------------------------------
2259
2260    @classmethod
2261    def get_manual_mouse_settings(cls) -> tuple[int | None, int | None]:
2262        with cls.__manual_lock:
2263            return (cls.__manual_store_epp, cls.__manual_store_speed)
2264        # ----------------------------------------------------------------------
2265
2266    @classmethod
2267    def set_manual_mouse_settings(
2268        cls, enhanced_pointer_precision_enabled: int, mouse_speed: int
2269    ) -> None:
2270        with cls.__manual_lock:
2271            cls.__manual_store_epp = enhanced_pointer_precision_enabled
2272            cls.__manual_store_speed = mouse_speed
2273        # ----------------------------------------------------------------------
2274
2275    @classmethod
2276    def get_ctxtmgr_mouse_settings(cls) -> tuple[int | None, int | None]:
2277        with cls.__context_manager_lock:
2278            cls.__context_manager_count -= 1
2279            if cls.__context_manager_count > 0:
2280                # Don't retrieve stored value until last
2281                return (None, None)
2282            epp_enabled: int | None = cls.__context_manager_epp
2283            mouse_speed: int | None = cls.__context_manager_speed
2284            cls.__context_manager_epp = None
2285            cls.__context_manager_speed = None
2286            return (epp_enabled, mouse_speed)
2287        # ----------------------------------------------------------------------
2288
2289    @classmethod
2290    def set_ctxtmgr_mouse_settings(
2291        cls, enhanced_pointer_precision_enabled: int, mouse_speed: int
2292    ) -> None:
2293        with cls.__context_manager_lock:
2294            cls.__context_manager_count += 1
2295            if cls.__context_manager_count > 1:
2296                # Don't allow changing the stored value if another value is
2297                # already stored
2298                return
2299            cls.__context_manager_epp = enhanced_pointer_precision_enabled
2300            cls.__context_manager_speed = mouse_speed
2301        # ----------------------------------------------------------------------
2302
2303
2304# ----- Temporarily disable Enhanced Pointer Precision -------------------------
2305@contextmanager
2306def _no_mouse_acceleration() -> Generator[None, None, None]:
2307    """
2308    Context manager that allows temporarily disabling Windows Enhanced Pointer
2309    Precision on enter and restoring the previous setting on exit.
2310    """
2311    th1: int
2312    th2: int
2313    precision: int | None
2314    # store mouse parameters (thresholds, enhanced pointer precision)
2315    th1, th2, precision = _get_mouse_parameters()
2316    speed: int | None = _get_mouse_speed()
2317    assert isinstance(precision, int)
2318    assert isinstance(speed, int)
2319    __MouseSpeedSettings.set_ctxtmgr_mouse_settings(precision, speed)
2320    try:
2321        # modify mouse parameters
2322        if precision != 0:
2323            _set_mouse_parameters(th1, th2, 0)
2324        if speed != 10:
2325            _set_mouse_speed(10)
2326        yield
2327    finally:
2328        # restore mouse parameters
2329        precision, speed = __MouseSpeedSettings.get_ctxtmgr_mouse_settings()
2330        if precision is not None:
2331            _set_mouse_parameters(th1, th2, precision)
2332        if speed is not None:
2333            _set_mouse_speed(speed)
2334    # --------------------------------------------------------------------------
2335
2336
2337# ----- manually store current enhanced pointer precision setting --------------
2338def store_mouse_acceleration_settings() -> None:
2339    """
2340    Manually save the current Windows Enhanced Pointer Precision setting so
2341    that it can be restored later with `restore_mouse_acceleration_settings()`.
2342    """
2343    precision: int
2344    _, _, precision = _get_mouse_parameters()
2345    speed: int = _get_mouse_speed()
2346    __MouseSpeedSettings.set_manual_mouse_settings(precision, speed)
2347    # --------------------------------------------------------------------------
2348
2349
2350# ----- manually restore current enhanced pointer precision setting ------------
2351def restore_mouse_acceleration_settings() -> None:
2352    """
2353    Manually restore the current Windows Enhanced Pointer Precision setting to
2354    what it was beforehand when it was saved with
2355    `store_mouse_acceleration_settings()`.
2356    """
2357    precision: int | None
2358    speed: int | None
2359    precision, speed = __MouseSpeedSettings.get_manual_mouse_settings()
2360    if precision is None or speed is None:
2361        raise ValueError(
2362            "Can't restore Enhanced Pointer Precision setting! "
2363            "Setting was not saved beforehand!"
2364        )
2365    th1: int
2366    th2: int
2367    th1, th2, _ = _get_mouse_parameters()
2368    _set_mouse_parameters(th1, th2, precision)
2369    _set_mouse_speed(speed)
2370    # --------------------------------------------------------------------------
2371
2372
2373# ==============================================================================
2374# ===== Main Mouse Functions ===================================================
2375# ==============================================================================
2376
2377
2378# ----- mouseDown --------------------------------------------------------------
2379@_genericPyDirectInputChecks
2380def mouseDown(
2381    x: int | None = None,
2382    y: int | None = None,
2383    button: str = MOUSE_PRIMARY,
2384    duration: float = 0.0,
2385    tween: Callable[[float], float] | None = None,
2386    logScreenshot: bool = False,
2387    _pause: bool = True,
2388    *,
2389    relative: bool = False,
2390    virtual: bool = False,
2391    path_function: PathFunction | None = None,
2392    attempt_pixel_perfect: bool = False,
2393    disable_mouse_acceleration: bool = False,
2394) -> None:
2395    """
2396    Press down mouse button `button`.
2397
2398    If `x` or `y` are given and not None, then the mouse will move the
2399    indicated postion before pressing the button.
2400
2401    `button` is the name of the button to press. Use the public `MOUSE_*`
2402    constants to get valid argument values. (If you change the constants, then
2403    you will have to call `update_MOUSEEVENT_mappings()` to resync the lookup
2404    functions)
2405
2406    If `_pause` is True (default), then an automatic sleep will be performed
2407    after the function finshes executing. The duration is set by the global
2408    variable `PAUSE`.
2409
2410    `duration`, `tween`, `relative`, `virtual`, `path_function`,
2411    `attempt_pixel_perfect`, `disable_mouse_acceleration` are only relevant
2412    if x or y are given.
2413    See `moveTo()` for further information.
2414
2415    Raises `ValueError` if `button` is not a valid mouse button name.
2416
2417    ----------------------------------------------------------------------------
2418
2419    NOTE: `logScreenshot` is currently unsupported.
2420    """
2421    # TODO: bounding box check for valid position
2422    if x is not None or y is not None:
2423        moveTo(
2424            x,
2425            y,
2426            duration=duration,
2427            tween=tween,
2428            logScreenshot=logScreenshot,
2429            _pause=False,  # don't add an additional pause
2430            relative=relative,
2431            virtual=virtual,
2432            path_function=path_function,
2433            attempt_pixel_perfect=attempt_pixel_perfect,
2434            disable_mouse_acceleration=disable_mouse_acceleration,
2435        )
2436
2437    event_value: int | None = None
2438    mouseData: int
2439    event_value, mouseData = _get_mouse_struct_data(button, _MOUSE_PRESS)
2440
2441    if not event_value:
2442        raise ValueError(
2443            f"Invalid button argument! "
2444            f'Expected "{MOUSE_LEFT}", "{MOUSE_RIGHT}", "{MOUSE_MIDDLE}", '
2445            f'"{MOUSE_BUTTON4}", "{MOUSE_BUTTON5}", "{MOUSE_PRIMARY}" or '
2446            f'"{MOUSE_SECONDARY}"; got "{button}" instead!'
2447        )
2448
2449    _send_input(_create_mouse_input(mouseData=mouseData, dwFlags=event_value))
2450    # --------------------------------------------------------------------------
2451
2452
2453# ----- mouseUp ----------------------------------------------------------------
2454@_genericPyDirectInputChecks
2455def mouseUp(
2456    x: int | None = None,
2457    y: int | None = None,
2458    button: str = MOUSE_PRIMARY,
2459    duration: float = 0.0,
2460    tween: Callable[[float], float] | None = None,
2461    logScreenshot: bool = False,
2462    _pause: bool = True,
2463    *,
2464    relative: bool = False,
2465    virtual: bool = False,
2466    path_function: PathFunction | None = None,
2467    attempt_pixel_perfect: bool = False,
2468    disable_mouse_acceleration: bool = False,
2469) -> None:
2470    """
2471    Lift up mouse button `button`.
2472
2473    If `x` or `y` are given and not None, then the mouse will move the
2474    indicated postion before lifting the button.
2475
2476    `button` is the name of the button to press. Use the public `MOUSE_*`
2477    constants to get valid argument values. (If you change the constants, then
2478    you will have to call `update_MOUSEEVENT_mappings()` to resync the lookup
2479    functions)
2480
2481    If `_pause` is True (default), then an automatic sleep will be performed
2482    after the function finshes executing. The duration is set by the global
2483    variable `PAUSE`.
2484
2485    `duration`, `tween`, `relative`, `virtual`, `path_function`,
2486    `attempt_pixel_perfect`, `disable_mouse_acceleration` are only relevant
2487    if x or y are given.
2488    See `moveTo()` for further information.
2489
2490    Raises `ValueError` if `button` is not a valid mouse button name.
2491
2492    ----------------------------------------------------------------------------
2493
2494    NOTE: `logScreenshot` is currently unsupported.
2495    """
2496    # TODO: bounding box check for valid position
2497    if x is not None or y is not None:
2498        moveTo(
2499            x,
2500            y,
2501            duration=duration,
2502            tween=tween,
2503            logScreenshot=logScreenshot,
2504            _pause=False,  # don't add an additional pause
2505            relative=relative,
2506            virtual=virtual,
2507            path_function=path_function,
2508            attempt_pixel_perfect=attempt_pixel_perfect,
2509            disable_mouse_acceleration=disable_mouse_acceleration,
2510        )
2511
2512    event_value: int | None = None
2513    mouseData: int
2514    event_value, mouseData = _get_mouse_struct_data(button, _MOUSE_RELEASE)
2515
2516    if not event_value:
2517        raise ValueError(
2518            "Invalid button argument! "
2519            f'Expected "{MOUSE_LEFT}", "{MOUSE_RIGHT}", "{MOUSE_MIDDLE}", '
2520            f'"{MOUSE_BUTTON4}", "{MOUSE_BUTTON5}", "{MOUSE_PRIMARY}" or '
2521            f'"{MOUSE_SECONDARY}"; got "{button}" instead!'
2522        )
2523
2524    _send_input(_create_mouse_input(mouseData=mouseData, dwFlags=event_value))
2525    # --------------------------------------------------------------------------
2526
2527
2528# ----- click ------------------------------------------------------------------
2529@_genericPyDirectInputChecks
2530def click(
2531    x: int | None = None,
2532    y: int | None = None,
2533    clicks: int = 1,
2534    interval: float = 0.0,
2535    button: str = MOUSE_PRIMARY,
2536    duration: float = 0.0,
2537    tween: Callable[[float], float] | None = None,
2538    logScreenshot: bool = False,
2539    _pause: bool = True,
2540    *,
2541    relative: bool = False,
2542    virtual: bool = False,
2543    path_function: PathFunction | None = None,
2544    attempt_pixel_perfect: bool = False,
2545    disable_mouse_acceleration: bool = False,
2546) -> None:
2547    """
2548    Click mouse button `button` (combining press down and lift up).
2549
2550    If `x` or `y` are given and not None, then the mouse will move the
2551    indicated postion before clicking the button.
2552
2553    `button` is the name of the button to press. Use the public `MOUSE_*`
2554    constants to get valid argument values. (If you change the constants, then
2555    you will have to call `update_MOUSEEVENT_mappings()` to resync the lookup
2556    functions)
2557
2558    `clicks` is an integer that determines the amount of times the button will
2559    be clicked.
2560
2561    `interval` is the wait time in seconds between clicks.
2562
2563    If `_pause` is True (default), then an automatic sleep will be performed
2564    after the function finshes executing. The duration is set by the global
2565    variable `PAUSE`.
2566
2567    `duration`, `tween`, `relative`, `virtual`, `path_function`,
2568    `attempt_pixel_perfect`, `disable_mouse_acceleration` are only relevant
2569    if x or y are given.
2570    See `moveTo()` for further information.
2571
2572    Raises `ValueError` if `button` is not a valid mouse button name.
2573
2574    ----------------------------------------------------------------------------
2575
2576    NOTE: `logScreenshot` is currently unsupported.
2577    """
2578    # TODO: bounding box check for valid position
2579    if x is not None or y is not None:
2580        moveTo(
2581            x,
2582            y,
2583            duration=duration,
2584            tween=tween,
2585            logScreenshot=logScreenshot,
2586            _pause=False,  # don't add an additional pause
2587            relative=relative,
2588            virtual=virtual,
2589            path_function=path_function,
2590            attempt_pixel_perfect=attempt_pixel_perfect,
2591            disable_mouse_acceleration=disable_mouse_acceleration,
2592        )
2593
2594    event_value: int | None = None
2595    mouseData: int
2596    event_value, mouseData = _get_mouse_struct_data(button, _MOUSE_CLICK)
2597
2598    if not event_value:
2599        raise ValueError(
2600            f"Invalid button argument! "
2601            f'Expected "{MOUSE_LEFT}", "{MOUSE_RIGHT}", "{MOUSE_MIDDLE}", '
2602            f'"{MOUSE_BUTTON4}", "{MOUSE_BUTTON5}", "{MOUSE_PRIMARY}" or '
2603            f'"{MOUSE_SECONDARY}"; got "{button}" instead!'
2604        )
2605
2606    apply_interval: bool = False
2607    for _ in range(clicks):
2608        if apply_interval:  # Don't delay first press
2609            _sleep(interval)
2610        apply_interval = True
2611
2612        _send_input(
2613            _create_mouse_input(mouseData=mouseData, dwFlags=event_value)
2614        )
2615    # --------------------------------------------------------------------------
2616
2617
2618# ----- leftClick --------------------------------------------------------------
2619def leftClick(
2620    x: int | None = None,
2621    y: int | None = None,
2622    interval: float = 0.0,
2623    duration: float = 0.0,
2624    tween: Callable[[float], float] | None = None,
2625    logScreenshot: bool = False,
2626    _pause: bool = True,
2627    *,
2628    relative: bool = False,
2629    virtual: bool = False,
2630    path_function: PathFunction | None = None,
2631    attempt_pixel_perfect: bool = False,
2632    disable_mouse_acceleration: bool = False,
2633) -> None:
2634    """
2635    Click Left Mouse button.
2636
2637    See `click()` for more information
2638    """
2639    click(
2640        x,
2641        y,
2642        clicks=1,
2643        interval=interval,
2644        button=MOUSE_LEFT,
2645        duration=duration,
2646        tween=tween,
2647        logScreenshot=logScreenshot,
2648        _pause=_pause,  # Keep _pause since this function has no input checks
2649        relative=relative,
2650        virtual=virtual,
2651        path_function=path_function,
2652        attempt_pixel_perfect=attempt_pixel_perfect,
2653        disable_mouse_acceleration=disable_mouse_acceleration,
2654    )
2655    # --------------------------------------------------------------------------
2656
2657
2658# ----- rightClick -------------------------------------------------------------
2659def rightClick(
2660    x: int | None = None,
2661    y: int | None = None,
2662    interval: float = 0.0,
2663    duration: float = 0.0,
2664    tween: Callable[[float], float] | None = None,
2665    logScreenshot: bool = False,
2666    _pause: bool = True,
2667    *,
2668    relative: bool = False,
2669    virtual: bool = False,
2670    path_function: PathFunction | None = None,
2671    attempt_pixel_perfect: bool = False,
2672    disable_mouse_acceleration: bool = False,
2673) -> None:
2674    """
2675    Click Right Mouse button.
2676
2677    See `click()` for more information
2678    """
2679    click(
2680        x,
2681        y,
2682        clicks=1,
2683        interval=interval,
2684        button=MOUSE_RIGHT,
2685        duration=duration,
2686        tween=tween,
2687        logScreenshot=logScreenshot,
2688        _pause=_pause,  # Keep _pause since this function has no input checks
2689        relative=relative,
2690        virtual=virtual,
2691        path_function=path_function,
2692        attempt_pixel_perfect=attempt_pixel_perfect,
2693        disable_mouse_acceleration=disable_mouse_acceleration,
2694    )
2695    # --------------------------------------------------------------------------
2696
2697
2698# ----- middleClick ------------------------------------------------------------
2699def middleClick(
2700    x: int | None = None,
2701    y: int | None = None,
2702    interval: float = 0.0,
2703    duration: float = 0.0,
2704    tween: Callable[[float], float] | None = None,
2705    logScreenshot: bool = False,
2706    _pause: bool = True,
2707    *,
2708    relative: bool = False,
2709    virtual: bool = False,
2710    path_function: PathFunction | None = None,
2711    attempt_pixel_perfect: bool = False,
2712    disable_mouse_acceleration: bool = False,
2713) -> None:
2714    """
2715    Click Middle Mouse button.
2716
2717    See `click()` for more information
2718    """
2719    click(
2720        x,
2721        y,
2722        clicks=1,
2723        interval=interval,
2724        button=MOUSE_MIDDLE,
2725        duration=duration,
2726        tween=tween,
2727        logScreenshot=logScreenshot,
2728        _pause=_pause,  # Keep _pause since this function has no input checks
2729        relative=relative,
2730        virtual=virtual,
2731        path_function=path_function,
2732        attempt_pixel_perfect=attempt_pixel_perfect,
2733        disable_mouse_acceleration=disable_mouse_acceleration,
2734    )
2735    # --------------------------------------------------------------------------
2736
2737
2738# ----- doubleClick ------------------------------------------------------------
2739def doubleClick(
2740    x: int | None = None,
2741    y: int | None = None,
2742    interval: float = 0.0,
2743    button: str = MOUSE_LEFT,
2744    duration: float = 0.0,
2745    tween: Callable[[float], float] | None = None,
2746    logScreenshot: bool = False,
2747    _pause: bool = True,
2748    *,
2749    relative: bool = False,
2750    virtual: bool = False,
2751    path_function: PathFunction | None = None,
2752    attempt_pixel_perfect: bool = False,
2753    disable_mouse_acceleration: bool = False,
2754) -> None:
2755    """
2756    Double click `button`.
2757
2758    See `click()` for more information
2759    """
2760    click(
2761        x,
2762        y,
2763        clicks=2,
2764        interval=interval,
2765        button=button,
2766        duration=duration,
2767        tween=tween,
2768        logScreenshot=logScreenshot,
2769        _pause=_pause,  # Keep _pause since this function has no input checks
2770        relative=relative,
2771        virtual=virtual,
2772        path_function=path_function,
2773        attempt_pixel_perfect=attempt_pixel_perfect,
2774        disable_mouse_acceleration=disable_mouse_acceleration,
2775    )
2776    # --------------------------------------------------------------------------
2777
2778
2779# ----- tripleClick ------------------------------------------------------------
2780def tripleClick(
2781    x: int | None = None,
2782    y: int | None = None,
2783    interval: float = 0.0,
2784    button: str = MOUSE_LEFT,
2785    duration: float = 0.0,
2786    tween: Callable[[float], float] | None = None,
2787    logScreenshot: bool = False,
2788    _pause: bool = True,
2789    *,
2790    relative: bool = False,
2791    virtual: bool = False,
2792    path_function: PathFunction | None = None,
2793    attempt_pixel_perfect: bool = False,
2794    disable_mouse_acceleration: bool = False,
2795) -> None:
2796    """
2797    Triple click `button`.
2798
2799    See `click()` for more information
2800    """
2801    click(
2802        x,
2803        y,
2804        clicks=3,
2805        interval=interval,
2806        button=button,
2807        duration=duration,
2808        tween=tween,
2809        logScreenshot=logScreenshot,
2810        _pause=_pause,  # Keep _pause since this function has no input checks
2811        relative=relative,
2812        virtual=virtual,
2813        path_function=path_function,
2814        attempt_pixel_perfect=attempt_pixel_perfect,
2815        disable_mouse_acceleration=disable_mouse_acceleration,
2816    )
2817    # --------------------------------------------------------------------------
2818
2819
2820# ----- scroll -----------------------------------------------------------------
2821# Originally implemented by
2822# https://github.com/learncodebygaming/pydirectinput/pull/22
2823@_genericPyDirectInputChecks
2824def scroll(
2825    clicks: int = 0,
2826    x: Any = None,  # x and y do absolutely nothing, still keeping the arguments
2827    y: Any = None,  # to stay consistent with PyAutoGUI.
2828    logScreenshot: bool = False,
2829    _pause: bool = True,
2830    *,
2831    interval: float = 0.0,
2832) -> None:
2833    """
2834    Vertically scroll mouse `clicks` number of times, waiting `interval`
2835    seconds between every scroll.
2836
2837    Negative values of `clicks` will scroll down, postive values will scroll
2838    up.
2839
2840    `x` and `y` are intentionally ignored and only exists to keep the call
2841    signature backwards-compatible with PyAutoGui.
2842    If you need to change the mouse position before scrolling use one of the
2843    `move()` functions.
2844
2845    If `_pause` is True (default), then an automatic sleep will be performed
2846    after the function finshes executing. The duration is set by the global
2847    variable `PAUSE`.
2848
2849    ----------------------------------------------------------------------------
2850
2851    NOTE: `logScreenshot` is currently unsupported.
2852    """
2853    direction: Literal[-1, 1]
2854    if clicks >= 0:
2855        direction = 1
2856    else:
2857        direction = -1
2858        clicks = abs(clicks)
2859
2860    apply_interval: bool = False
2861    for _ in range(clicks):
2862        if apply_interval:
2863            _sleep(interval)
2864        apply_interval = True
2865
2866        _send_input(
2867            _create_mouse_input(
2868                mouseData=(direction * _WHEEL_DELTA), dwFlags=_MOUSEEVENTF_WHEEL
2869            )
2870        )
2871    # --------------------------------------------------------------------------
2872
2873
2874# ----- hscroll ----------------------------------------------------------------
2875@_genericPyDirectInputChecks
2876def hscroll(
2877    clicks: int = 0,
2878    x: Any = None,  # x and y do absolutely nothing, still keeping the arguments
2879    y: Any = None,  # to stay consistent with PyAutoGUI.
2880    logScreenshot: bool = False,
2881    _pause: bool = True,
2882    *,
2883    interval: float = 0.0,
2884) -> None:
2885    """
2886    Horizontally scroll mouse `clicks` number of times, waiting `interval`
2887    seconds between every scroll.
2888
2889    Negative values of `clicks` will scroll left, postive values will scroll
2890    right.
2891
2892    `x` and `y` are intentionally ignored and only exists to keep the call
2893    signature backwards-compatible with PyAutoGui.
2894    If you need to change the mouse position before scrolling use one of the
2895    `move()` functions.
2896
2897    If `_pause` is True (default), then an automatic sleep will be performed
2898    after the function finshes executing. The duration is set by the global
2899    variable `PAUSE`.
2900
2901    ----------------------------------------------------------------------------
2902
2903    NOTE: `logScreenshot` is currently unsupported.
2904    """
2905    direction: Literal[-1, 1]
2906    if clicks >= 0:
2907        direction = 1
2908    else:
2909        direction = -1
2910        clicks = abs(clicks)
2911
2912    apply_interval: bool = False
2913    for _ in range(clicks):
2914        if apply_interval:
2915            _sleep(interval)
2916        apply_interval = True
2917
2918        _send_input(
2919            _create_mouse_input(
2920                mouseData=(direction * _WHEEL_DELTA),
2921                dwFlags=_MOUSEEVENTF_HWHEEL,
2922            )
2923        )
2924    # --------------------------------------------------------------------------
2925
2926
2927# ----- scroll alias -----------------------------------------------------------
2928vscroll = scroll
2929# ------------------------------------------------------------------------------
2930
2931
2932# ----- absolute_mouse_move ----------------------------------------------------
2933def _absolute_mouse_move(
2934    x: int | None = None,
2935    y: int | None = None,
2936    duration: float = 0.0,
2937    tween: Callable[[float], float] | None = None,
2938    logScreenshot: bool = False,
2939    target_coords_relative: bool = False,
2940    *,
2941    virtual: bool = False,
2942    path_function: PathFunction | None = None,
2943    attempt_pixel_perfect: bool = False,
2944    disable_mouse_acceleration: bool = False,
2945) -> None:
2946    """
2947    Move the mouse to an absolute(*) postion indicated by the arguments of
2948    `x` and `y`. The coordinates 0,0 represent the top left pixel of the
2949    primary monitor.
2950
2951    If `duration` is floating point number greater than 0, then this function
2952    will automatically split the movement into microsteps instead of moving
2953    straight to the target position.
2954
2955    `tween` is a function that takes a floating point number between 0.0 and
2956    1.0 and returns another floating point number between 0.0 and 1.0. The
2957    returned number will be used to calculate the next position of the
2958    mouse between the start and the end position based on the current duration.
2959    The default tweening function is linear, which will move the mouse at a
2960    constant speed. See the `pytweening` package for more tweening functions.
2961
2962    `path_function` is a function that takes the start and end coordinates of
2963    the mouse movement (4 integers) and returns a list of coordinates (list of
2964    tuples containting 2 integers each) that the mouse will move through.
2965    The default path function is Bresenham's line algorithm, which will move
2966    the mouse in a straight line.
2967
2968    (*) If `target_coords_relative` is set: Use absolute mouse movement to move
2969    the mouse cursor to the current mouse position offset by arguments
2970    `x` and `y`.
2971
2972    If `_pause` is True (default), then an automatic sleep will be performed
2973    after the function finshes executing. The duration is set by the global
2974    variable `PAUSE`.
2975
2976    Setting `virtual` to True (default: False) changes the way internal APIs
2977    handle coordinates and is intended for multi monitor systems. It should be
2978    pretty much unncessary even for multi monitor systems, since all the
2979    necessary internal calculations beyond the border of the primay monitor
2980    work without it.
2981
2982    The way that Windows calculates the target pixel coordinates internally
2983    unfortunately leads to inaccuracies and unreachable pixels, especially
2984    if the `virtual` option is used.
2985
2986    If you need the target position to be pixel perfect, you can try setting
2987    `attempt_pixel_perfect` to True, which will use tiny relative movements
2988    to correct the unreachable position.
2989
2990    Relative movement is influenced by mouse speed and Windows Enhanced Pointer
2991    Precision, which can be temporarily disabled by setting
2992    `disable_mouse_acceleration`.
2993
2994    ----------------------------------------------------------------------------
2995    Careful! Disabling mouse acceleration settings is MAYBE thread-safe,
2996    NOT multiprocessing-safe, and DEFINITELY NOT independent processses safe!
2997
2998    If you you start a relative movement while another is already in progress
2999    than the second movement could overwrite the first setting and disable
3000    Enhanced Pointer Precision and change mouse speed.
3001    There are some measures in place to try to mitigate that risk, such as an
3002    internal counter that only allows storing and restoring the acceleration
3003    settings as long as no other movement is currently in progress.
3004    Additionally, the acceleration settings can be manually saved and
3005    restored with `store_mouse_acceleration_settings()` and
3006    `restore_mouse_acceleration_settings()`. For your convenience, the
3007    store function is automatically called during import to save your current
3008    setting. You can then call the restore function at any time.
3009
3010    If all fails, the setting is not written permanently to your Windows
3011    settings, so it should restore itself upon reboot.
3012
3013    Bottom line: Don't use the `disable_mouse_acceleration` argument if you use
3014    this library in multiple threads / processes / programs at the same time!
3015
3016    ----------------------------------------------------------------------------
3017
3018    NOTE: `logScreenshot` is currently unsupported.
3019    """
3020    # TODO: bounding box check for valid position
3021    if tween is None:
3022        tween = _linear
3023    if path_function is None:
3024        path_function = _bresenham
3025    final_x: int
3026    final_y: int
3027    current_x: int = 0
3028    current_y: int = 0
3029    current_x, current_y = position()
3030    if target_coords_relative:
3031        final_x = current_x + (0 if x is None else x)
3032        final_y = current_y + (0 if y is None else y)
3033    else:
3034        # if only x or y is provided, will keep the current position for the
3035        # other axis
3036        final_x, final_y = position(x, y)
3037
3038    dwFlags: int = _MOUSEEVENTF_MOVE | _MOUSEEVENTF_ABSOLUTE
3039    if virtual:
3040        dwFlags |= _MOUSEEVENTF_VIRTUALDESK
3041
3042    if duration <= 0.0:
3043        # no duration -> move mouse instantly
3044        x, y = _to_windows_coordinates(final_x, final_y, virtual=virtual)
3045        _send_input(_create_mouse_input(dx=x, dy=y, dwFlags=dwFlags))
3046
3047    else:
3048        start_time: Final[float] = _time()
3049        final_time: Final[float] = start_time + duration
3050        path: list[tuple[int, int]] = path_function(
3051            current_x, current_y, final_x, final_y
3052        )
3053        path_length = len(path)
3054        keep_looping: bool = True
3055        sleep_duration: float = MINIMUM_SLEEP_IDEAL
3056
3057        apply_duration: bool = False
3058        while keep_looping:
3059            if apply_duration:
3060                _sleep(sleep_duration)  # sleep between iterations
3061            else:
3062                apply_duration = True
3063
3064            _failSafeCheck()
3065
3066            current_time = _time()
3067            if current_time >= final_time:
3068                keep_looping = False
3069                segment_count = path_length - 1
3070            else:
3071                time_ratio = (current_time - start_time) / duration
3072                if time_ratio <= 0.0:
3073                    time_ratio = 0.0
3074                if time_ratio >= 1.0:
3075                    time_ratio = 1.0
3076                path_ratio = tween(time_ratio)
3077                segment_count = int(path_length * path_ratio)
3078                if segment_count >= path_length:
3079                    segment_count = path_length - 1
3080
3081            current_x, current_y = position()
3082            x, y = path[segment_count]
3083
3084            if x == current_x and y == current_y:
3085                # no change in movement for current segment ->try again
3086                continue
3087
3088            x, y = _to_windows_coordinates(x, y, virtual=virtual)
3089            _send_input(_create_mouse_input(dx=x, dy=y, dwFlags=dwFlags))
3090
3091    # After-care: Did Windows move the cursor correctly?
3092    # If not, attempt to fix off-by-one errors.
3093    if attempt_pixel_perfect:
3094        current_x, current_y = position()
3095        if current_x == final_x and current_y == final_y:
3096            return  # We are already pixel perfect, great!
3097        _relative_mouse_move(
3098            x=final_x - current_x,
3099            y=final_y - current_y,
3100            duration=0.0,
3101            target_coords_relative=True,
3102            virtual=virtual,
3103            disable_mouse_acceleration=disable_mouse_acceleration,
3104        )
3105    # --------------------------------------------------------------------------
3106
3107
3108# ----- relative_mouse_move ----------------------------------------------------
3109def _relative_mouse_move(
3110    x: int | None = None,
3111    y: int | None = None,
3112    duration: float = 0.0,
3113    tween: Callable[[float], float] | None = None,
3114    logScreenshot: bool = False,
3115    target_coords_relative: bool = True,
3116    *,
3117    virtual: bool = False,
3118    path_function: PathFunction | None = None,
3119    disable_mouse_acceleration: bool = False,
3120) -> None:
3121    """
3122    Move the mouse a relative amount determined by `x` and `y`.
3123
3124    If `duration` is floating point number greater than 0, then this function
3125    will automatically split the movement into microsteps instead of moving the
3126    complete distance instantly.
3127
3128    `tween` is a function that takes a floating point number between 0.0 and
3129    1.0 and returns another floating point number between 0.0 and 1.0. The
3130    returned number will be used to calculate the next position of the
3131    mouse between the start and the end position based on the current duration.
3132    The default tweening function is linear, which will move the mouse at a
3133    constant speed. See the `pytweening` package for more tweening functions.
3134
3135    `path_function` is a function that takes the start and end coordinates of
3136    the mouse movement (4 integers) and returns a list of coordinates (list of
3137    tuples containting 2 integers each) that the mouse will move through.
3138    The default path function is Bresenham's line algorithm, which will move
3139    the mouse in a straight line.
3140
3141    `target_coords_relative` parameter decides how `x` and `y` are interpreted:
3142
3143    -> `False`: `x` and `y` are assumed to be absolute coordinates
3144    and the offset to move will be calculated based on the current mouse
3145    position. Movement is then performed using relative mouse movements
3146    (can be inconsistent).
3147
3148    -> `True`: `x` and `y` are assumed to be relative coordinates
3149    and the mouse will be moved by that amount. Movement is then performed
3150    using relative mouse movements (can be inconsistent).
3151
3152    The inconsistency issue can be solved by disabling Enhanced Pointer
3153    Precision and set Mouse speed to 10 in Windows mouse settings. Since users
3154    may not want to permanently change their input settings just for this
3155    library, the `disable_mouse_acceleration` argument can be used to
3156    temporarily disable Enhanced Pointer Precision and fix mouse speed at 10
3157    and restore it after the mouse movement.
3158
3159    If `_pause` is True (default), then an automatic sleep will be performed
3160    after the function finshes executing. The duration is set by the global
3161    variable `PAUSE`.
3162
3163    Setting `virtual` to True (default: False) changes the way internal APIs
3164    handle coordinates and is intended for multi monitor systems. It should be
3165    pretty much unncessary even for multi monitor systems, since all the
3166    necessary internal calculations beyond the border of the primay monitor
3167    work without it.
3168
3169    ----------------------------------------------------------------------------
3170    Careful! Disabling mouse acceleration settings is MAYBE thread-safe,
3171    NOT multiprocessing-safe, and DEFINITELY NOT independent processses safe!
3172
3173    If you you start a relative movement while another is already in progress
3174    than the second movement could overwrite the first setting and disable
3175    Enhanced Pointer Precision and change mouse speed.
3176    There are some measures in place to try to mitigate that risk, such as an
3177    internal counter that only allows storing and restoring the acceleration
3178    settings as long as no other movement is currently in progress.
3179    Additionally, the acceleration settings can be manually saved and
3180    restored with `store_mouse_acceleration_settings()` and
3181    `restore_mouse_acceleration_settings()`. For your convinnience, the
3182    store function is automatically called during import to save your current
3183    setting. You can then call the restore function at any time.
3184
3185    If all fails, the setting is not written permanently to your Windows
3186    settings, so it should restore itself upon reboot.
3187
3188    Bottom line: Don't use the `disable_mouse_acceleration` argument if you use
3189    this library in multiple threads / processes / programs at the same time!
3190
3191    ----------------------------------------------------------------------------
3192
3193    NOTE: `logScreenshot` is are currently unsupported.
3194    """
3195    # TODO: bounding box check for valid position
3196    if tween is None:
3197        tween = _linear
3198    if path_function is None:
3199        path_function = _bresenham
3200    x, y = _helper_relative_move_target_coords(x, y, target_coords_relative)
3201
3202    if duration <= 0.0:
3203        # no duration -> move mouse instantly
3204        _helper_relative_mouse_move(x, y, disable_mouse_acceleration)
3205        return
3206
3207    current_x: int = 0
3208    current_y: int = 0
3209
3210    next_x: int
3211    next_y: int
3212
3213    final_x: int = x
3214    final_y: int = y
3215
3216    start_time: Final[float] = _time()
3217    final_time: Final[float] = start_time + duration
3218
3219    path: list[tuple[int, int]] = path_function(
3220        current_x, current_y, final_x, final_y
3221    )
3222    path_length: int = len(path)
3223
3224    keep_looping: bool = True
3225    sleep_duration: float = MINIMUM_SLEEP_IDEAL
3226
3227    apply_duration: bool = False
3228    while keep_looping:
3229        if apply_duration:
3230            _sleep(sleep_duration)  # sleep between iterations
3231        else:
3232            apply_duration = True
3233
3234        _failSafeCheck()
3235
3236        current_time: float = _time()
3237        if current_time >= final_time:
3238            keep_looping = False
3239            segment_count: int = path_length - 1
3240        else:
3241            time_ratio: float = (current_time - start_time) / duration
3242            if time_ratio <= 0.0:
3243                time_ratio = 0.0
3244            if time_ratio >= 1.0:
3245                time_ratio = 1.0
3246            path_ratio: float = tween(time_ratio)
3247            segment_count = int(path_length * path_ratio)
3248            if segment_count >= path_length:
3249                segment_count = path_length - 1
3250
3251        next_x, next_y = path[segment_count]
3252        x = next_x - current_x
3253        y = next_y - current_y
3254
3255        if x == 0 and y == 0:
3256            # no change in movement for current segment ->try again
3257            continue
3258
3259        current_x = next_x
3260        current_y = next_y
3261
3262        _helper_relative_mouse_move(x, y, disable_mouse_acceleration)
3263    # --------------------------------------------------------------------------
3264
3265
3266# ----- helper_relative_move_target_coords -------------------------------------
3267def _helper_relative_move_target_coords(
3268    x: int | None,
3269    y: int | None,
3270    target_coords_relative: bool
3271) -> tuple[int, int]:
3272    """
3273    Calculate target coordinates for relative mouse movement.
3274    """
3275    if target_coords_relative:
3276        if x is None:
3277            x = 0
3278        if y is None:
3279            y = 0
3280    else:
3281        if x is None and y is None:
3282            # Prevent unnecessary API calls
3283            return 0, 0
3284        current_x: int
3285        current_y: int
3286        current_x, current_y = position()
3287        if x is None:
3288            x = 0
3289        else:
3290            x = x - current_x
3291        if y is None:
3292            y = 0
3293        else:
3294            y = y - current_y
3295    return x, y
3296    # --------------------------------------------------------------------------
3297
3298
3299# ----- helper_relative_mouse_move ---------------------------------------------
3300def _helper_relative_mouse_move(
3301    x: int,
3302    y: int,
3303    disable_mouse_acceleration: bool
3304) -> None:
3305    """
3306    When using MOUSEEVENTF_MOVE for relative movement the results may
3307    be inconsistent. "Relative mouse motion is subject to the effects
3308    of the mouse speed and the two-mouse threshold values. A user
3309    sets these three values with the Pointer Speed slider of the
3310    Control Panel's Mouse Properties sheet. You can obtain and set
3311    these values using the SystemParametersInfo function."
3312
3313    https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput
3314    https://stackoverflow.com/questions/50601200/pyhon-directinput-mouse-relative-moving-act-not-as-expected
3315
3316    We can solve this issue by just disabling Enhanced Pointer
3317    Precision and forcing Mouse speed to neutral 10.
3318    Since that is a user setting that users may want to have
3319    enabled, use a optional keyword-only argument and a
3320    state-restoring context manager to give users the choice if they
3321    want this library messing around in their Windows settings.
3322    """
3323    input_struct: _INPUT = _create_mouse_input(
3324        dx=x, dy=y, dwFlags=_MOUSEEVENTF_MOVE
3325    )
3326    if disable_mouse_acceleration:
3327        # Use a context manager to temporarily disable enhanced pointer
3328        # precision
3329        with _no_mouse_acceleration():
3330            _send_input(input_struct)
3331    else:
3332        _send_input(input_struct)
3333    # --------------------------------------------------------------------------
3334
3335
3336# ----- moveTo -----------------------------------------------------------------
3337@_genericPyDirectInputChecks
3338def moveTo(
3339    x: int | None = None,
3340    y: int | None = None,
3341    duration: float = 0.0,
3342    tween: Callable[[float], float] | None = None,
3343    logScreenshot: bool = False,
3344    _pause: bool = True,
3345    relative: bool = False,
3346    *,
3347    virtual: bool = False,
3348    path_function: PathFunction | None = None,
3349    attempt_pixel_perfect: bool = False,
3350    disable_mouse_acceleration: bool = False,
3351) -> None:
3352    """
3353    Move the mouse to an absolute(*) postion indicated by the arguments of
3354    `x` and `y`. The coordinates 0,0 represent the top left pixel of the
3355    primary monitor.
3356
3357    If `duration` is floating point number greater than 0, then this function
3358    will automatically split the movement into microsteps instead of moving
3359    straight to the target position.
3360
3361    `tween` is a function that takes a floating point number between 0.0 and
3362    1.0 and returns another floating point number between 0.0 and 1.0. The
3363    returned number will be used to calculate the next position of the
3364    mouse between the start and the end position based on the current duration.
3365    The default tweening function is linear, which will move the mouse at a
3366    constant speed. See the `pytweening` package for more tweening functions.
3367
3368    `path_function` is a function that takes the start and end coordinates of
3369    the mouse movement (4 integers) and returns a list of coordinates (list of
3370    tuples containting 2 integers each) that the mouse will move through.
3371    The default path function is Bresenham's line algorithm, which will move
3372    the mouse in a straight line.
3373
3374    (*) `relative` parameter decides how the movement is executed:
3375
3376    -> `False`: Target postion is given and absolute movement is used.
3377
3378    -> `True`: Calculates target offset and uses relative movement API
3379    (can be inconsistent)
3380
3381    If `_pause` is True (default), then an automatic sleep will be performed
3382    after the function finshes executing. The duration is set by the global
3383    variable `PAUSE`.
3384
3385    Setting `virtual` to True (default: False) changes the way internal APIs
3386    handle coordinates and is intended for multi monitor systems. It should be
3387    pretty much unncessary even for multi monitor systems, since all the
3388    necessary internal calculations beyond the border of the primay monitor
3389    work without it.
3390
3391    The way that Windows calculates the target pixel coordinates internally
3392    unfortunately leads to inaccuracies and unreachable pixels, especially
3393    if the `virtual` option is used.
3394
3395    If you need the target position to be pixel perfect, you can try setting
3396    `attempt_pixel_perfect` to True, which will use tiny relative movements
3397    to correct the unreachable position.
3398
3399    Relative movement is influenced by mouse speed and Windows Enhanced Pointer
3400    Precision, which can be temporarily disabled by setting
3401    `disable_mouse_acceleration`.
3402
3403    ----------------------------------------------------------------------------
3404    Careful! Disabling mouse acceleration settings is MAYBE thread-safe,
3405    NOT multiprocessing-safe, and DEFINITELY NOT independent processses safe!
3406
3407    If you you start a relative movement while another is already in progress
3408    than the second movement could overwrite the first setting and disable
3409    Enhanced Pointer Precision and change mouse speed.
3410    There are some measures in place to try to mitigate that risk, such as an
3411    internal counter that only allows storing and restoring the acceleration
3412    settings as long as no other movement is currently in progress.
3413    Additionally, the acceleration settings can be manually saved and
3414    restored with `store_mouse_acceleration_settings()` and
3415    `restore_mouse_acceleration_settings()`. For your convenience, the
3416    store function is automatically called during import to save your current
3417    setting. You can then call the restore function at any time.
3418
3419    If all fails, the setting is not written permanently to your Windows
3420    settings, so it should restore itself upon reboot.
3421
3422    Bottom line: Don't use the `disable_mouse_acceleration` argument if you use
3423    this library in multiple threads / processes / programs at the same time!
3424
3425    ----------------------------------------------------------------------------
3426
3427    NOTE: `logScreenshot` is currently unsupported.
3428    """
3429    if relative:
3430        _relative_mouse_move(
3431            x=x,
3432            y=y,
3433            duration=duration,
3434            tween=tween,
3435            logScreenshot=logScreenshot,
3436            target_coords_relative=False,
3437            virtual=virtual,
3438            path_function=path_function,
3439            disable_mouse_acceleration=disable_mouse_acceleration,
3440        )
3441    else:
3442        _absolute_mouse_move(
3443            x=x,
3444            y=y,
3445            duration=duration,
3446            tween=tween,
3447            logScreenshot=logScreenshot,
3448            target_coords_relative=False,
3449            virtual=virtual,
3450            path_function=path_function,
3451            attempt_pixel_perfect=attempt_pixel_perfect,
3452            disable_mouse_acceleration=disable_mouse_acceleration,
3453        )
3454    # --------------------------------------------------------------------------
3455
3456
3457# ----- moveRel ----------------------------------------------------------------
3458@_genericPyDirectInputChecks
3459def moveRel(
3460    xOffset: int | None = None,
3461    yOffset: int | None = None,
3462    duration: float = 0.0,
3463    tween: Callable[[float], float] | None = None,
3464    logScreenshot: bool = False,
3465    _pause: bool = True,
3466    relative: bool = False,
3467    *,
3468    virtual: bool = False,
3469    path_function: PathFunction | None = None,
3470    attempt_pixel_perfect: bool = False,
3471    disable_mouse_acceleration: bool = False,
3472) -> None:
3473    """
3474    Move the mouse a relative amount determined by `xOffset` and `yOffset`.
3475
3476    If `duration` is floating point number greater than 0, then this function
3477    will automatically split the movement into microsteps instead of moving the
3478    complete distance instantly.
3479
3480    `tween` is a function that takes a floating point number between 0.0 and
3481    1.0 and returns another floating point number between 0.0 and 1.0. The
3482    returned number will be used to calculate the next position of the
3483    mouse between the start and the end position based on the current duration.
3484    The default tweening function is linear, which will move the mouse at a
3485    constant speed. See the `pytweening` package for more tweening functions.
3486
3487    `path_function` is a function that takes the start and end coordinates of
3488    the mouse movement (4 integers) and returns a list of coordinates (list of
3489    tuples containting 2 integers each) that the mouse will move through.
3490    The default path function is Bresenham's line algorithm, which will move
3491    the mouse in a straight line.
3492
3493    `relative` parameter decides how the movement is executed:
3494
3495    -> `False`: Target postion is calculated and absolute movement is used.
3496
3497    -> `True`: Target offset is given and relative movement API is used
3498    (can be inconsistent)
3499
3500    The inconsistency issue can be solved by disabling Enhanced Pointer
3501    Precision and set Mouse speed to 10 in Windows mouse settings. Since users
3502    may not want to permanently change their input settings just for this
3503    library, the `disable_mouse_acceleration` argument can be used to
3504    temporarily disable Enhanced Pointer Precision and fix mouse speed at 10
3505    and restore it after the mouse movement.
3506
3507    If `_pause` is True (default), then an automatic sleep will be performed
3508    after the function finshes executing. The duration is set by the global
3509    variable `PAUSE`.
3510
3511    Setting `virtual` to True (default: False) changes the way internal APIs
3512    handle coordinates and is intended for multi monitor systems. It should be
3513    pretty much unncessary even for multi monitor systems, since all the
3514    necessary internal calculations beyond the border of the primay monitor
3515    work without it.
3516
3517    The way that Windows calculates the target pixel coordinates internally
3518    unfortunately leads to inaccuracies and unreachable pixels, especially
3519    if the `virtual` option is used.
3520
3521    If you need the target position to be pixel perfect, you can try setting
3522    `attempt_pixel_perfect` to True, which will use tiny relative movements
3523    to correct the unreachable position.
3524
3525    ----------------------------------------------------------------------------
3526    Careful! Disabling mouse acceleration settings is MAYBE thread-safe,
3527    NOT multiprocessing-safe, and DEFINITELY NOT independent processses safe!
3528
3529    If you you start a relative movement while another is already in progress
3530    than the second movement could overwrite the first setting and disable
3531    Enhanced Pointer Precision and change mouse speed.
3532    There are some measures in place to try to mitigate that risk, such as an
3533    internal counter that only allows storing and restoring the acceleration
3534    settings as long as no other movement is currently in progress.
3535    Additionally, the acceleration settings can be manually saved and
3536    restored with `store_mouse_acceleration_settings()` and
3537    `restore_mouse_acceleration_settings()`. For your convinnience, the
3538    store function is automatically called during import to save your current
3539    setting. You can then call the restore function at any time.
3540
3541    If all fails, the setting is not written permanently to your Windows
3542    settings, so it should restore itself upon reboot.
3543
3544    Bottom line: Don't use the `disable_mouse_acceleration` argument if you use
3545    this library in multiple threads / processes / programs at the same time!
3546
3547    ----------------------------------------------------------------------------
3548
3549    NOTE: `logScreenshot` is are currently unsupported.
3550    """
3551    if relative:
3552        _relative_mouse_move(
3553            x=xOffset,
3554            y=yOffset,
3555            duration=duration,
3556            tween=tween,
3557            logScreenshot=logScreenshot,
3558            target_coords_relative=True,
3559            virtual=virtual,
3560            path_function=path_function,
3561            disable_mouse_acceleration=disable_mouse_acceleration,
3562        )
3563    else:
3564        _absolute_mouse_move(
3565            x=xOffset,
3566            y=yOffset,
3567            duration=duration,
3568            tween=tween,
3569            logScreenshot=logScreenshot,
3570            target_coords_relative=True,
3571            virtual=virtual,
3572            path_function=path_function,
3573            attempt_pixel_perfect=attempt_pixel_perfect,
3574            disable_mouse_acceleration=disable_mouse_acceleration,
3575        )
3576    # --------------------------------------------------------------------------
3577
3578
3579# ----- move alias -------------------------------------------------------------
3580# move() and moveRel() are equivalent.
3581move = moveRel
3582# ------------------------------------------------------------------------------
3583
3584
3585# ----- dragTo -----------------------------------------------------------------
3586@_genericPyDirectInputChecks
3587def dragTo(
3588    x: int | None = None,
3589    y: int | None = None,
3590    duration: float = 0.0,
3591    tween: Callable[[float], float] | None = None,
3592    button: str | None = None,
3593    logScreenshot: bool = False,
3594    _pause: bool = True,
3595    mouseDownUp: bool = True,
3596    *,
3597    relative: bool = False,
3598    virtual: bool = False,
3599    path_function: PathFunction | None = None,
3600    attempt_pixel_perfect: bool = False,
3601    disable_mouse_acceleration: bool = False,
3602) -> None:
3603    """
3604    Press and hold a mouse button while moving to the target coordinates.
3605
3606    See `moveTo` for more information on most arguments.
3607
3608    `button` is a string that is one of the following constants:
3609    MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE, MOUSE_BUTTON4, MOUSE_BUTTON5,
3610    MOUSE_PRIMARY (default), MOUSE_SECONDARY.
3611
3612    If `mouseDownUp` (default: True) is manually set to False, then this
3613    function is basically the same as `moveTo`. Only exists to match PyAutoGUI.
3614
3615    If `_pause` is True (default), then an automatic sleep will be performed
3616    after the function finshes executing. The duration is set by the global
3617    variable `PAUSE`.
3618
3619    ----------------------------------------------------------------------------
3620
3621    NOTE: `logScreenshot` is currently unsupported.
3622    """
3623    # TODO: bounding box check for valid position
3624    if button is None:
3625        button = MOUSE_PRIMARY
3626    if mouseDownUp:
3627        mouseDown(button=button, _pause=False, virtual=virtual)
3628    moveTo(
3629        x,
3630        y,
3631        duration=duration,
3632        tween=tween,
3633        logScreenshot=logScreenshot,
3634        _pause=False,  # don't add an additional pause
3635        relative=relative,
3636        virtual=virtual,
3637        path_function=path_function,
3638        attempt_pixel_perfect=attempt_pixel_perfect,
3639        disable_mouse_acceleration=disable_mouse_acceleration,
3640    )
3641    if mouseDownUp:
3642        mouseUp(button=button, _pause=False, virtual=virtual)
3643    # --------------------------------------------------------------------------
3644
3645
3646# ----- dragRel ----------------------------------------------------------------
3647@_genericPyDirectInputChecks
3648def dragRel(
3649    xOffset: int | None = None,
3650    yOffset: int | None = None,
3651    duration: float = 0.0,
3652    tween: Callable[[float], float] | None = None,
3653    button: str | None = None,
3654    logScreenshot: bool = False,
3655    _pause: bool = True,
3656    mouseDownUp: bool = True,
3657    *,
3658    relative: bool = False,
3659    virtual: bool = False,
3660    path_function: PathFunction | None = None,
3661    disable_mouse_acceleration: bool = False,
3662) -> None:
3663    """
3664    Press and hold a mouse button while moving a relative distance
3665
3666    See `moveRel` for more information on most arguments.
3667
3668    `button` is a string that is one of the following constants:
3669    MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE, MOUSE_BUTTON4, MOUSE_BUTTON5,
3670    MOUSE_PRIMARY (default), MOUSE_SECONDARY.
3671
3672    If `mouseDownUp` (default: True) is manually set to False, then this
3673    function is basically the same as `moveTo`. Only exists to match PyAutoGUI.
3674
3675    If `_pause` is True (default), then an automatic sleep will be performed
3676    after the function finshes executing. The duration is set by the global
3677    variable `PAUSE`.
3678
3679    ----------------------------------------------------------------------------
3680
3681    NOTE: `logScreenshot` is currently unsupported.
3682    """
3683    # TODO: bounding box check for valid position
3684    if button is None:
3685        button = MOUSE_PRIMARY
3686    if mouseDownUp:
3687        mouseDown(button=button, _pause=False, virtual=virtual)
3688    moveRel(
3689        xOffset,
3690        yOffset,
3691        duration=duration,
3692        tween=tween,
3693        logScreenshot=logScreenshot,
3694        _pause=False,  # don't add an additional pause
3695        relative=relative,
3696        virtual=virtual,
3697        path_function=path_function,
3698        disable_mouse_acceleration=disable_mouse_acceleration,
3699    )
3700    if mouseDownUp:
3701        mouseUp(button=button, _pause=False, virtual=virtual)
3702    # --------------------------------------------------------------------------
3703
3704
3705# ----- drag alias -------------------------------------------------------------
3706drag = dragRel
3707# ------------------------------------------------------------------------------
3708
3709
3710# ==============================================================================
3711# ===== Keyboard Functions =====================================================
3712# ==============================================================================
3713
3714
3715# ----- is_valid_key -------------------------------------------------------------
3716def is_valid_key(key: str) -> bool:
3717    """
3718    Returns true if key name `key` can be translated into a valid scan code.
3719    """
3720    return key in KEYBOARD_MAPPING
3721
3722
3723isValidKey = is_valid_key
3724# ------------------------------------------------------------------------------
3725
3726
3727# ===== scancode functions =====================================================
3728
3729
3730# ----- scancode_keyDown -------------------------------------------------------
3731@_genericPyDirectInputChecks
3732def scancode_keyDown(
3733    scancodes: ScancodeTypes,
3734    logScreenshot: None = None,
3735    _pause: bool = True,
3736    *,
3737    auto_shift: bool = False,
3738) -> bool:
3739    """
3740    Press down key corresponding to `scancodes`.
3741
3742    The actually pressed key will depend on your system keyboard layout.
3743    Limits the available character set but should provide the best
3744    compatibility.
3745
3746    If `_pause` is True (default), then an automatic sleep will be performed
3747    after the function finshes executing. The duration is set by the global
3748    variable `PAUSE`.
3749
3750    `auto_shift` is used internally by higher level functions to automatically
3751    press the shift key before supported scancodes (indicitated by a special
3752    bit outside the regular scancode range, while it technically can be used,
3753    it's not intended for public access).
3754
3755    ----------------------------------------------------------------------------
3756
3757    NOTE: `logScreenshot` is currently unsupported.
3758    """
3759    scancodes_sequence: ScancodeSequence
3760    if isinstance(scancodes, int):
3761        scancodes_sequence = ScancodeSequence([scancodes])
3762    else:
3763        scancodes_sequence = scancodes
3764
3765    keybdFlags: int = _KEYEVENTF_SCANCODE
3766    input_structs: list[_INPUT] = []
3767    extendedFlag: int
3768
3769    # Init event tracking
3770    insertedEvents: int = 0
3771    expectedEvents: int = 0
3772
3773    for scancode in scancodes_sequence:
3774        if auto_shift and scancode & _OFFSET_SHIFTKEY:
3775            input_structs += [
3776                _create_keyboard_input(
3777                    wScan=_SHIFT_SCANCODE, dwFlags=keybdFlags
3778                )
3779            ]
3780            expectedEvents += 1
3781
3782        scancode = scancode & 0xFFFF
3783
3784        extendedFlag = _KEYEVENTF_EXTENDEDKEY if scancode >= 0xE000 else 0
3785        input_structs += [
3786            _create_keyboard_input(
3787                wScan=scancode, dwFlags=keybdFlags | extendedFlag
3788            )
3789        ]
3790        expectedEvents += 1
3791
3792    insertedEvents += _send_input(input_structs)
3793
3794    # SendInput returns the number of event successfully inserted into
3795    # input stream
3796    # https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput#return-value
3797    return insertedEvents == expectedEvents
3798    # --------------------------------------------------------------------------
3799
3800
3801# ----- scancode_keyUp ---------------------------------------------------------
3802@_genericPyDirectInputChecks
3803def scancode_keyUp(
3804    scancodes: ScancodeTypes,
3805    logScreenshot: None = None,
3806    _pause: bool = True,
3807    *,
3808    auto_shift: bool = False,
3809) -> bool:
3810    """
3811    Release key corresponding to `scancodes`.
3812
3813    The actually pressed key will depend on your system keyboard layout.
3814    Limits the available character set but should provide the best
3815    compatibility.
3816
3817    If `_pause` is True (default), then an automatic sleep will be performed
3818    after the function finshes executing. The duration is set by the global
3819    variable `PAUSE`.
3820
3821    `auto_shift` is used internally by higher level functions to automatically
3822    press the shift key before supported scancodes (indicitated by a special
3823    bit outside the regular scancode range, while it technically can be used,
3824    it's not intended for public access).
3825
3826    ----------------------------------------------------------------------------
3827
3828    NOTE: `logScreenshot` is currently unsupported.
3829    """
3830    scancodes_sequence: ScancodeSequence
3831    if isinstance(scancodes, int):
3832        scancodes_sequence = ScancodeSequence([scancodes])
3833    else:
3834        scancodes_sequence = scancodes
3835
3836    keybdFlags: int = _KEYEVENTF_SCANCODE | _KEYEVENTF_KEYUP
3837    input_structs: list[_INPUT] = []
3838    extendedFlag: int
3839
3840    # Init event tracking
3841    insertedEvents: int = 0
3842    expectedEvents: int = 0
3843
3844    for scancode in scancodes_sequence:
3845        if auto_shift and scancode & _OFFSET_SHIFTKEY:
3846            input_structs += [
3847                _create_keyboard_input(
3848                    wScan=_SHIFT_SCANCODE, dwFlags=keybdFlags
3849                )
3850            ]
3851            expectedEvents += 1
3852
3853        scancode = scancode & 0xFFFF
3854
3855        extendedFlag = _KEYEVENTF_EXTENDEDKEY if scancode >= 0xE000 else 0
3856        input_structs += [
3857            _create_keyboard_input(
3858                wScan=scancode & 0xFFFF, dwFlags=keybdFlags | extendedFlag
3859            )
3860        ]
3861        expectedEvents += 1
3862
3863    insertedEvents += _send_input(input_structs)
3864    return insertedEvents == expectedEvents
3865    # --------------------------------------------------------------------------
3866
3867
3868# ----- _helper_scancode_press -------------------------------------------------
3869def _helper_scancode_press(
3870    scancodes: ScancodeTypes,
3871    duration: float = 0.0,
3872    _pause: bool = True,
3873    auto_shift: bool = False,
3874) -> bool:
3875    """
3876    Press `scancode`, wait for `duration` seconds, release `scancode`.
3877
3878    Return `True` if complete press was successful.
3879    """
3880    downed: bool = scancode_keyDown(
3881        scancodes, _pause=_pause, auto_shift=auto_shift
3882    )
3883    _sleep(duration)
3884    upped: bool = scancode_keyUp(
3885        scancodes, _pause=_pause, auto_shift=auto_shift
3886    )
3887    # Count key press as complete if key was "downed" and "upped"
3888    # successfully
3889    return bool(downed and upped)
3890    # --------------------------------------------------------------------------
3891
3892
3893# ----- scancode_press ---------------------------------------------------------
3894# Ignored parameters: logScreenshot
3895@_genericPyDirectInputChecks
3896def scancode_press(
3897    scancodes: ScancodeTypes | Sequence[ScancodeTypes],
3898    presses: int = 1,
3899    interval: float = 0.0,
3900    logScreenshot: None = None,
3901    _pause: bool = True,
3902    *,
3903    auto_shift: bool = False,
3904    delay: float = 0.0,
3905    duration: float = 0.0,
3906) -> bool:
3907    """
3908    Press the sequence of `keys` for `presses` amount of times.
3909
3910    The actually pressed key will depend on your system keyboard layout.
3911    Limits the available character set but should provide the best
3912    compatibility.
3913
3914    Explanation of time parameters (seconds as floating point numbers):
3915
3916    - `interval` is the time spent waiting between sequences. If `keys` is a
3917    str instance or single element list, then `interval` will be ignored.
3918    - `delay` is the time from one complete key (press+release) to the next one
3919    in the same sequence. If there is only a single key in a sequence, then
3920    `delay` will be ignored.
3921    - `duration` is the time spent on holding every key before releasing it
3922    again.
3923
3924    If `_pause` is True (default), then an automatic sleep will be performed
3925    after the function finshes executing. The duration is set by the global
3926    variable `PAUSE`.
3927    Be aware, that the global pause defined by the PAUSE `constant` only
3928    applies after every call to this function, not inbetween (no extra pause
3929    between pressing and releasing key, use the `duration` argument instead)!
3930
3931    `auto_shift` is used internally by higher level functions to automatically
3932    press the shift key before supported scancodes (indicitated by a special
3933    bit outside the regular scancode range, while it technically can be used,
3934    it's not intended for public access).
3935
3936    ----------------------------------------------------------------------------
3937
3938    NOTE: `logScreenshot` is currently unsupported.
3939    """
3940    scancodes_sequence: Sequence[ScancodeTypes]
3941    if isinstance(scancodes, int):
3942        scancodes_sequence = [ScancodeSequence([scancodes])]
3943    elif isinstance(scancodes, ScancodeSequence):
3944        scancodes_sequence = [scancodes]
3945    else:
3946        scancodes_sequence = scancodes
3947
3948    # We need to press x keys y times, which comes out to x*y presses in total
3949    expectedPresses: int = presses * len(scancodes_sequence)
3950    completedPresses: int = 0
3951
3952    apply_interval: bool = False
3953    for _ in range(presses):
3954        if apply_interval:  # Don't delay first press
3955            _sleep(interval)
3956        apply_interval = True
3957
3958        apply_delay: bool = False
3959        for c in scancodes_sequence:
3960            if apply_delay:  # Don't delay first press
3961                _sleep(delay)
3962            apply_delay = True
3963
3964            completedPresses += _helper_scancode_press(
3965                c, duration, _pause=False, auto_shift=auto_shift
3966            )
3967
3968    return completedPresses == expectedPresses
3969    # --------------------------------------------------------------------------
3970
3971
3972# ----- scancode_hold ----------------------------------------------------------
3973@contextmanager
3974@_genericPyDirectInputChecks
3975def scancode_hold(
3976    scancodes: ScancodeTypes | Sequence[ScancodeTypes],
3977    logScreenshot: None = None,
3978    _pause: bool = True,
3979    *,
3980    auto_shift: bool = False,
3981    raise_on_failure: bool = False,
3982) -> Generator[None, None, None]:
3983    """
3984    Hold the sequence of keys corresponding to `scancodes` as long as the
3985    context manager is in scope (press upon entry, release upon exit).
3986
3987    Keys will be released in reverse order (LIFO), but still practically
3988    instantenous.
3989
3990    The actually pressed key will depend on your system keyboard layout.
3991    Limits the available character set but should provide the best
3992    compatibility.
3993
3994    If `_pause` is True (default), then an automatic sleep will be performed
3995    after the function finshes executing. The duration is set by the global
3996    variable `PAUSE`.
3997    Be aware, that the global pause defined by the PAUSE `constant` only
3998    applies after every call to this function, not inbetween (no pause between
3999    press and releasing key)!
4000
4001    `auto_shift` is used internally by higher level functions to automatically
4002    press the shift key before supported scancodes (indicitated by a special
4003    bit outside the regular scancode range, while it technically can be used,
4004    it's not intended for public access).
4005
4006    If `raise_on_failure` is True, then `PriorInputFailedException` will be
4007    raised if not all keyboard inputs could be executed successfully.
4008
4009    ----------------------------------------------------------------------------
4010
4011    NOTE: `logScreenshot` is currently unsupported.
4012    """
4013    scancodes_sequence: Sequence[ScancodeTypes]
4014    if isinstance(scancodes, int):
4015        scancodes_sequence = [ScancodeSequence([scancodes])]
4016    elif isinstance(scancodes, ScancodeSequence):
4017        scancodes_sequence = [scancodes]
4018    else:
4019        scancodes_sequence = scancodes
4020
4021    expectedPresses: int = len(scancodes_sequence)
4022    downed: int = 0
4023    upped: int = 0
4024
4025    try:
4026        for c in scancodes_sequence:
4027            downed += scancode_keyDown(c, _pause=False, auto_shift=auto_shift)
4028        yield
4029    finally:
4030        for c in reversed(scancodes_sequence):
4031            upped += scancode_keyUp(c, _pause=False, auto_shift=auto_shift)
4032        if raise_on_failure and not (expectedPresses == downed == upped):
4033            raise PriorInputFailedException
4034    # --------------------------------------------------------------------------
4035
4036
4037# ----- scancode_hotkey --------------------------------------------------------
4038@_genericPyDirectInputChecks
4039def scancode_hotkey(
4040    *args: ScancodeTypes,
4041    interval: float = 0.0,
4042    wait: float = 0.0,
4043    logScreenshot: None = None,
4044    _pause: bool = True,
4045    auto_shift: bool = True,
4046) -> bool:
4047    """
4048    Press down buttons in order they are specified as arguments,
4049    releasing them in reverse order, e.g. 0x1D, 0x2E will first press
4050    Control, then C and release C before releasing Control.
4051
4052    Use keyword-only argument `interval` to specify a delay between single
4053    keys when pressing and releasing and `wait` for delay between last press
4054    and first release.
4055
4056    If `_pause` is True (default), then an automatic sleep will be performed
4057    after the function finshes executing. The duration is set by the global
4058    variable `PAUSE`.
4059    Be aware, that the global pause defined by the PAUSE `constant` only
4060    applies after every call to this function, not inbetween (no pause between
4061    press and releasing key)!
4062
4063    `auto_shift` is used internally by higher level functions to automatically
4064    press the shift key before supported scancodes (indicitated by a special
4065    bit outside the regular scancode range, while it technically can be used,
4066    it's not intended for public access).
4067
4068    ----------------------------------------------------------------------------
4069
4070    NOTE: `logScreenshot` is currently unsupported.
4071    """
4072    expectedPresses: int = len(args)
4073    downed: int = 0
4074    upped: int = 0
4075
4076    apply_interval: bool = False
4077    for code in args:
4078        if apply_interval:
4079            _sleep(interval)  # sleep between iterations
4080        apply_interval = True
4081
4082        downed += scancode_keyDown(code, _pause=False, auto_shift=auto_shift)
4083
4084    _sleep(wait)
4085
4086    apply_interval = False
4087    for code in reversed(args):
4088        if apply_interval:
4089            _sleep(interval)  # sleep between iterations
4090        apply_interval = True
4091
4092        upped += scancode_keyUp(code, _pause=False, auto_shift=auto_shift)
4093
4094    return expectedPresses == downed == upped
4095    # --------------------------------------------------------------------------
4096
4097
4098# ===== keyname functions ======================================================
4099
4100
4101# ----- keyDown ----------------------------------------------------------------
4102# Ignored parameters: logScreenshot
4103def keyDown(
4104    key: str,
4105    logScreenshot: None = None,
4106    _pause: bool = True,
4107    *,
4108    auto_shift: bool = False,
4109) -> bool:
4110    """
4111    Press down key corresponding to key name `key`.
4112
4113    `key` will be interpreted as a keyboard key (US QWERTY).
4114    The actually pressed key will depend on your system keyboard layout.
4115    Limits the available character set but should provide the best
4116    compatibility.
4117
4118    If `_pause` is True (default), then an automatic sleep will be performed
4119    after the function finshes executing. The duration is set by the global
4120    variable `PAUSE`.
4121
4122    If `auto_shift` is True, then "shifted" characters like upper case letters
4123    and the symbols on the number row automatically insert a Shift scancode
4124    into the input sequence.
4125
4126    ----------------------------------------------------------------------------
4127
4128    NOTE: `logScreenshot` is currently unsupported.
4129    """
4130    scancode: ScancodeTypes | None = KEYBOARD_MAPPING.get(key)
4131    if scancode is None:
4132        return False
4133    return scancode_keyDown(
4134        scancode, logScreenshot, _pause=_pause, auto_shift=auto_shift
4135    )
4136    # --------------------------------------------------------------------------
4137
4138
4139# ----- keyUp ------------------------------------------------------------------
4140# Ignored parameters: logScreenshot
4141def keyUp(
4142    key: str,
4143    logScreenshot: None = None,
4144    _pause: bool = True,
4145    *,
4146    auto_shift: bool = False,
4147) -> bool:
4148    """
4149    Lift up key corresponding to key name `key`.
4150
4151    `key` will be interpreted as a keyboard key (US QWERTY).
4152    The actually lifted key will depend on your system keyboard layout.
4153    Limits the available character set but should provide the best
4154    compatibility.
4155
4156    If `_pause` is True (default), then an automatic sleep will be performed
4157    after the function finshes executing. The duration is set by the global
4158    variable `PAUSE`.
4159
4160    If `auto_shift` is True, then "shifted" characters like upper case letters
4161    and the symbols on the number row automatically insert a Shift scancode
4162    into the input sequence.
4163
4164    ----------------------------------------------------------------------------
4165
4166    NOTE: `logScreenshot` is currently unsupported.
4167    """
4168    scancode: ScancodeTypes | None = KEYBOARD_MAPPING.get(key)
4169    if scancode is None:
4170        return False
4171    return scancode_keyUp(
4172        scancode, logScreenshot, _pause=_pause, auto_shift=auto_shift
4173    )
4174    # --------------------------------------------------------------------------
4175
4176
4177# ----- _helper_press ----------------------------------------------------------
4178def _helper_press(
4179    key: str,
4180    duration: float = 0.0,
4181    _pause: bool = True,
4182    auto_shift: bool = False,
4183) -> bool:
4184    """
4185    Press `key`, wait for `duration` seconds, release `key`.
4186
4187    Return `True` if complete press was successful.
4188    """
4189    downed: bool = keyDown(key, _pause=_pause, auto_shift=auto_shift)
4190    _sleep(duration)
4191    upped: bool = keyUp(key, _pause=_pause, auto_shift=auto_shift)
4192    # Count key press as complete if key was "downed" and "upped"
4193    # successfully
4194    return bool(downed and upped)
4195    # --------------------------------------------------------------------------
4196
4197
4198# ----- press ------------------------------------------------------------------
4199# Ignored parameters: logScreenshot
4200@_genericPyDirectInputChecks
4201def press(
4202    keys: str | Sequence[str],
4203    presses: int = 1,
4204    interval: float = 0.0,
4205    logScreenshot: None = None,
4206    _pause: bool = True,
4207    *,
4208    auto_shift: bool = False,
4209    delay: float = 0.0,
4210    duration: float = 0.0,
4211) -> bool:
4212    """
4213    Press the sequence of `keys` for `presses` amount of times.
4214
4215    `keys` will be interpreted as sequence of keyboard keys (US QWERTY).
4216    The actually pressed key will depend on your system keyboard layout.
4217    Limits the available character set but should provide the best
4218    compatibility.
4219
4220    Explanation of time parameters (seconds as floating point numbers):
4221
4222    - `interval` is the time spent waiting between sequences. If `keys` is a
4223    str instance, single element list or presses equals 1 (the default),
4224    then `interval` will be ignored.
4225    - `delay` is the time from one complete key (press+release) to the next one
4226    in the same sequence. If there is only a single key in a sequence, then
4227    `delay` will be ignored.
4228    - `duration` is the time spent on holding every key before releasing it
4229    again.
4230
4231    If `_pause` is True (default), then an automatic sleep will be performed
4232    after the function finshes executing. The duration is set by the global
4233    variable `PAUSE`.
4234    Be aware, that the global pause defined by the `PAUSE` var only applies
4235    after every call to this function, not inbetween (no extra pause between
4236    pressing and releasing key, use the `duration` argument instead)!
4237
4238    If `auto_shift` is True, then "shifted" characters like upper case letters
4239    and the symbols on the number row automatically insert a Shift scancode
4240    into the input sequence.
4241
4242    ----------------------------------------------------------------------------
4243
4244    NOTE: `logScreenshot` is currently unsupported.
4245    """
4246    if isinstance(keys, str):
4247        keys = [keys]  # If keys is 'enter', convert it to ['enter'].
4248    keys = [_normalize_key(key, auto_shift=auto_shift) for key in keys]
4249
4250    # We need to press x keys y times, which comes out to x*y presses in total
4251    expectedPresses: int = presses * len(keys)
4252    completedPresses: int = 0
4253
4254    apply_interval: bool = False
4255    for _ in range(presses):
4256        if apply_interval:  # Don't delay first press
4257            _sleep(interval)
4258        apply_interval = True
4259
4260        apply_delay: bool = False
4261        for k in keys:
4262            if apply_delay:  # Don't delay first press
4263                _sleep(delay)
4264            apply_delay = True
4265
4266            completedPresses += _helper_press(
4267                k, duration, _pause=False, auto_shift=auto_shift
4268            )
4269
4270    return completedPresses == expectedPresses
4271    # --------------------------------------------------------------------------
4272
4273
4274# ----- hold -------------------------------------------------------------------
4275@contextmanager
4276@_genericPyDirectInputChecks
4277def hold(
4278    keys: str | Sequence[str],
4279    logScreenshot: None = None,
4280    _pause: bool = True,
4281    *,
4282    auto_shift: bool = False,
4283    raise_on_failure: bool = False,
4284) -> Generator[None, None, None]:
4285    """
4286    Hold the sequence of keys corresponding to key names in `keys` as long as
4287    the context manager is in scope (press upon entry, release upon exit).
4288
4289    Keys will be released in reverse order (LIFO), but still practically
4290    instantenous.
4291
4292    `key` will be interpreted as a keyboard key (US QWERTY).
4293    The actually pressed key will depend on your system keyboard layout.
4294    Limits the available character set but should provide the best
4295    compatibility.
4296
4297    If `_pause` is True (default), then an automatic sleep will be performed
4298    after the function finshes executing. The duration is set by the global
4299    variable `PAUSE`.
4300    Be aware, that the global pause defined by the PAUSE `constant` only
4301    applies after every call to this function, not inbetween (no pause between
4302    press and releasing key)!
4303
4304    `auto_shift` is used internally by higher level functions to automatically
4305    press the shift key before supported scancodes (indicitated by a special
4306    bit outside the regular scancode range, while it technically can be used,
4307    it's not intended for public access).
4308
4309    If `raise_on_failure` is True, then `PriorInputFailedException` will be
4310    raised if not all keyboard inputs could be executed successfully.
4311
4312    ----------------------------------------------------------------------------
4313
4314    NOTE: `logScreenshot` is currently unsupported.
4315    """
4316    if isinstance(keys, str):
4317        keys = [keys]  # make single element into iterable
4318    keys = [_normalize_key(key, auto_shift=auto_shift) for key in keys]
4319
4320    expectedPresses: int = len(keys)
4321    downed: int = 0
4322    upped: int = 0
4323
4324    try:
4325        for k in keys:
4326            downed += keyDown(k, auto_shift=auto_shift)
4327        yield
4328    finally:
4329        for k in reversed(keys):
4330            upped += keyUp(k, auto_shift=auto_shift)
4331        if raise_on_failure and not (expectedPresses == downed == upped):
4332            raise PriorInputFailedException
4333    # --------------------------------------------------------------------------
4334
4335
4336# ----- typewrite --------------------------------------------------------------
4337@_genericPyDirectInputChecks
4338def typewrite(
4339    message: str,
4340    interval: float = 0.0,
4341    logScreenshot: None = None,
4342    _pause: bool = True,
4343    *,
4344    auto_shift: bool = False,
4345    delay: float = 0.0,
4346    duration: float = 0.0,
4347) -> None:
4348    """
4349    Break down `message` into a single character key sequence and press each
4350    key one by one.
4351
4352    `message` will be interpreted as sequence of keyboard keys (US QWERTY).
4353    The actually pressed keys will depend on your system keyboard layout.
4354    Limits the available character set but should provide the best
4355    compatibility.
4356
4357    Explanation of time parameters (seconds as floating point numbers):
4358
4359    - `interval` is the time spent waiting between sequences. If `message` is a
4360    single character string, then `interval` will be ignored.
4361    - `delay` is the time from one complete key (press+release) to the next one
4362    in the same sequence. If there is only a single key in a sequence, then
4363    `delay` will be ignored.
4364    - `duration` is the time spent on holding every key before releasing it
4365    again.
4366
4367    If `_pause` is True (default), then an automatic sleep will be performed
4368    after the function finshes executing. The duration is set by the global
4369    variable `PAUSE`.
4370    Be aware, that the global pause defined by the PAUSE `constant` only
4371    applies after every call to this function, not inbetween (no pause between
4372    press and releasing key)!
4373
4374    `auto_shift` is used internally by higher level functions to automatically
4375    press the shift key before supported scancodes (indicitated by a special
4376    bit outside the regular scancode range, while it technically can be used,
4377    it's not intended for public access).
4378
4379    ----------------------------------------------------------------------------
4380
4381    NOTE: `logScreenshot` is currently unsupported.
4382    """
4383
4384    apply_interval: bool = False
4385    for key in message:
4386        if apply_interval:  # Don't delay first press
4387            _sleep(interval)
4388        apply_interval = True
4389
4390        press(
4391            key,
4392            _pause=False,
4393            auto_shift=auto_shift,
4394            delay=delay,
4395            duration=duration,
4396        )
4397    # --------------------------------------------------------------------------
4398
4399
4400# ----- typewrite alias --------------------------------------------------------
4401write = typewrite
4402# ------------------------------------------------------------------------------
4403
4404
4405# ----- hotkey -----------------------------------------------------------------
4406# Originally implemented by
4407# https://github.com/learncodebygaming/pydirectinput/pull/30
4408@_genericPyDirectInputChecks
4409def hotkey(
4410    *args: str,
4411    interval: float = 0.0,
4412    wait: float = 0.0,
4413    logScreenshot: None = None,
4414    _pause: bool = True,
4415    auto_shift: bool = True,
4416) -> None:
4417    """
4418    Press down buttons in order they are specified as arguments,
4419    releasing them in reverse order, e.g. 'ctrl', 'c' will first press
4420    Control, then C and release C before releasing Control.
4421
4422    Use keyword-only argument `interval` to specify a delay between single
4423    keys when pressing and releasing and `wait` for delay between last press
4424    and first release.
4425
4426    If `_pause` is True (default), then an automatic sleep will be performed
4427    after the function finshes executing. The duration is set by the global
4428    variable `PAUSE`.
4429    Be aware, that the global pause defined by the PAUSE `constant` only
4430    applies after every call to this function, not inbetween (no pause between
4431    press and releasing key)!
4432
4433    `auto_shift` is used internally by higher level functions to automatically
4434    press the shift key before supported scancodes (indicitated by a special
4435    bit outside the regular scancode range, while it technically can be used,
4436    it's not intended for public access).
4437
4438    ----------------------------------------------------------------------------
4439
4440    NOTE: `logScreenshot` is currently unsupported.
4441    """
4442    apply_interval: bool = False
4443    for key in args:
4444        if apply_interval:
4445            _sleep(interval)  # sleep between iterations
4446        apply_interval = True
4447
4448        keyDown(key, _pause=False, auto_shift=auto_shift)
4449
4450    _sleep(wait)
4451
4452    apply_interval = False
4453    for key in reversed(args):
4454        if apply_interval:
4455            _sleep(interval)  # sleep between iterations
4456        apply_interval = True
4457
4458        keyUp(key, _pause=False, auto_shift=auto_shift)
4459    # --------------------------------------------------------------------------
4460
4461
4462# ===== unicode functions ======================================================
4463
4464
4465# ----- unicode_charDown -------------------------------------------------------
4466@_genericPyDirectInputChecks
4467def unicode_charDown(
4468    char: str, logScreenshot: None = None, _pause: bool = True
4469) -> bool:
4470    """
4471    Send Unicode character(s) `char` to currently focused application as
4472    WM_KEYDOWN message.
4473
4474    `char` will be interpreted as a string of Unicode characters
4475    (independet from keyboard layout). Supports complete Unicode character set
4476    but may not be compatible with every application.
4477
4478    If `_pause` is True (default), then an automatic sleep will be performed
4479    after the function finshes executing. The duration is set by the global
4480    variable `PAUSE`.
4481
4482    ----------------------------------------------------------------------------
4483
4484    NOTE: `logScreenshot` is currently unsupported.
4485    """
4486    utf16surrogates: bytes = char.encode("utf-16be")
4487    codes: Sequence[int] = unpack(
4488        f">{len(utf16surrogates) // 2}H", utf16surrogates
4489    )
4490
4491    keybdFlags: int = _KEYEVENTF_UNICODE
4492
4493    input_structs: list[_INPUT] = [
4494        _create_keyboard_input(wVk=0, wScan=charcode, dwFlags=keybdFlags)
4495        for charcode in codes
4496    ]
4497    # Init event tracking
4498    expectedEvents: int = len(input_structs)
4499    insertedEvents: int = _send_input(input_structs)
4500
4501    return insertedEvents == expectedEvents
4502    # --------------------------------------------------------------------------
4503
4504
4505# ----- unicode_charUp ---------------------------------------------------------
4506@_genericPyDirectInputChecks
4507def unicode_charUp(
4508    char: str, logScreenshot: None = None, _pause: bool = True
4509) -> bool:
4510    """
4511    Send Unicode character(s) `char` to currently focused application as
4512    WM_KEYUP message.
4513
4514    `char` will be interpreted as a string of Unicode characters
4515    (independet from keyboard layout). Supports complete Unicode character set
4516    but may not be compatible with every application.
4517
4518    If `_pause` is True (default), then an automatic sleep will be performed
4519    after the function finshes executing. The duration is set by the global
4520    variable `PAUSE`.
4521
4522    ----------------------------------------------------------------------------
4523
4524    NOTE: `logScreenshot` is currently unsupported.
4525    """
4526    utf16surrogates: bytes = char.encode("utf-16be")
4527    codes: Sequence[int] = unpack(
4528        f">{len(utf16surrogates) // 2}H", utf16surrogates
4529    )
4530
4531    keybdFlags: int = _KEYEVENTF_UNICODE | _KEYEVENTF_KEYUP
4532
4533    input_structs: list[_INPUT] = [
4534        _create_keyboard_input(wVk=0, wScan=charcode, dwFlags=keybdFlags)
4535        for charcode in codes
4536    ]
4537    # Init event tracking
4538    expectedEvents: int = len(input_structs)
4539    insertedEvents: int = _send_input(input_structs)
4540
4541    return insertedEvents == expectedEvents
4542    # --------------------------------------------------------------------------
4543
4544
4545# ----- _helper_unicode_press_char ---------------------------------------------
4546def _helper_unicode_press_char(
4547    char: str,
4548    duration: float = 0.0,
4549    _pause: bool = True,
4550) -> bool:
4551    """
4552    Press `key`, wait for `duration` seconds, release `key`.
4553
4554    Return `True` if complete press was successful.
4555    """
4556    downed: bool = unicode_charDown(char, _pause=_pause)
4557    _sleep(duration)
4558    upped: bool = unicode_charUp(char, _pause=_pause)
4559    # Count key press as complete if key was "downed" and "upped"
4560    # successfully
4561    return bool(downed and upped)
4562    # --------------------------------------------------------------------------
4563
4564
4565# ----- unicode_press ----------------------------------------------------------
4566@_genericPyDirectInputChecks
4567def unicode_press(
4568    chars: str | Sequence[str],
4569    presses: int = 1,
4570    interval: float = 0.0,
4571    logScreenshot: None = None,
4572    _pause: bool = True,
4573    *,
4574    delay: float = 0.0,
4575    duration: float = 0.0,
4576) -> bool:
4577    """
4578    Press the sequence of `chars` for `presses` amount of times.
4579
4580    `chars` will be interpreted as a sequence of Unicode characters
4581    (independent from keyboard layout). Supports complete Unicode character set
4582    but may not be compatible with every application.
4583
4584    Explanation of time parameters (seconds as floating point numbers):
4585
4586    - `interval` is the time spent waiting between sequences. If `chars` is a
4587    str instance or single element list, then `interval` will be ignored.
4588    - `delay` is the time from one complete char (press+release) to the next
4589    one in the same sequence. If there is only a single char in a sequence,
4590    then `delay` will be ignored.
4591    - `duration` is the time spent on holding every char before releasing it
4592    again.
4593
4594    If `_pause` is True (default), then an automatic sleep will be performed
4595    after the function finshes executing. The duration is set by the global
4596    variable `PAUSE`.
4597    Be aware, that the global pause defined by the PAUSE `constant` only
4598    applies after every call to this function, not inbetween (no extra pause
4599    between pressing and releasing key, use the `duration` argument instead)!
4600
4601    ----------------------------------------------------------------------------
4602
4603    NOTE: `logScreenshot` is currently unsupported.
4604    """
4605    if isinstance(chars, str):
4606        chars = [chars]
4607
4608    # We need to press x keys y times, which comes out to x*y presses in total
4609    expectedPresses: int = presses * len(chars)
4610    completedPresses: int = 0
4611
4612    apply_interval: bool = False
4613    for _ in range(presses):
4614        if apply_interval:  # Don't delay first press
4615            _sleep(interval)
4616        apply_interval = True
4617
4618        apply_delay: bool = False
4619        for c in chars:
4620            if apply_delay:  # Don't delay first press
4621                _sleep(delay)
4622            apply_delay = True
4623
4624            completedPresses += _helper_unicode_press_char(
4625                c,
4626                duration,
4627                _pause=False,
4628            )
4629
4630    return completedPresses == expectedPresses
4631    # --------------------------------------------------------------------------
4632
4633
4634# ----- unicode_hold -----------------------------------------------------------
4635@contextmanager
4636@_genericPyDirectInputChecks
4637def unicode_hold(
4638    chars: str | Sequence[str],
4639    logScreenshot: None = None,
4640    _pause: bool = True,
4641    *,
4642    raise_on_failure: bool = False,
4643) -> Generator[None, None, None]:
4644    """
4645    Hold the sequence of "keys" corresponding to unicode characters in `chars`
4646    as long as the context manager is in scope (press upon entry,
4647    release upon exit).
4648
4649    `chars` will be interpreted as a sequence of Unicode characters
4650    (independet from keyboard layout). Supports complete Unicode character set
4651    but may not be compatible with every application.
4652
4653    Keys will be released in reverse order (LIFO), but still practically
4654    instantenous.
4655
4656    If `_pause` is True (default), then an automatic sleep will be performed
4657    after the function finshes executing. The duration is set by the global
4658    variable `PAUSE`.
4659    Be aware, that the global pause defined by the PAUSE `constant` only
4660    applies  after every call to this function, not inbetween (no pause between
4661    press and releasing key)!
4662
4663    If `raise_on_failure` is True, then `PriorInputFailedException` will be
4664    raised if not all keyboard inputs could be executed successfully.
4665
4666    ----------------------------------------------------------------------------
4667
4668    NOTE: `logScreenshot` is currently unsupported.
4669    """
4670    if isinstance(chars, str):
4671        chars = [chars]  # make single element into iterable
4672
4673    expectedPresses: int = len(chars)
4674    downed: int = 0
4675    upped: int = 0
4676
4677    try:
4678        for c in chars:
4679            downed += unicode_charDown(c, _pause=False)
4680        yield
4681    finally:
4682        for c in reversed(chars):
4683            upped += unicode_charUp(c, _pause=False)
4684        if raise_on_failure and not (expectedPresses == downed == upped):
4685            raise PriorInputFailedException
4686    # --------------------------------------------------------------------------
4687
4688
4689# ----- unicode_typewrite ------------------------------------------------------
4690@_genericPyDirectInputChecks
4691def unicode_typewrite(
4692    message: str,
4693    interval: float = 0.0,
4694    logScreenshot: None = None,
4695    _pause: bool = True,
4696    *,
4697    delay: float = 0.0,
4698    duration: float = 0.0,
4699) -> None:
4700    """
4701    Break down `message` into characters and press them one by one.
4702
4703    `message` will be interpreted as a sequence of Unicode characters
4704    (independet from keyboard layout). Supports complete Unicode character set
4705    but may not be compatible with every application.
4706
4707    Explanation of time parameters (seconds as floating point numbers):
4708
4709    - `interval` is the time spent waiting between sequences. If `message` is a
4710    single character string, then `interval` will be ignored.
4711    - `delay` is the time from one complete key (press+release) to the next one
4712    in the same sequence. If there is only a single key in a sequence, then
4713    `delay` will be ignored.
4714    - `duration` is the time spent on holding every key before releasing it
4715    again.
4716
4717    If `_pause` is True (default), then an automatic sleep will be performed
4718    after the function finshes executing. The duration is set by the global
4719    variable `PAUSE`.
4720    Be aware, that the global pause defined by the PAUSE `constant` only
4721    applies after every call to this function, not inbetween (no pause between
4722    press and releasing key)!
4723
4724    ----------------------------------------------------------------------------
4725
4726    NOTE: `logScreenshot` is currently unsupported.
4727    """
4728    apply_interval: bool = False
4729    for char in message:
4730        if apply_interval:
4731            _sleep(interval)  # sleep between iterations
4732        apply_interval = True
4733
4734        unicode_press(char, _pause=False, delay=delay, duration=duration)
4735    # --------------------------------------------------------------------------
4736
4737
4738# ----- unicode_typewrite alias ------------------------------------------------
4739unicode_write = unicode_typewrite
4740# ------------------------------------------------------------------------------
4741
4742
4743# ----- unicode_hotkey ---------------------------------------------------------
4744@_genericPyDirectInputChecks
4745def unicode_hotkey(
4746    *args: str,
4747    interval: float = 0.0,
4748    wait: float = 0.0,
4749    logScreenshot: None = None,
4750    _pause: bool = True,
4751) -> None:
4752    """
4753    Press down buttons in order they are specified as arguments,
4754    releasing them in reverse order.
4755
4756    This function makes little sense for Unicode characters and mainly exists
4757    for parity with the other, lower-level hotkey functions!
4758
4759    See `unicode_press()` for an alternative function that presses keys in
4760    series instead.
4761
4762    Use keyword-only argument `interval` to specify a delay between single
4763    keys when pressing and releasing and `wait` for delay between last press
4764    and first release.
4765
4766    If `_pause` is True (default), then an automatic sleep will be performed
4767    after the function finshes executing. The duration is set by the global
4768    variable `PAUSE`.
4769    Be aware, that the global pause defined by the PAUSE `constant` only
4770    applies after every call to this function, not inbetween (no pause between
4771    press and releasing key)!
4772
4773    ----------------------------------------------------------------------------
4774
4775    NOTE: `logScreenshot` is currently unsupported.
4776    """
4777    apply_interval: bool = False
4778    for char in args:
4779        if apply_interval:
4780            _sleep(interval)  # sleep between iterations
4781        apply_interval = True
4782
4783        unicode_charDown(char, _pause=False)
4784
4785    _sleep(wait)
4786
4787    apply_interval = False
4788    for char in reversed(args):
4789        if apply_interval:
4790            _sleep(interval)  # sleep between iterations
4791        apply_interval = True
4792
4793        unicode_charUp(char, _pause=False)
4794    # --------------------------------------------------------------------------
4795
4796
4797# ------------------------------------------------------------------------------
4798# Save current Enhanced Pointer Precsion setting during import
4799# unless disabled by environment variable.
4800# Since this is a safety feature, this import side-effect is enabled by default.
4801if not os.environ.get("PYDIRECTINPUT_SKIP_STORING_MOUSE_SETTINGS"):
4802    store_mouse_acceleration_settings()
4803# ------------------------------------------------------------------------------
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]):
1544class ScancodeSequence(List[int]):
1545    """
1546    A special class with the sole purpose of representing extended scancode
1547    sequences that should be grouped together in a single INPUT array.
1548
1549    Inserting non-scancode elements is illegal, but no runtime checks exist
1550    to verify correct input! Violations could lead to unpredictable runtime
1551    behaviour. You've been warned.
1552    """
1553
1554    pass
1555    # --------------------------------------------------------------------------

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.

Inherited Members
builtins.list
list
clear
copy
append
insert
extend
pop
remove
index
count
reverse
sort
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):
1852class FailSafeException(Exception):
1853    """Raised when _failSafeCheck detects failsafe mouse position."""
1854
1855    pass

Raised when _failSafeCheck detects failsafe mouse position.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
add_note
args
class PriorInputFailedException(builtins.Exception):
1858class PriorInputFailedException(Exception):
1859    """Raised in hold() context managers when raise_on_failure is set."""
1860
1861    pass
1862    # --------------------------------------------------------------------------

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

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
add_note
args
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]:
2066def position(
2067    x: int | float | None = None, y: int | float | None = None
2068) -> tuple[int, int]:
2069    """
2070    Return a postion tuple `(x, y)`.
2071
2072    If x and/or y argument(s) are not given, use current mouse cursor coordinate
2073    instead.
2074    """
2075    cursor: _POINT = _get_cursor_pos()
2076    return (
2077        cursor.x if x is None else int(x),
2078        cursor.y if y is None else int(y),
2079    )
2080    # --------------------------------------------------------------------------

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]:
2084def size() -> tuple[int, int]:
2085    """
2086    Return the size of the primary display as tuple `(width, height)`.
2087    """
2088    return (
2089        _get_system_metrics(_SM_CXSCREEN),
2090        _get_system_metrics(_SM_CYSCREEN),
2091    )
2092    # --------------------------------------------------------------------------

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

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

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:
2126def on_primary_monitor(
2127    x: int | tuple[int, int] | None = None, y: int | None = None
2128) -> bool:
2129    """
2130    Returns whether the given xy coordinates are on the primary screen or not.
2131
2132    If x and/or y argument(s) are not given, current mouse cursor coordinates
2133    will be used instead.
2134    """
2135    if isinstance(x, Sequence):
2136        assert not isinstance(x, int)  # remove int annotation, mypy needs this
2137        if y is not None:
2138            raise ValueError(
2139                "onScreen() does not accept Sequence-types as first argument "
2140                "if a second argument is also provided!"
2141            )
2142        try:
2143            x, y = x[0], x[1]
2144        except IndexError as e:
2145            raise ValueError(
2146                "onScreen() does not accept single element sequences "
2147                "as first argument!"
2148            ) from e
2149
2150    x, y = position(x, y)
2151    display_width: int
2152    display_height: int
2153    display_width, display_height = size()
2154
2155    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:
2126def on_primary_monitor(
2127    x: int | tuple[int, int] | None = None, y: int | None = None
2128) -> bool:
2129    """
2130    Returns whether the given xy coordinates are on the primary screen or not.
2131
2132    If x and/or y argument(s) are not given, current mouse cursor coordinates
2133    will be used instead.
2134    """
2135    if isinstance(x, Sequence):
2136        assert not isinstance(x, int)  # remove int annotation, mypy needs this
2137        if y is not None:
2138            raise ValueError(
2139                "onScreen() does not accept Sequence-types as first argument "
2140                "if a second argument is also provided!"
2141            )
2142        try:
2143            x, y = x[0], x[1]
2144        except IndexError as e:
2145            raise ValueError(
2146                "onScreen() does not accept single element sequences "
2147                "as first argument!"
2148            ) from e
2149
2150    x, y = position(x, y)
2151    display_width: int
2152    display_height: int
2153    display_width, display_height = size()
2154
2155    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:
2176def valid_screen_coordinates(
2177    x: int | tuple[int, int] | None = None, y: int | None = None
2178) -> bool:
2179    """
2180    Returns whether the given xy coordinates are on a real monitor or not.
2181
2182    If x and/or y argument(s) are not given, current mouse cursor coordinates
2183    will be used instead.
2184    """
2185    if isinstance(x, Sequence):
2186        assert not isinstance(x, int)  # remove int annotation, mypy needs this
2187        if y is not None:
2188            raise ValueError(
2189                "onScreen() does not accept Sequence-types as first argument "
2190                "if a second argument is also provided!"
2191            )
2192        try:
2193            x, y = x[0], x[1]
2194        except IndexError as e:
2195            raise ValueError(
2196                "onScreen() does not accept single element sequences "
2197                "as first argument!"
2198            ) from e
2199
2200    x, y = position(x, y)
2201    return _monitor_from_point(x, y) is not None
2202    # --------------------------------------------------------------------------

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:
2339def store_mouse_acceleration_settings() -> None:
2340    """
2341    Manually save the current Windows Enhanced Pointer Precision setting so
2342    that it can be restored later with `restore_mouse_acceleration_settings()`.
2343    """
2344    precision: int
2345    _, _, precision = _get_mouse_parameters()
2346    speed: int = _get_mouse_speed()
2347    __MouseSpeedSettings.set_manual_mouse_settings(precision, speed)
2348    # --------------------------------------------------------------------------

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:
2352def restore_mouse_acceleration_settings() -> None:
2353    """
2354    Manually restore the current Windows Enhanced Pointer Precision setting to
2355    what it was beforehand when it was saved with
2356    `store_mouse_acceleration_settings()`.
2357    """
2358    precision: int | None
2359    speed: int | None
2360    precision, speed = __MouseSpeedSettings.get_manual_mouse_settings()
2361    if precision is None or speed is None:
2362        raise ValueError(
2363            "Can't restore Enhanced Pointer Precision setting! "
2364            "Setting was not saved beforehand!"
2365        )
2366    th1: int
2367    th2: int
2368    th1, th2, _ = _get_mouse_parameters()
2369    _set_mouse_parameters(th1, th2, precision)
2370    _set_mouse_speed(speed)
2371    # --------------------------------------------------------------------------

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

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

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

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: Optional[Callable[[float], float]] = 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:
2620def leftClick(
2621    x: int | None = None,
2622    y: int | None = None,
2623    interval: float = 0.0,
2624    duration: float = 0.0,
2625    tween: Callable[[float], float] | None = None,
2626    logScreenshot: bool = False,
2627    _pause: bool = True,
2628    *,
2629    relative: bool = False,
2630    virtual: bool = False,
2631    path_function: PathFunction | None = None,
2632    attempt_pixel_perfect: bool = False,
2633    disable_mouse_acceleration: bool = False,
2634) -> None:
2635    """
2636    Click Left Mouse button.
2637
2638    See `click()` for more information
2639    """
2640    click(
2641        x,
2642        y,
2643        clicks=1,
2644        interval=interval,
2645        button=MOUSE_LEFT,
2646        duration=duration,
2647        tween=tween,
2648        logScreenshot=logScreenshot,
2649        _pause=_pause,  # Keep _pause since this function has no input checks
2650        relative=relative,
2651        virtual=virtual,
2652        path_function=path_function,
2653        attempt_pixel_perfect=attempt_pixel_perfect,
2654        disable_mouse_acceleration=disable_mouse_acceleration,
2655    )
2656    # --------------------------------------------------------------------------

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: Optional[Callable[[float], float]] = 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:
2660def rightClick(
2661    x: int | None = None,
2662    y: int | None = None,
2663    interval: float = 0.0,
2664    duration: float = 0.0,
2665    tween: Callable[[float], float] | None = None,
2666    logScreenshot: bool = False,
2667    _pause: bool = True,
2668    *,
2669    relative: bool = False,
2670    virtual: bool = False,
2671    path_function: PathFunction | None = None,
2672    attempt_pixel_perfect: bool = False,
2673    disable_mouse_acceleration: bool = False,
2674) -> None:
2675    """
2676    Click Right Mouse button.
2677
2678    See `click()` for more information
2679    """
2680    click(
2681        x,
2682        y,
2683        clicks=1,
2684        interval=interval,
2685        button=MOUSE_RIGHT,
2686        duration=duration,
2687        tween=tween,
2688        logScreenshot=logScreenshot,
2689        _pause=_pause,  # Keep _pause since this function has no input checks
2690        relative=relative,
2691        virtual=virtual,
2692        path_function=path_function,
2693        attempt_pixel_perfect=attempt_pixel_perfect,
2694        disable_mouse_acceleration=disable_mouse_acceleration,
2695    )
2696    # --------------------------------------------------------------------------

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: Optional[Callable[[float], float]] = 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:
2700def middleClick(
2701    x: int | None = None,
2702    y: int | None = None,
2703    interval: float = 0.0,
2704    duration: float = 0.0,
2705    tween: Callable[[float], float] | None = None,
2706    logScreenshot: bool = False,
2707    _pause: bool = True,
2708    *,
2709    relative: bool = False,
2710    virtual: bool = False,
2711    path_function: PathFunction | None = None,
2712    attempt_pixel_perfect: bool = False,
2713    disable_mouse_acceleration: bool = False,
2714) -> None:
2715    """
2716    Click Middle Mouse button.
2717
2718    See `click()` for more information
2719    """
2720    click(
2721        x,
2722        y,
2723        clicks=1,
2724        interval=interval,
2725        button=MOUSE_MIDDLE,
2726        duration=duration,
2727        tween=tween,
2728        logScreenshot=logScreenshot,
2729        _pause=_pause,  # Keep _pause since this function has no input checks
2730        relative=relative,
2731        virtual=virtual,
2732        path_function=path_function,
2733        attempt_pixel_perfect=attempt_pixel_perfect,
2734        disable_mouse_acceleration=disable_mouse_acceleration,
2735    )
2736    # --------------------------------------------------------------------------

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: Optional[Callable[[float], float]] = 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:
2740def doubleClick(
2741    x: int | None = None,
2742    y: int | None = None,
2743    interval: float = 0.0,
2744    button: str = MOUSE_LEFT,
2745    duration: float = 0.0,
2746    tween: Callable[[float], float] | None = None,
2747    logScreenshot: bool = False,
2748    _pause: bool = True,
2749    *,
2750    relative: bool = False,
2751    virtual: bool = False,
2752    path_function: PathFunction | None = None,
2753    attempt_pixel_perfect: bool = False,
2754    disable_mouse_acceleration: bool = False,
2755) -> None:
2756    """
2757    Double click `button`.
2758
2759    See `click()` for more information
2760    """
2761    click(
2762        x,
2763        y,
2764        clicks=2,
2765        interval=interval,
2766        button=button,
2767        duration=duration,
2768        tween=tween,
2769        logScreenshot=logScreenshot,
2770        _pause=_pause,  # Keep _pause since this function has no input checks
2771        relative=relative,
2772        virtual=virtual,
2773        path_function=path_function,
2774        attempt_pixel_perfect=attempt_pixel_perfect,
2775        disable_mouse_acceleration=disable_mouse_acceleration,
2776    )
2777    # --------------------------------------------------------------------------

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: Optional[Callable[[float], float]] = 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:
2781def tripleClick(
2782    x: int | None = None,
2783    y: int | None = None,
2784    interval: float = 0.0,
2785    button: str = MOUSE_LEFT,
2786    duration: float = 0.0,
2787    tween: Callable[[float], float] | None = None,
2788    logScreenshot: bool = False,
2789    _pause: bool = True,
2790    *,
2791    relative: bool = False,
2792    virtual: bool = False,
2793    path_function: PathFunction | None = None,
2794    attempt_pixel_perfect: bool = False,
2795    disable_mouse_acceleration: bool = False,
2796) -> None:
2797    """
2798    Triple click `button`.
2799
2800    See `click()` for more information
2801    """
2802    click(
2803        x,
2804        y,
2805        clicks=3,
2806        interval=interval,
2807        button=button,
2808        duration=duration,
2809        tween=tween,
2810        logScreenshot=logScreenshot,
2811        _pause=_pause,  # Keep _pause since this function has no input checks
2812        relative=relative,
2813        virtual=virtual,
2814        path_function=path_function,
2815        attempt_pixel_perfect=attempt_pixel_perfect,
2816        disable_mouse_acceleration=disable_mouse_acceleration,
2817    )
2818    # --------------------------------------------------------------------------

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

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

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

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

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.

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

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.

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

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.

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 containting 2 integers each) 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: Optional[Callable[[float], float]] = 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:
3587@_genericPyDirectInputChecks
3588def dragTo(
3589    x: int | None = None,
3590    y: int | None = None,
3591    duration: float = 0.0,
3592    tween: Callable[[float], float] | None = None,
3593    button: str | None = None,
3594    logScreenshot: bool = False,
3595    _pause: bool = True,
3596    mouseDownUp: bool = True,
3597    *,
3598    relative: bool = False,
3599    virtual: bool = False,
3600    path_function: PathFunction | None = None,
3601    attempt_pixel_perfect: bool = False,
3602    disable_mouse_acceleration: bool = False,
3603) -> None:
3604    """
3605    Press and hold a mouse button while moving to the target coordinates.
3606
3607    See `moveTo` for more information on most arguments.
3608
3609    `button` is a string that is one of the following constants:
3610    MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE, MOUSE_BUTTON4, MOUSE_BUTTON5,
3611    MOUSE_PRIMARY (default), MOUSE_SECONDARY.
3612
3613    If `mouseDownUp` (default: True) is manually set to False, then this
3614    function is basically the same as `moveTo`. Only exists to match PyAutoGUI.
3615
3616    If `_pause` is True (default), then an automatic sleep will be performed
3617    after the function finshes executing. The duration is set by the global
3618    variable `PAUSE`.
3619
3620    ----------------------------------------------------------------------------
3621
3622    NOTE: `logScreenshot` is currently unsupported.
3623    """
3624    # TODO: bounding box check for valid position
3625    if button is None:
3626        button = MOUSE_PRIMARY
3627    if mouseDownUp:
3628        mouseDown(button=button, _pause=False, virtual=virtual)
3629    moveTo(
3630        x,
3631        y,
3632        duration=duration,
3633        tween=tween,
3634        logScreenshot=logScreenshot,
3635        _pause=False,  # don't add an additional pause
3636        relative=relative,
3637        virtual=virtual,
3638        path_function=path_function,
3639        attempt_pixel_perfect=attempt_pixel_perfect,
3640        disable_mouse_acceleration=disable_mouse_acceleration,
3641    )
3642    if mouseDownUp:
3643        mouseUp(button=button, _pause=False, virtual=virtual)
3644    # --------------------------------------------------------------------------

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: Optional[Callable[[float], float]] = 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:
3648@_genericPyDirectInputChecks
3649def dragRel(
3650    xOffset: int | None = None,
3651    yOffset: int | None = None,
3652    duration: float = 0.0,
3653    tween: Callable[[float], float] | None = None,
3654    button: str | None = None,
3655    logScreenshot: bool = False,
3656    _pause: bool = True,
3657    mouseDownUp: bool = True,
3658    *,
3659    relative: bool = False,
3660    virtual: bool = False,
3661    path_function: PathFunction | None = None,
3662    disable_mouse_acceleration: bool = False,
3663) -> None:
3664    """
3665    Press and hold a mouse button while moving a relative distance
3666
3667    See `moveRel` for more information on most arguments.
3668
3669    `button` is a string that is one of the following constants:
3670    MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE, MOUSE_BUTTON4, MOUSE_BUTTON5,
3671    MOUSE_PRIMARY (default), MOUSE_SECONDARY.
3672
3673    If `mouseDownUp` (default: True) is manually set to False, then this
3674    function is basically the same as `moveTo`. Only exists to match PyAutoGUI.
3675
3676    If `_pause` is True (default), then an automatic sleep will be performed
3677    after the function finshes executing. The duration is set by the global
3678    variable `PAUSE`.
3679
3680    ----------------------------------------------------------------------------
3681
3682    NOTE: `logScreenshot` is currently unsupported.
3683    """
3684    # TODO: bounding box check for valid position
3685    if button is None:
3686        button = MOUSE_PRIMARY
3687    if mouseDownUp:
3688        mouseDown(button=button, _pause=False, virtual=virtual)
3689    moveRel(
3690        xOffset,
3691        yOffset,
3692        duration=duration,
3693        tween=tween,
3694        logScreenshot=logScreenshot,
3695        _pause=False,  # don't add an additional pause
3696        relative=relative,
3697        virtual=virtual,
3698        path_function=path_function,
3699        disable_mouse_acceleration=disable_mouse_acceleration,
3700    )
3701    if mouseDownUp:
3702        mouseUp(button=button, _pause=False, virtual=virtual)
3703    # --------------------------------------------------------------------------

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: Optional[Callable[[float], float]] = 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:
3648@_genericPyDirectInputChecks
3649def dragRel(
3650    xOffset: int | None = None,
3651    yOffset: int | None = None,
3652    duration: float = 0.0,
3653    tween: Callable[[float], float] | None = None,
3654    button: str | None = None,
3655    logScreenshot: bool = False,
3656    _pause: bool = True,
3657    mouseDownUp: bool = True,
3658    *,
3659    relative: bool = False,
3660    virtual: bool = False,
3661    path_function: PathFunction | None = None,
3662    disable_mouse_acceleration: bool = False,
3663) -> None:
3664    """
3665    Press and hold a mouse button while moving a relative distance
3666
3667    See `moveRel` for more information on most arguments.
3668
3669    `button` is a string that is one of the following constants:
3670    MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE, MOUSE_BUTTON4, MOUSE_BUTTON5,
3671    MOUSE_PRIMARY (default), MOUSE_SECONDARY.
3672
3673    If `mouseDownUp` (default: True) is manually set to False, then this
3674    function is basically the same as `moveTo`. Only exists to match PyAutoGUI.
3675
3676    If `_pause` is True (default), then an automatic sleep will be performed
3677    after the function finshes executing. The duration is set by the global
3678    variable `PAUSE`.
3679
3680    ----------------------------------------------------------------------------
3681
3682    NOTE: `logScreenshot` is currently unsupported.
3683    """
3684    # TODO: bounding box check for valid position
3685    if button is None:
3686        button = MOUSE_PRIMARY
3687    if mouseDownUp:
3688        mouseDown(button=button, _pause=False, virtual=virtual)
3689    moveRel(
3690        xOffset,
3691        yOffset,
3692        duration=duration,
3693        tween=tween,
3694        logScreenshot=logScreenshot,
3695        _pause=False,  # don't add an additional pause
3696        relative=relative,
3697        virtual=virtual,
3698        path_function=path_function,
3699        disable_mouse_acceleration=disable_mouse_acceleration,
3700    )
3701    if mouseDownUp:
3702        mouseUp(button=button, _pause=False, virtual=virtual)
3703    # --------------------------------------------------------------------------

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:
3717def is_valid_key(key: str) -> bool:
3718    """
3719    Returns true if key name `key` can be translated into a valid scan code.
3720    """
3721    return key in KEYBOARD_MAPPING

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

def isValidKey(key: str) -> bool:
3717def is_valid_key(key: str) -> bool:
3718    """
3719    Returns true if key name `key` can be translated into a valid scan code.
3720    """
3721    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:
3732@_genericPyDirectInputChecks
3733def scancode_keyDown(
3734    scancodes: ScancodeTypes,
3735    logScreenshot: None = None,
3736    _pause: bool = True,
3737    *,
3738    auto_shift: bool = False,
3739) -> bool:
3740    """
3741    Press down key corresponding to `scancodes`.
3742
3743    The actually pressed key will depend on your system keyboard layout.
3744    Limits the available character set but should provide the best
3745    compatibility.
3746
3747    If `_pause` is True (default), then an automatic sleep will be performed
3748    after the function finshes executing. The duration is set by the global
3749    variable `PAUSE`.
3750
3751    `auto_shift` is used internally by higher level functions to automatically
3752    press the shift key before supported scancodes (indicitated by a special
3753    bit outside the regular scancode range, while it technically can be used,
3754    it's not intended for public access).
3755
3756    ----------------------------------------------------------------------------
3757
3758    NOTE: `logScreenshot` is currently unsupported.
3759    """
3760    scancodes_sequence: ScancodeSequence
3761    if isinstance(scancodes, int):
3762        scancodes_sequence = ScancodeSequence([scancodes])
3763    else:
3764        scancodes_sequence = scancodes
3765
3766    keybdFlags: int = _KEYEVENTF_SCANCODE
3767    input_structs: list[_INPUT] = []
3768    extendedFlag: int
3769
3770    # Init event tracking
3771    insertedEvents: int = 0
3772    expectedEvents: int = 0
3773
3774    for scancode in scancodes_sequence:
3775        if auto_shift and scancode & _OFFSET_SHIFTKEY:
3776            input_structs += [
3777                _create_keyboard_input(
3778                    wScan=_SHIFT_SCANCODE, dwFlags=keybdFlags
3779                )
3780            ]
3781            expectedEvents += 1
3782
3783        scancode = scancode & 0xFFFF
3784
3785        extendedFlag = _KEYEVENTF_EXTENDEDKEY if scancode >= 0xE000 else 0
3786        input_structs += [
3787            _create_keyboard_input(
3788                wScan=scancode, dwFlags=keybdFlags | extendedFlag
3789            )
3790        ]
3791        expectedEvents += 1
3792
3793    insertedEvents += _send_input(input_structs)
3794
3795    # SendInput returns the number of event successfully inserted into
3796    # input stream
3797    # https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput#return-value
3798    return insertedEvents == expectedEvents
3799    # --------------------------------------------------------------------------

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:
3803@_genericPyDirectInputChecks
3804def scancode_keyUp(
3805    scancodes: ScancodeTypes,
3806    logScreenshot: None = None,
3807    _pause: bool = True,
3808    *,
3809    auto_shift: bool = False,
3810) -> bool:
3811    """
3812    Release key corresponding to `scancodes`.
3813
3814    The actually pressed key will depend on your system keyboard layout.
3815    Limits the available character set but should provide the best
3816    compatibility.
3817
3818    If `_pause` is True (default), then an automatic sleep will be performed
3819    after the function finshes executing. The duration is set by the global
3820    variable `PAUSE`.
3821
3822    `auto_shift` is used internally by higher level functions to automatically
3823    press the shift key before supported scancodes (indicitated by a special
3824    bit outside the regular scancode range, while it technically can be used,
3825    it's not intended for public access).
3826
3827    ----------------------------------------------------------------------------
3828
3829    NOTE: `logScreenshot` is currently unsupported.
3830    """
3831    scancodes_sequence: ScancodeSequence
3832    if isinstance(scancodes, int):
3833        scancodes_sequence = ScancodeSequence([scancodes])
3834    else:
3835        scancodes_sequence = scancodes
3836
3837    keybdFlags: int = _KEYEVENTF_SCANCODE | _KEYEVENTF_KEYUP
3838    input_structs: list[_INPUT] = []
3839    extendedFlag: int
3840
3841    # Init event tracking
3842    insertedEvents: int = 0
3843    expectedEvents: int = 0
3844
3845    for scancode in scancodes_sequence:
3846        if auto_shift and scancode & _OFFSET_SHIFTKEY:
3847            input_structs += [
3848                _create_keyboard_input(
3849                    wScan=_SHIFT_SCANCODE, dwFlags=keybdFlags
3850                )
3851            ]
3852            expectedEvents += 1
3853
3854        scancode = scancode & 0xFFFF
3855
3856        extendedFlag = _KEYEVENTF_EXTENDEDKEY if scancode >= 0xE000 else 0
3857        input_structs += [
3858            _create_keyboard_input(
3859                wScan=scancode & 0xFFFF, dwFlags=keybdFlags | extendedFlag
3860            )
3861        ]
3862        expectedEvents += 1
3863
3864    insertedEvents += _send_input(input_structs)
3865    return insertedEvents == expectedEvents
3866    # --------------------------------------------------------------------------

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

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

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:
4039@_genericPyDirectInputChecks
4040def scancode_hotkey(
4041    *args: ScancodeTypes,
4042    interval: float = 0.0,
4043    wait: float = 0.0,
4044    logScreenshot: None = None,
4045    _pause: bool = True,
4046    auto_shift: bool = True,
4047) -> bool:
4048    """
4049    Press down buttons in order they are specified as arguments,
4050    releasing them in reverse order, e.g. 0x1D, 0x2E will first press
4051    Control, then C and release C before releasing Control.
4052
4053    Use keyword-only argument `interval` to specify a delay between single
4054    keys when pressing and releasing and `wait` for delay between last press
4055    and first release.
4056
4057    If `_pause` is True (default), then an automatic sleep will be performed
4058    after the function finshes executing. The duration is set by the global
4059    variable `PAUSE`.
4060    Be aware, that the global pause defined by the PAUSE `constant` only
4061    applies after every call to this function, not inbetween (no pause between
4062    press and releasing key)!
4063
4064    `auto_shift` is used internally by higher level functions to automatically
4065    press the shift key before supported scancodes (indicitated by a special
4066    bit outside the regular scancode range, while it technically can be used,
4067    it's not intended for public access).
4068
4069    ----------------------------------------------------------------------------
4070
4071    NOTE: `logScreenshot` is currently unsupported.
4072    """
4073    expectedPresses: int = len(args)
4074    downed: int = 0
4075    upped: int = 0
4076
4077    apply_interval: bool = False
4078    for code in args:
4079        if apply_interval:
4080            _sleep(interval)  # sleep between iterations
4081        apply_interval = True
4082
4083        downed += scancode_keyDown(code, _pause=False, auto_shift=auto_shift)
4084
4085    _sleep(wait)
4086
4087    apply_interval = False
4088    for code in reversed(args):
4089        if apply_interval:
4090            _sleep(interval)  # sleep between iterations
4091        apply_interval = True
4092
4093        upped += scancode_keyUp(code, _pause=False, auto_shift=auto_shift)
4094
4095    return expectedPresses == downed == upped
4096    # --------------------------------------------------------------------------

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:
4104def keyDown(
4105    key: str,
4106    logScreenshot: None = None,
4107    _pause: bool = True,
4108    *,
4109    auto_shift: bool = False,
4110) -> bool:
4111    """
4112    Press down key corresponding to key name `key`.
4113
4114    `key` will be interpreted as a keyboard key (US QWERTY).
4115    The actually pressed key will depend on your system keyboard layout.
4116    Limits the available character set but should provide the best
4117    compatibility.
4118
4119    If `_pause` is True (default), then an automatic sleep will be performed
4120    after the function finshes executing. The duration is set by the global
4121    variable `PAUSE`.
4122
4123    If `auto_shift` is True, then "shifted" characters like upper case letters
4124    and the symbols on the number row automatically insert a Shift scancode
4125    into the input sequence.
4126
4127    ----------------------------------------------------------------------------
4128
4129    NOTE: `logScreenshot` is currently unsupported.
4130    """
4131    scancode: ScancodeTypes | None = KEYBOARD_MAPPING.get(key)
4132    if scancode is None:
4133        return False
4134    return scancode_keyDown(
4135        scancode, logScreenshot, _pause=_pause, auto_shift=auto_shift
4136    )
4137    # --------------------------------------------------------------------------

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:
4142def keyUp(
4143    key: str,
4144    logScreenshot: None = None,
4145    _pause: bool = True,
4146    *,
4147    auto_shift: bool = False,
4148) -> bool:
4149    """
4150    Lift up key corresponding to key name `key`.
4151
4152    `key` will be interpreted as a keyboard key (US QWERTY).
4153    The actually lifted key will depend on your system keyboard layout.
4154    Limits the available character set but should provide the best
4155    compatibility.
4156
4157    If `_pause` is True (default), then an automatic sleep will be performed
4158    after the function finshes executing. The duration is set by the global
4159    variable `PAUSE`.
4160
4161    If `auto_shift` is True, then "shifted" characters like upper case letters
4162    and the symbols on the number row automatically insert a Shift scancode
4163    into the input sequence.
4164
4165    ----------------------------------------------------------------------------
4166
4167    NOTE: `logScreenshot` is currently unsupported.
4168    """
4169    scancode: ScancodeTypes | None = KEYBOARD_MAPPING.get(key)
4170    if scancode is None:
4171        return False
4172    return scancode_keyUp(
4173        scancode, logScreenshot, _pause=_pause, auto_shift=auto_shift
4174    )
4175    # --------------------------------------------------------------------------

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

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

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:
4338@_genericPyDirectInputChecks
4339def typewrite(
4340    message: str,
4341    interval: float = 0.0,
4342    logScreenshot: None = None,
4343    _pause: bool = True,
4344    *,
4345    auto_shift: bool = False,
4346    delay: float = 0.0,
4347    duration: float = 0.0,
4348) -> None:
4349    """
4350    Break down `message` into a single character key sequence and press each
4351    key one by one.
4352
4353    `message` will be interpreted as sequence of keyboard keys (US QWERTY).
4354    The actually pressed keys will depend on your system keyboard layout.
4355    Limits the available character set but should provide the best
4356    compatibility.
4357
4358    Explanation of time parameters (seconds as floating point numbers):
4359
4360    - `interval` is the time spent waiting between sequences. If `message` is a
4361    single character string, then `interval` will be ignored.
4362    - `delay` is the time from one complete key (press+release) to the next one
4363    in the same sequence. If there is only a single key in a sequence, then
4364    `delay` will be ignored.
4365    - `duration` is the time spent on holding every key before releasing it
4366    again.
4367
4368    If `_pause` is True (default), then an automatic sleep will be performed
4369    after the function finshes executing. The duration is set by the global
4370    variable `PAUSE`.
4371    Be aware, that the global pause defined by the PAUSE `constant` only
4372    applies after every call to this function, not inbetween (no pause between
4373    press and releasing key)!
4374
4375    `auto_shift` is used internally by higher level functions to automatically
4376    press the shift key before supported scancodes (indicitated by a special
4377    bit outside the regular scancode range, while it technically can be used,
4378    it's not intended for public access).
4379
4380    ----------------------------------------------------------------------------
4381
4382    NOTE: `logScreenshot` is currently unsupported.
4383    """
4384
4385    apply_interval: bool = False
4386    for key in message:
4387        if apply_interval:  # Don't delay first press
4388            _sleep(interval)
4389        apply_interval = True
4390
4391        press(
4392            key,
4393            _pause=False,
4394            auto_shift=auto_shift,
4395            delay=delay,
4396            duration=duration,
4397        )
4398    # --------------------------------------------------------------------------

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:
4338@_genericPyDirectInputChecks
4339def typewrite(
4340    message: str,
4341    interval: float = 0.0,
4342    logScreenshot: None = None,
4343    _pause: bool = True,
4344    *,
4345    auto_shift: bool = False,
4346    delay: float = 0.0,
4347    duration: float = 0.0,
4348) -> None:
4349    """
4350    Break down `message` into a single character key sequence and press each
4351    key one by one.
4352
4353    `message` will be interpreted as sequence of keyboard keys (US QWERTY).
4354    The actually pressed keys will depend on your system keyboard layout.
4355    Limits the available character set but should provide the best
4356    compatibility.
4357
4358    Explanation of time parameters (seconds as floating point numbers):
4359
4360    - `interval` is the time spent waiting between sequences. If `message` is a
4361    single character string, then `interval` will be ignored.
4362    - `delay` is the time from one complete key (press+release) to the next one
4363    in the same sequence. If there is only a single key in a sequence, then
4364    `delay` will be ignored.
4365    - `duration` is the time spent on holding every key before releasing it
4366    again.
4367
4368    If `_pause` is True (default), then an automatic sleep will be performed
4369    after the function finshes executing. The duration is set by the global
4370    variable `PAUSE`.
4371    Be aware, that the global pause defined by the PAUSE `constant` only
4372    applies after every call to this function, not inbetween (no pause between
4373    press and releasing key)!
4374
4375    `auto_shift` is used internally by higher level functions to automatically
4376    press the shift key before supported scancodes (indicitated by a special
4377    bit outside the regular scancode range, while it technically can be used,
4378    it's not intended for public access).
4379
4380    ----------------------------------------------------------------------------
4381
4382    NOTE: `logScreenshot` is currently unsupported.
4383    """
4384
4385    apply_interval: bool = False
4386    for key in message:
4387        if apply_interval:  # Don't delay first press
4388            _sleep(interval)
4389        apply_interval = True
4390
4391        press(
4392            key,
4393            _pause=False,
4394            auto_shift=auto_shift,
4395            delay=delay,
4396            duration=duration,
4397        )
4398    # --------------------------------------------------------------------------

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:
4409@_genericPyDirectInputChecks
4410def hotkey(
4411    *args: str,
4412    interval: float = 0.0,
4413    wait: float = 0.0,
4414    logScreenshot: None = None,
4415    _pause: bool = True,
4416    auto_shift: bool = True,
4417) -> None:
4418    """
4419    Press down buttons in order they are specified as arguments,
4420    releasing them in reverse order, e.g. 'ctrl', 'c' will first press
4421    Control, then C and release C before releasing Control.
4422
4423    Use keyword-only argument `interval` to specify a delay between single
4424    keys when pressing and releasing and `wait` for delay between last press
4425    and first release.
4426
4427    If `_pause` is True (default), then an automatic sleep will be performed
4428    after the function finshes executing. The duration is set by the global
4429    variable `PAUSE`.
4430    Be aware, that the global pause defined by the PAUSE `constant` only
4431    applies after every call to this function, not inbetween (no pause between
4432    press and releasing key)!
4433
4434    `auto_shift` is used internally by higher level functions to automatically
4435    press the shift key before supported scancodes (indicitated by a special
4436    bit outside the regular scancode range, while it technically can be used,
4437    it's not intended for public access).
4438
4439    ----------------------------------------------------------------------------
4440
4441    NOTE: `logScreenshot` is currently unsupported.
4442    """
4443    apply_interval: bool = False
4444    for key in args:
4445        if apply_interval:
4446            _sleep(interval)  # sleep between iterations
4447        apply_interval = True
4448
4449        keyDown(key, _pause=False, auto_shift=auto_shift)
4450
4451    _sleep(wait)
4452
4453    apply_interval = False
4454    for key in reversed(args):
4455        if apply_interval:
4456            _sleep(interval)  # sleep between iterations
4457        apply_interval = True
4458
4459        keyUp(key, _pause=False, auto_shift=auto_shift)
4460    # --------------------------------------------------------------------------

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:
4467@_genericPyDirectInputChecks
4468def unicode_charDown(
4469    char: str, logScreenshot: None = None, _pause: bool = True
4470) -> bool:
4471    """
4472    Send Unicode character(s) `char` to currently focused application as
4473    WM_KEYDOWN message.
4474
4475    `char` will be interpreted as a string of Unicode characters
4476    (independet from keyboard layout). Supports complete Unicode character set
4477    but may not be compatible with every application.
4478
4479    If `_pause` is True (default), then an automatic sleep will be performed
4480    after the function finshes executing. The duration is set by the global
4481    variable `PAUSE`.
4482
4483    ----------------------------------------------------------------------------
4484
4485    NOTE: `logScreenshot` is currently unsupported.
4486    """
4487    utf16surrogates: bytes = char.encode("utf-16be")
4488    codes: Sequence[int] = unpack(
4489        f">{len(utf16surrogates) // 2}H", utf16surrogates
4490    )
4491
4492    keybdFlags: int = _KEYEVENTF_UNICODE
4493
4494    input_structs: list[_INPUT] = [
4495        _create_keyboard_input(wVk=0, wScan=charcode, dwFlags=keybdFlags)
4496        for charcode in codes
4497    ]
4498    # Init event tracking
4499    expectedEvents: int = len(input_structs)
4500    insertedEvents: int = _send_input(input_structs)
4501
4502    return insertedEvents == expectedEvents
4503    # --------------------------------------------------------------------------

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:
4507@_genericPyDirectInputChecks
4508def unicode_charUp(
4509    char: str, logScreenshot: None = None, _pause: bool = True
4510) -> bool:
4511    """
4512    Send Unicode character(s) `char` to currently focused application as
4513    WM_KEYUP message.
4514
4515    `char` will be interpreted as a string of Unicode characters
4516    (independet from keyboard layout). Supports complete Unicode character set
4517    but may not be compatible with every application.
4518
4519    If `_pause` is True (default), then an automatic sleep will be performed
4520    after the function finshes executing. The duration is set by the global
4521    variable `PAUSE`.
4522
4523    ----------------------------------------------------------------------------
4524
4525    NOTE: `logScreenshot` is currently unsupported.
4526    """
4527    utf16surrogates: bytes = char.encode("utf-16be")
4528    codes: Sequence[int] = unpack(
4529        f">{len(utf16surrogates) // 2}H", utf16surrogates
4530    )
4531
4532    keybdFlags: int = _KEYEVENTF_UNICODE | _KEYEVENTF_KEYUP
4533
4534    input_structs: list[_INPUT] = [
4535        _create_keyboard_input(wVk=0, wScan=charcode, dwFlags=keybdFlags)
4536        for charcode in codes
4537    ]
4538    # Init event tracking
4539    expectedEvents: int = len(input_structs)
4540    insertedEvents: int = _send_input(input_structs)
4541
4542    return insertedEvents == expectedEvents
4543    # --------------------------------------------------------------------------

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

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]:
4636@contextmanager
4637@_genericPyDirectInputChecks
4638def unicode_hold(
4639    chars: str | Sequence[str],
4640    logScreenshot: None = None,
4641    _pause: bool = True,
4642    *,
4643    raise_on_failure: bool = False,
4644) -> Generator[None, None, None]:
4645    """
4646    Hold the sequence of "keys" corresponding to unicode characters in `chars`
4647    as long as the context manager is in scope (press upon entry,
4648    release upon exit).
4649
4650    `chars` will be interpreted as a sequence of Unicode characters
4651    (independet from keyboard layout). Supports complete Unicode character set
4652    but may not be compatible with every application.
4653
4654    Keys will be released in reverse order (LIFO), but still practically
4655    instantenous.
4656
4657    If `_pause` is True (default), then an automatic sleep will be performed
4658    after the function finshes executing. The duration is set by the global
4659    variable `PAUSE`.
4660    Be aware, that the global pause defined by the PAUSE `constant` only
4661    applies  after every call to this function, not inbetween (no pause between
4662    press and releasing key)!
4663
4664    If `raise_on_failure` is True, then `PriorInputFailedException` will be
4665    raised if not all keyboard inputs could be executed successfully.
4666
4667    ----------------------------------------------------------------------------
4668
4669    NOTE: `logScreenshot` is currently unsupported.
4670    """
4671    if isinstance(chars, str):
4672        chars = [chars]  # make single element into iterable
4673
4674    expectedPresses: int = len(chars)
4675    downed: int = 0
4676    upped: int = 0
4677
4678    try:
4679        for c in chars:
4680            downed += unicode_charDown(c, _pause=False)
4681        yield
4682    finally:
4683        for c in reversed(chars):
4684            upped += unicode_charUp(c, _pause=False)
4685        if raise_on_failure and not (expectedPresses == downed == upped):
4686            raise PriorInputFailedException
4687    # --------------------------------------------------------------------------

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:
4691@_genericPyDirectInputChecks
4692def unicode_typewrite(
4693    message: str,
4694    interval: float = 0.0,
4695    logScreenshot: None = None,
4696    _pause: bool = True,
4697    *,
4698    delay: float = 0.0,
4699    duration: float = 0.0,
4700) -> None:
4701    """
4702    Break down `message` into characters and press them one by one.
4703
4704    `message` will be interpreted as a sequence of Unicode characters
4705    (independet from keyboard layout). Supports complete Unicode character set
4706    but may not be compatible with every application.
4707
4708    Explanation of time parameters (seconds as floating point numbers):
4709
4710    - `interval` is the time spent waiting between sequences. If `message` is a
4711    single character string, then `interval` will be ignored.
4712    - `delay` is the time from one complete key (press+release) to the next one
4713    in the same sequence. If there is only a single key in a sequence, then
4714    `delay` will be ignored.
4715    - `duration` is the time spent on holding every key before releasing it
4716    again.
4717
4718    If `_pause` is True (default), then an automatic sleep will be performed
4719    after the function finshes executing. The duration is set by the global
4720    variable `PAUSE`.
4721    Be aware, that the global pause defined by the PAUSE `constant` only
4722    applies after every call to this function, not inbetween (no pause between
4723    press and releasing key)!
4724
4725    ----------------------------------------------------------------------------
4726
4727    NOTE: `logScreenshot` is currently unsupported.
4728    """
4729    apply_interval: bool = False
4730    for char in message:
4731        if apply_interval:
4732            _sleep(interval)  # sleep between iterations
4733        apply_interval = True
4734
4735        unicode_press(char, _pause=False, delay=delay, duration=duration)
4736    # --------------------------------------------------------------------------

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:
4691@_genericPyDirectInputChecks
4692def unicode_typewrite(
4693    message: str,
4694    interval: float = 0.0,
4695    logScreenshot: None = None,
4696    _pause: bool = True,
4697    *,
4698    delay: float = 0.0,
4699    duration: float = 0.0,
4700) -> None:
4701    """
4702    Break down `message` into characters and press them one by one.
4703
4704    `message` will be interpreted as a sequence of Unicode characters
4705    (independet from keyboard layout). Supports complete Unicode character set
4706    but may not be compatible with every application.
4707
4708    Explanation of time parameters (seconds as floating point numbers):
4709
4710    - `interval` is the time spent waiting between sequences. If `message` is a
4711    single character string, then `interval` will be ignored.
4712    - `delay` is the time from one complete key (press+release) to the next one
4713    in the same sequence. If there is only a single key in a sequence, then
4714    `delay` will be ignored.
4715    - `duration` is the time spent on holding every key before releasing it
4716    again.
4717
4718    If `_pause` is True (default), then an automatic sleep will be performed
4719    after the function finshes executing. The duration is set by the global
4720    variable `PAUSE`.
4721    Be aware, that the global pause defined by the PAUSE `constant` only
4722    applies after every call to this function, not inbetween (no pause between
4723    press and releasing key)!
4724
4725    ----------------------------------------------------------------------------
4726
4727    NOTE: `logScreenshot` is currently unsupported.
4728    """
4729    apply_interval: bool = False
4730    for char in message:
4731        if apply_interval:
4732            _sleep(interval)  # sleep between iterations
4733        apply_interval = True
4734
4735        unicode_press(char, _pause=False, delay=delay, duration=duration)
4736    # --------------------------------------------------------------------------

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:
4745@_genericPyDirectInputChecks
4746def unicode_hotkey(
4747    *args: str,
4748    interval: float = 0.0,
4749    wait: float = 0.0,
4750    logScreenshot: None = None,
4751    _pause: bool = True,
4752) -> None:
4753    """
4754    Press down buttons in order they are specified as arguments,
4755    releasing them in reverse order.
4756
4757    This function makes little sense for Unicode characters and mainly exists
4758    for parity with the other, lower-level hotkey functions!
4759
4760    See `unicode_press()` for an alternative function that presses keys in
4761    series instead.
4762
4763    Use keyword-only argument `interval` to specify a delay between single
4764    keys when pressing and releasing and `wait` for delay between last press
4765    and first release.
4766
4767    If `_pause` is True (default), then an automatic sleep will be performed
4768    after the function finshes executing. The duration is set by the global
4769    variable `PAUSE`.
4770    Be aware, that the global pause defined by the PAUSE `constant` only
4771    applies after every call to this function, not inbetween (no pause between
4772    press and releasing key)!
4773
4774    ----------------------------------------------------------------------------
4775
4776    NOTE: `logScreenshot` is currently unsupported.
4777    """
4778    apply_interval: bool = False
4779    for char in args:
4780        if apply_interval:
4781            _sleep(interval)  # sleep between iterations
4782        apply_interval = True
4783
4784        unicode_charDown(char, _pause=False)
4785
4786    _sleep(wait)
4787
4788    apply_interval = False
4789    for char in reversed(args):
4790        if apply_interval:
4791            _sleep(interval)  # sleep between iterations
4792        apply_interval = True
4793
4794        unicode_charUp(char, _pause=False)
4795    # --------------------------------------------------------------------------

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.