pydirectinput

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

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

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

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

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

PAUSE: float | None = 0.01

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

Set to None to disable automatic pauses entirely.

MINIMUM_SLEEP_IDEAL: float = 1e-06

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

MINIMUM_SLEEP_ACTUAL: float = 0.002

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

MOUSE_LEFT: str = 'left'

Name of left mouse button

MOUSE_MIDDLE: str = 'middle'

Name of middle mouse button

MOUSE_RIGHT: str = 'right'

Name of right mouse button

MOUSE_PRIMARY: str = 'primary'

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

MOUSE_SECONDARY: str = 'secondary'

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

MOUSE_BUTTON4: str = 'mouse4'

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

MOUSE_X1: str = 'x1'

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

MOUSE_BUTTON5: str = 'mouse5'

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

MOUSE_X2: str = 'x2'

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

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

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

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

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

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

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

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

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

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

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

ScancodeTypes: TypeAlias = 'int | ScancodeSequence'

Acceptable value types in KEYBOARD_MAPPING.

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

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

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

Not intended to be changed at runtime!

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

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

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

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

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

Raised when _failSafeCheck detects failsafe mouse position.

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

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

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

Return a postion tuple (x, y).

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Press down mouse button button.

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

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

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

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

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


NOTE: logScreenshot is currently unsupported.

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

Lift up mouse button button.

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

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

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

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

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


NOTE: logScreenshot is currently unsupported.

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

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

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

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

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

interval is the wait time in seconds between clicks.

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

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

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


NOTE: logScreenshot is currently unsupported.

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

Click Left Mouse button.

See click() for more information

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

Click Right Mouse button.

See click() for more information

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

Click Middle Mouse button.

See click() for more information

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

Double click button.

See click() for more information

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

Triple click button.

See click() for more information

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

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

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

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

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


NOTE: logScreenshot is currently unsupported.

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

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

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

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

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


NOTE: logScreenshot is currently unsupported.

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

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

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

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

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


NOTE: logScreenshot is currently unsupported.

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

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

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

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

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

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

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:
3733def is_valid_key(key: str) -> bool:
3734    """
3735    Returns true if key name `key` can be translated into a valid scan code.
3736    """
3737    return key in KEYBOARD_MAPPING

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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.