diff --git a/.github/ISSUE_TEMPLATE/00_bug.yaml b/.github/ISSUE_TEMPLATE/00_bug.yaml new file mode 100644 index 0000000..0394b23 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/00_bug.yaml @@ -0,0 +1,60 @@ +name: bug report +description: something went wrong +body: + - type: markdown + attributes: + value: | + Please use this issue template to report bug behavior + + - type: textarea + id: what-happened + attributes: + label: describe your issue + description: Please describe the problem, the expected behavior, and the actual behavior + placeholder: | + I was doing ... + I ran ... + I expected ... + I got ... + validations: + required: true + - type: input + id: library-version + attributes: + label: ahk.__version__ + placeholder: 1.x.x + validations: + required: false + - type: input + id: ahk-version + attributes: + label: AutoHotkey version + placeholder: v1 or v2 + validations: + required: false + - type: textarea + id: code + attributes: + label: Code to reproduce the issue + description: Minimal Python code that can be used to reproduce the issue. (no backticks needed) + placeholder: | + from ahk import AHK + ahk = AHK() + ahk.do_something() + render: python + validations: + required: false + - type: textarea + id: error-log + attributes: + label: 'Traceback/Error message' + description: The full traceback/error you receive or other error information, if applicable + placeholder: | + Traceback (most recent call last): + File "C:\path\to\yourscript.py", line 3, in + ahk.failure() + File "C:\path\to\site-packages\ahk\_sync\engine.py", line 220, in __getattr__ + raise AttributeError(f'{self.__class__.__name__!r} object has no attribute {name!r}') + AttributeError: 'AHK' object has no attribute 'failure' + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/01_feature.yaml b/.github/ISSUE_TEMPLATE/01_feature.yaml new file mode 100644 index 0000000..4076969 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/01_feature.yaml @@ -0,0 +1,33 @@ +name: feature request +description: something new +body: + - type: markdown + attributes: + value: | + Use this form to create feature requests + + - type: checkboxes + attributes: + label: Checked the documentation + description: | + The documentation contains information about features that are already implemented. Please check this first before making a request. + (requests for features marked as "Not Implemented" in the documentation are OK, but please provide context on how you want to use this feature). + options: + - label: I have checked [the documentation](https://ahk.readthedocs.io/en/latest/api/methods.html) for the feature I am requesting + required: true + + + - type: textarea + id: freeform + attributes: + label: describe your feature request + placeholder: | + I want to do ... + I tried ... + It does not work because ... + + This feature would be useful because ... + + Additional information can be found at https://www.autohotkey.com/docs/ ... + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..933290b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: +- name: documentation + url: https://ahk.readthedocs.io/en/latest/ + about: See the full documentation diff --git a/ahk/_async/engine.py b/ahk/_async/engine.py index b7dde23..0713787 100644 --- a/ahk/_async/engine.py +++ b/ahk/_async/engine.py @@ -109,6 +109,8 @@ ], ] +SendMode: TypeAlias = Literal['Event', 'Input', 'InputThenPlay', 'Play', ''] + AsyncPropertyReturnTupleIntInt: TypeAlias = Coroutine[None, None, Tuple[int, int]] # unasync: remove SyncPropertyReturnTupleIntInt: TypeAlias = Tuple[int, int] @@ -372,6 +374,18 @@ async def get_coord_mode(self, target: CoordModeTargets) -> str: resp = await self._transport.function_call('AHKGetCoordMode', args) return resp + async def set_send_mode(self, mode: SendMode) -> None: + """ + Analog for `SendMode `_ + """ + args = [str(mode)] + await self._transport.function_call('AHKSetSendMode', args) + return None + + async def get_send_mode(self) -> str: + resp = await self._transport.function_call('AHKGetSendMode') + return resp + # fmt: off @overload async def control_click(self, button: Literal['L', 'R', 'M', 'LEFT', 'RIGHT', 'MIDDLE'] = 'L', click_count: int = 1, options: str = '', control: str = '', title: str = '', text: str = '', exclude_title: str = '', exclude_text: str = '', *, title_match_mode: Optional[TitleMatchMode] = None, detect_hidden_windows: Optional[bool] = None) -> None: ... @@ -769,13 +783,13 @@ def mouse_position(self, new_position: Tuple[int, int]) -> None: # fmt: off @overload - async def mouse_move(self, x: Optional[Union[str, int]] = None, y: Optional[Union[str, int]] = None, *, speed: Optional[int] = None, relative: bool = False) -> None: ... + async def mouse_move(self, x: Optional[Union[str, int]] = None, y: Optional[Union[str, int]] = None, *, speed: Optional[int] = None, relative: bool = False, send_mode: Optional[SendMode] = None) -> None: ... @overload - async def mouse_move(self, x: Optional[Union[str, int]] = None, y: Optional[Union[str, int]] = None, *, blocking: Literal[True], speed: Optional[int] = None, relative: bool = False) -> None: ... + async def mouse_move(self, x: Optional[Union[str, int]] = None, y: Optional[Union[str, int]] = None, *, blocking: Literal[True], speed: Optional[int] = None, relative: bool = False, send_mode: Optional[SendMode] = None) -> None: ... @overload - async def mouse_move(self, x: Optional[Union[str, int]] = None, y: Optional[Union[str, int]] = None, *, blocking: Literal[False], speed: Optional[int] = None, relative: bool = False, ) -> AsyncFutureResult[None]: ... + async def mouse_move(self, x: Optional[Union[str, int]] = None, y: Optional[Union[str, int]] = None, *, blocking: Literal[False], speed: Optional[int] = None, relative: bool = False, send_mode: Optional[SendMode] = None) -> AsyncFutureResult[None]: ... @overload - async def mouse_move(self, x: Optional[Union[str, int]] = None, y: Optional[Union[str, int]] = None, *, speed: Optional[int] = None, relative: bool = False, blocking: bool = True) -> Union[None, AsyncFutureResult[None]]: ... + async def mouse_move(self, x: Optional[Union[str, int]] = None, y: Optional[Union[str, int]] = None, *, speed: Optional[int] = None, relative: bool = False, blocking: bool = True, send_mode: Optional[SendMode] = None) -> Union[None, AsyncFutureResult[None]]: ... # fmt: on async def mouse_move( self, @@ -784,6 +798,7 @@ async def mouse_move( *, speed: Optional[int] = None, relative: bool = False, + send_mode: Optional[SendMode] = None, blocking: bool = True, ) -> Union[None, AsyncFutureResult[None]]: """ @@ -804,6 +819,11 @@ async def mouse_move( args.append('R') else: args.append('') + if send_mode: + args.append(send_mode) + else: + args.append('') + resp = await self._transport.function_call('AHKMouseMove', args, blocking=blocking) return resp @@ -1109,13 +1129,13 @@ async def key_up(self, key: Union[str, Key], blocking: bool = True) -> Union[Non # fmt: off @overload - async def key_wait(self, key_name: str, *, timeout: Optional[int] = None, logical_state: bool = False, released: bool = False) -> int: ... + async def key_wait(self, key_name: str, *, timeout: Optional[int] = None, logical_state: bool = False, released: bool = False) -> bool: ... @overload - async def key_wait(self, key_name: str, *, blocking: Literal[True], timeout: Optional[int] = None, logical_state: bool = False, released: bool = False) -> int: ... + async def key_wait(self, key_name: str, *, blocking: Literal[True], timeout: Optional[int] = None, logical_state: bool = False, released: bool = False) -> bool: ... @overload - async def key_wait(self, key_name: str, *, blocking: Literal[False], timeout: Optional[int] = None, logical_state: bool = False, released: bool = False) -> AsyncFutureResult[int]: ... + async def key_wait(self, key_name: str, *, blocking: Literal[False], timeout: Optional[int] = None, logical_state: bool = False, released: bool = False) -> AsyncFutureResult[bool]: ... @overload - async def key_wait(self, key_name: str, *, timeout: Optional[int] = None, logical_state: bool = False, released: bool = False, blocking: bool = True) -> Union[int, AsyncFutureResult[int]]: ... + async def key_wait(self, key_name: str, *, timeout: Optional[int] = None, logical_state: bool = False, released: bool = False, blocking: bool = True) -> Union[bool, AsyncFutureResult[bool]]: ... # fmt: on async def key_wait( self, @@ -1125,7 +1145,7 @@ async def key_wait( logical_state: bool = False, released: bool = False, blocking: bool = True, - ) -> Union[int, AsyncFutureResult[int]]: + ) -> Union[bool, AsyncFutureResult[bool]]: """ Analog for `KeyWait `_ """ @@ -1134,11 +1154,10 @@ async def key_wait( options += 'D' if logical_state: options += 'L' - if timeout: + if timeout is not None: + assert timeout >= 0, 'Timeout value must be non-negative' options += f'T{timeout}' - args = [key_name] - if options: - args.append(options) + args = [key_name, options] resp = await self._transport.function_call('AHKKeyWait', args, blocking=blocking) return resp @@ -1173,13 +1192,13 @@ async def get_send_level(self) -> int: # fmt: off @overload - async def send(self, s: str, *, raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None) -> None: ... + async def send(self, s: str, *, raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None, send_mode: Optional[SendMode] = None) -> None: ... @overload - async def send(self, s: str, *, raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None, blocking: Literal[True]) -> None: ... + async def send(self, s: str, *, raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None, send_mode: Optional[SendMode] = None, blocking: Literal[True]) -> None: ... @overload - async def send(self, s: str, *, raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None, blocking: Literal[False]) -> AsyncFutureResult[None]: ... + async def send(self, s: str, *, raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None, send_mode: Optional[SendMode] = None, blocking: Literal[False]) -> AsyncFutureResult[None]: ... @overload - async def send(self, s: str, *, raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None, blocking: bool = True) -> Union[None, AsyncFutureResult[None]]: ... + async def send(self, s: str, *, raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None, send_mode: Optional[SendMode] = None, blocking: bool = True) -> Union[None, AsyncFutureResult[None]]: ... # fmt: on async def send( self, @@ -1188,6 +1207,7 @@ async def send( raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None, + send_mode: Optional[SendMode] = None, blocking: bool = True, ) -> Union[None, AsyncFutureResult[None]]: """ @@ -1202,6 +1222,10 @@ async def send( args.append(str(key_press_duration)) else: args.append('') + if send_mode: + args.append(send_mode) + else: + args.append('') if raw: raw_resp = await self._transport.function_call('AHKSendRaw', args=args, blocking=blocking) @@ -2754,13 +2778,13 @@ async def win_set_trans_color( # fmt: off @overload - async def right_click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, coord_mode: Optional[CoordModeRelativeTo] = None) -> None: ... + async def right_click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> None: ... @overload - async def right_click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: Literal[True], coord_mode: Optional[CoordModeRelativeTo] = None) -> None: ... + async def right_click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: Literal[True], coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> None: ... @overload - async def right_click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: Literal[False], coord_mode: Optional[CoordModeRelativeTo] = None) -> AsyncFutureResult[None]: ... + async def right_click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: Literal[False], coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> AsyncFutureResult[None]: ... @overload - async def right_click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: bool = True, coord_mode: Optional[CoordModeRelativeTo] = None) -> Union[None, AsyncFutureResult[None]]: ... + async def right_click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: bool = True, coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> Union[None, AsyncFutureResult[None]]: ... # fmt: on async def right_click( self, @@ -2772,6 +2796,7 @@ async def right_click( relative: Optional[bool] = None, blocking: bool = True, coord_mode: Optional[CoordModeRelativeTo] = None, + send_mode: Optional[SendMode] = None, ) -> Union[None, AsyncFutureResult[None]]: button = 'R' return await self.click( @@ -2783,17 +2808,18 @@ async def right_click( relative=relative, blocking=blocking, coord_mode=coord_mode, + send_mode=send_mode, ) # fmt: off @overload - async def click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, coord_mode: Optional[CoordModeRelativeTo] = None) -> None: ... + async def click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> None: ... @overload - async def click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: Literal[True], coord_mode: Optional[CoordModeRelativeTo] = None) -> None: ... + async def click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: Literal[True], coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> None: ... @overload - async def click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: Literal[False], coord_mode: Optional[CoordModeRelativeTo] = None) -> AsyncFutureResult[None]: ... + async def click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: Literal[False], coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> AsyncFutureResult[None]: ... @overload - async def click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: bool = True, coord_mode: Optional[CoordModeRelativeTo] = None) -> Union[None, AsyncFutureResult[None]]: ... + async def click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: bool = True, coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> Union[None, AsyncFutureResult[None]]: ... # fmt: on async def click( self, @@ -2806,6 +2832,7 @@ async def click( relative: Optional[bool] = None, blocking: bool = True, coord_mode: Optional[CoordModeRelativeTo] = None, + send_mode: Optional[SendMode] = None, ) -> Union[None, AsyncFutureResult[None]]: """ Analog for `Click `_ @@ -2825,7 +2852,9 @@ async def click( r = '' if coord_mode is None: coord_mode = '' - args = [str(x), str(y), button, str(click_count), direction or '', r, coord_mode] + if send_mode is None: + send_mode = '' + args = [str(x), str(y), button, str(click_count), direction or '', r, coord_mode, str(send_mode)] resp = await self._transport.function_call('AHKClick', args, blocking=blocking) return resp @@ -2894,6 +2923,16 @@ async def image_search( resp = await self._transport.function_call('AHKImageSearch', args, blocking=blocking) return resp + # fmt: off + @overload + async def mouse_drag(self, x: int, y: int, *, from_position: Optional[Tuple[int, int]] = None, speed: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, relative: Optional[bool] = None, coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> None: ... + @overload + async def mouse_drag(self, x: int, y: int, *, from_position: Optional[Tuple[int, int]] = None, speed: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, relative: Optional[bool] = None, coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None, blocking: Literal[False]) -> AsyncFutureResult[None]: ... + @overload + async def mouse_drag(self, x: int, y: int, *, from_position: Optional[Tuple[int, int]] = None, speed: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, relative: Optional[bool] = None, coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None, blocking: Literal[True]) -> None: ... + @overload + async def mouse_drag(self, x: int, y: int, *, from_position: Optional[Tuple[int, int]] = None, speed: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, relative: Optional[bool] = None, blocking: bool = True, coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> Union[None, AsyncFutureResult[None]]: ... + # fmt: on async def mouse_drag( self, x: int, @@ -2901,14 +2940,19 @@ async def mouse_drag( *, from_position: Optional[Tuple[int, int]] = None, speed: Optional[int] = None, - button: MouseButton = 'left', + button: Optional[Union[MouseButton, str]] = None, relative: Optional[bool] = None, blocking: bool = True, coord_mode: Optional[CoordModeRelativeTo] = None, - ) -> None: + send_mode: Optional[SendMode] = None, + ) -> Union[None, AsyncFutureResult[None]]: """ Analog for `MouseClickDrag `_ """ + if button is None: + button = 'Left' + else: + button = _resolve_button(button) if from_position: x1, y1 = from_position args = [str(button), str(x1), str(y1), str(x), str(y)] @@ -2930,7 +2974,13 @@ async def mouse_drag( else: args.append('') - await self._transport.function_call('AHKMouseClickDrag', args, blocking=blocking) + if send_mode: + args.append(send_mode) + else: + args.append('') + + resp = await self._transport.function_call('AHKMouseClickDrag', args, blocking=blocking) + return resp # fmt: off @overload diff --git a/ahk/_async/transport.py b/ahk/_async/transport.py index 1501b4c..6aa7ce0 100644 --- a/ahk/_async/transport.py +++ b/ahk/_async/transport.py @@ -95,6 +95,7 @@ def result(self, timeout: Optional[float] = None) -> T_SyncFuture: 'AHKGetClipboardAll', 'AHKGetCoordMode', 'AHKGetSendLevel', + 'AHKGetSendMode', 'AHKGetTitleMatchMode', 'AHKGetTitleMatchSpeed', 'AHKGetVolume', @@ -125,6 +126,7 @@ def result(self, timeout: Optional[float] = None) -> T_SyncFuture: 'AHKSetCoordMode', 'AHKSetDetectHiddenWindows', 'AHKSetSendLevel', + 'AHKSetSendMode', 'AHKSetTitleMatchMode', 'AHKSetVolume', 'AHKShowToolTip', @@ -422,7 +424,7 @@ async def function_call(self, function_name: Literal['AHKClick'], args: Optional @overload async def function_call(self, function_name: Literal['AHKMouseClickDrag'], args: Optional[List[str]] = None, *, blocking: bool = True, engine: Optional[AsyncAHK[Any]] = None) -> Union[None, AsyncFutureResult[None]]: ... @overload - async def function_call(self, function_name: Literal['AHKKeyWait'], args: Optional[List[str]] = None, *, blocking: bool = True, engine: Optional[AsyncAHK[Any]] = None) -> Union[int, AsyncFutureResult[int]]: ... + async def function_call(self, function_name: Literal['AHKKeyWait'], args: Optional[List[str]] = None, *, blocking: bool = True, engine: Optional[AsyncAHK[Any]] = None) -> Union[bool, AsyncFutureResult[bool]]: ... @overload async def function_call(self, function_name: Literal['SetKeyDelay'], args: Optional[List[str]] = None, *, blocking: bool = True, engine: Optional[AsyncAHK[Any]] = None) -> Union[None, AsyncFutureResult[None]]: ... @overload @@ -540,6 +542,10 @@ async def function_call(self, function_name: Literal['AHKSetCoordMode'], args: L @overload async def function_call(self, function_name: Literal['AHKGetSendLevel']) -> int: ... @overload + async def function_call(self, function_name: Literal['AHKSetSendMode'], args: List[str]) -> None: ... + @overload + async def function_call(self, function_name: Literal['AHKGetSendMode']) -> str: ... + @overload async def function_call(self, function_name: Literal['AHKSetSendLevel'], args: List[str]) -> None: ... @overload async def function_call(self, function_name: Literal['AHKWinWait'], args: Optional[List[str]] = None, *, blocking: bool = True, engine: Optional[AsyncAHK[Any]] = None) -> Union[AsyncWindow, AsyncFutureResult[AsyncWindow]]: ... diff --git a/ahk/_constants.py b/ahk/_constants.py index 4158936..c4b2c90 100644 --- a/ahk/_constants.py +++ b/ahk/_constants.py @@ -1728,14 +1728,25 @@ direction := args[5] r := args[6] relative_to := args[7] + send_mode := args[8] current_coord_rel := Format("{}", A_CoordModeMouse) + current_send_mode := Format("{}", A_SendMode) + + if (send_mode != "") { + SendMode, %send_mode% + } if (relative_to != "") { CoordMode, Mouse, %relative_to% } + Click, %x%, %y%, %button%, %direction%, %r% + if (send_mode != "") { + SendMode, %current_send_mode% + } + if (relative_to != "") { CoordMode, Mouse, %current_coord_rel% } @@ -1779,6 +1790,18 @@ {% endblock AHKSetCoordMode %} } +AHKGetSendMode(args*) { + return FormatResponse("ahk.message.StringResponseMessage", A_SendMode) +} + + +AHKSetSendMode(args*) { + mode := args[1] + SendMode, %mode% + return FormatNoValueResponse() +} + + AHKMouseClickDrag(args*) { {% block AHKMouseClickDrag %} button := args[1] @@ -1789,6 +1812,11 @@ speed := args[6] relative := args[7] relative_to := args[8] + send_mode := args[8] + current_send_mode := Format("{}", A_SendMode) + if (send_mode != "") { + SendMode, %send_mode% + } current_coord_rel := Format("{}", A_CoordModeMouse) @@ -1802,6 +1830,10 @@ CoordMode, Mouse, %current_coord_rel% } + if (send_mode != "") { + SendMode, %current_send_mode% + } + return FormatNoValueResponse() {% endblock AHKMouseClickDrag %} @@ -1859,13 +1891,24 @@ {% block AHKKeyWait %} keyname := args[1] - if (args.Length() = 2) { + options := args[2] + + if (options = "") { KeyWait,% keyname } else { - options := args[2] KeyWait,% keyname,% options } - return FormatResponse("ahk.message.IntegerResponseMessage", ErrorLevel) + ret := ErrorLevel + + if (ret = 1) { + return FormatResponse("ahk.message.BooleanResponseMessage", 0) + } else if (ret = 0) { + return FormatResponse("ahk.message.BooleanResponseMessage", 1) + } else { + ; Unclear if this is even reachable + return FormatResponse("ahk.message.ExceptionResponseMessage", Format("There was a problem. ErrorLevel: {}", ret)) + } + {% endblock AHKKeyWait %} } @@ -1880,18 +1923,29 @@ str := args[1] key_delay := args[2] key_press_duration := args[3] + send_mode := args[4] current_delay := Format("{}", A_KeyDelay) current_key_duration := Format("{}", A_KeyDuration) + current_send_mode := Format("{}", A_SendMode) if (key_delay != "" or key_press_duration != "") { SetKeyDelay, %key_delay%, %key_press_duration% } + if (send_mode != "") { + SendMode, %send_mode% + } + Send,% str if (key_delay != "" or key_press_duration != "") { SetKeyDelay, %current_delay%, %current_key_duration% } + + if (send_mode != "") { + SendMode, %current_send_mode% + } + return FormatNoValueResponse() {% endblock AHKSend %} } @@ -1901,18 +1955,30 @@ str := args[1] key_delay := args[2] key_press_duration := args[3] + send_mode := args[4] current_delay := Format("{}", A_KeyDelay) current_key_duration := Format("{}", A_KeyDuration) + current_send_mode := Format("{}", A_SendMode) + if (key_delay != "" or key_press_duration != "") { SetKeyDelay, %key_delay%, %key_press_duration% } + if (send_mode != "") { + SendMode, %send_mode% + } + SendRaw,% str if (key_delay != "" or key_press_duration != "") { SetKeyDelay, %current_delay%, %current_key_duration% } + + if (send_mode != "") { + SendMode, %current_send_mode% + } + return FormatNoValueResponse() {% endblock AHKSendRaw %} } @@ -4728,11 +4794,23 @@ y := args[2] speed := args[3] relative := args[4] + send_mode := args[5] + current_send_mode := Format("{}", A_SendMode) + + if (send_mode != "") { + SendMode send_mode + } + if (relative != "") { - MouseMove(x, y, speed, "R") + MouseMove(x, y, speed, "R") } else { - MouseMove(x, y, speed) + MouseMove(x, y, speed) + } + + if (send_mode != "") { + SendMode current_send_mode } + resp := FormatNoValueResponse() return resp {% endblock AHKMouseMove %} @@ -4747,7 +4825,13 @@ direction := args[5] r := args[6] relative_to := args[7] + send_mode := args[8] current_coord_rel := Format("{}", A_CoordModeMouse) + current_send_mode := Format("{}", A_SendMode) + + if (send_mode != "") { + SendMode send_mode + } if (relative_to != "") { CoordMode("Mouse", relative_to) @@ -4759,6 +4843,9 @@ CoordMode("Mouse", current_coord_rel) } + if (send_mode != "") { + SendMode current_send_mode + } return FormatNoValueResponse() {% endblock AHKClick %} @@ -4798,6 +4885,19 @@ {% endblock AHKSetCoordMode %} } + +AHKGetSendMode(args*) { + return FormatResponse("ahk.message.StringResponseMessage", A_SendMode) +} + + +AHKSetSendMode(args*) { + mode := args[1] + SendMode mode + return FormatNoValueResponse() +} + + AHKMouseClickDrag(args*) { {% block AHKMouseClickDrag %} button := args[1] @@ -4808,8 +4908,13 @@ speed := args[6] relative := args[7] relative_to := args[8] - + send_mode := args[9] current_coord_rel := Format("{}", A_CoordModeMouse) + current_send_mode := Format("{}", A_SendMode) + + if (send_mode != "") { + SendMode send_mode + } if (relative_to != "") { CoordMode("Mouse", relative_to) @@ -4831,6 +4936,10 @@ CoordMode("Mouse", current_coord_rel) } + if (send_mode != "") { + SendMode current_send_mode + } + return FormatNoValueResponse() {% endblock AHKMouseClickDrag %} @@ -4883,13 +4992,19 @@ {% block AHKKeyWait %} keyname := args[1] - if (args.Length = 2) { + options := args[2] + + if (options = "") { ret := KeyWait(keyname) } else { - options := args[2] ret := KeyWait(keyname, options) } - return FormatResponse("ahk.message.IntegerResponseMessage", ret) + + if (ret = 0) { + return FormatResponse("ahk.message.BooleanResponseMessage", 0) + } else { + return FormatResponse("ahk.message.BooleanResponseMessage", 1) + } {% endblock AHKKeyWait %} } @@ -4904,8 +5019,14 @@ str := args[1] key_delay := args[2] key_press_duration := args[3] + send_mode := args[4] current_delay := Format("{}", A_KeyDelay) current_key_duration := Format("{}", A_KeyDuration) + current_send_mode := Format("{}", A_SendMode) + + if (send_mode != "") { + SendMode send_mode + } if (key_delay != "" or key_press_duration != "") { SetKeyDelay(key_delay, key_press_duration) @@ -4914,6 +5035,10 @@ Send(str) + if (send_mode != "") { + SendMode current_send_mode + } + if (key_delay != "" or key_press_duration != "") { SetKeyDelay(current_delay, current_key_duration) } diff --git a/ahk/_sync/engine.py b/ahk/_sync/engine.py index d383932..af5f0fc 100644 --- a/ahk/_sync/engine.py +++ b/ahk/_sync/engine.py @@ -107,6 +107,8 @@ ], ] +SendMode: TypeAlias = Literal['Event', 'Input', 'InputThenPlay', 'Play', ''] + SyncPropertyReturnTupleIntInt: TypeAlias = Tuple[int, int] SyncPropertyReturnOptionalAsyncWindow: TypeAlias = Optional[Window] @@ -367,6 +369,18 @@ def get_coord_mode(self, target: CoordModeTargets) -> str: resp = self._transport.function_call('AHKGetCoordMode', args) return resp + def set_send_mode(self, mode: SendMode) -> None: + """ + Analog for `SendMode `_ + """ + args = [str(mode)] + self._transport.function_call('AHKSetSendMode', args) + return None + + def get_send_mode(self) -> str: + resp = self._transport.function_call('AHKGetSendMode') + return resp + # fmt: off @overload def control_click(self, button: Literal['L', 'R', 'M', 'LEFT', 'RIGHT', 'MIDDLE'] = 'L', click_count: int = 1, options: str = '', control: str = '', title: str = '', text: str = '', exclude_title: str = '', exclude_text: str = '', *, title_match_mode: Optional[TitleMatchMode] = None, detect_hidden_windows: Optional[bool] = None) -> None: ... @@ -760,13 +774,13 @@ def mouse_position(self, new_position: Tuple[int, int]) -> None: # fmt: off @overload - def mouse_move(self, x: Optional[Union[str, int]] = None, y: Optional[Union[str, int]] = None, *, speed: Optional[int] = None, relative: bool = False) -> None: ... + def mouse_move(self, x: Optional[Union[str, int]] = None, y: Optional[Union[str, int]] = None, *, speed: Optional[int] = None, relative: bool = False, send_mode: Optional[SendMode] = None) -> None: ... @overload - def mouse_move(self, x: Optional[Union[str, int]] = None, y: Optional[Union[str, int]] = None, *, blocking: Literal[True], speed: Optional[int] = None, relative: bool = False) -> None: ... + def mouse_move(self, x: Optional[Union[str, int]] = None, y: Optional[Union[str, int]] = None, *, blocking: Literal[True], speed: Optional[int] = None, relative: bool = False, send_mode: Optional[SendMode] = None) -> None: ... @overload - def mouse_move(self, x: Optional[Union[str, int]] = None, y: Optional[Union[str, int]] = None, *, blocking: Literal[False], speed: Optional[int] = None, relative: bool = False, ) -> FutureResult[None]: ... + def mouse_move(self, x: Optional[Union[str, int]] = None, y: Optional[Union[str, int]] = None, *, blocking: Literal[False], speed: Optional[int] = None, relative: bool = False, send_mode: Optional[SendMode] = None) -> FutureResult[None]: ... @overload - def mouse_move(self, x: Optional[Union[str, int]] = None, y: Optional[Union[str, int]] = None, *, speed: Optional[int] = None, relative: bool = False, blocking: bool = True) -> Union[None, FutureResult[None]]: ... + def mouse_move(self, x: Optional[Union[str, int]] = None, y: Optional[Union[str, int]] = None, *, speed: Optional[int] = None, relative: bool = False, blocking: bool = True, send_mode: Optional[SendMode] = None) -> Union[None, FutureResult[None]]: ... # fmt: on def mouse_move( self, @@ -775,6 +789,7 @@ def mouse_move( *, speed: Optional[int] = None, relative: bool = False, + send_mode: Optional[SendMode] = None, blocking: bool = True, ) -> Union[None, FutureResult[None]]: """ @@ -795,6 +810,11 @@ def mouse_move( args.append('R') else: args.append('') + if send_mode: + args.append(send_mode) + else: + args.append('') + resp = self._transport.function_call('AHKMouseMove', args, blocking=blocking) return resp @@ -1097,13 +1117,13 @@ def key_up(self, key: Union[str, Key], blocking: bool = True) -> Union[None, Fut # fmt: off @overload - def key_wait(self, key_name: str, *, timeout: Optional[int] = None, logical_state: bool = False, released: bool = False) -> int: ... + def key_wait(self, key_name: str, *, timeout: Optional[int] = None, logical_state: bool = False, released: bool = False) -> bool: ... @overload - def key_wait(self, key_name: str, *, blocking: Literal[True], timeout: Optional[int] = None, logical_state: bool = False, released: bool = False) -> int: ... + def key_wait(self, key_name: str, *, blocking: Literal[True], timeout: Optional[int] = None, logical_state: bool = False, released: bool = False) -> bool: ... @overload - def key_wait(self, key_name: str, *, blocking: Literal[False], timeout: Optional[int] = None, logical_state: bool = False, released: bool = False) -> FutureResult[int]: ... + def key_wait(self, key_name: str, *, blocking: Literal[False], timeout: Optional[int] = None, logical_state: bool = False, released: bool = False) -> FutureResult[bool]: ... @overload - def key_wait(self, key_name: str, *, timeout: Optional[int] = None, logical_state: bool = False, released: bool = False, blocking: bool = True) -> Union[int, FutureResult[int]]: ... + def key_wait(self, key_name: str, *, timeout: Optional[int] = None, logical_state: bool = False, released: bool = False, blocking: bool = True) -> Union[bool, FutureResult[bool]]: ... # fmt: on def key_wait( self, @@ -1113,7 +1133,7 @@ def key_wait( logical_state: bool = False, released: bool = False, blocking: bool = True, - ) -> Union[int, FutureResult[int]]: + ) -> Union[bool, FutureResult[bool]]: """ Analog for `KeyWait `_ """ @@ -1122,11 +1142,10 @@ def key_wait( options += 'D' if logical_state: options += 'L' - if timeout: + if timeout is not None: + assert timeout >= 0, 'Timeout value must be non-negative' options += f'T{timeout}' - args = [key_name] - if options: - args.append(options) + args = [key_name, options] resp = self._transport.function_call('AHKKeyWait', args, blocking=blocking) return resp @@ -1161,13 +1180,13 @@ def get_send_level(self) -> int: # fmt: off @overload - def send(self, s: str, *, raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None) -> None: ... + def send(self, s: str, *, raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None, send_mode: Optional[SendMode] = None) -> None: ... @overload - def send(self, s: str, *, raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None, blocking: Literal[True]) -> None: ... + def send(self, s: str, *, raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None, send_mode: Optional[SendMode] = None, blocking: Literal[True]) -> None: ... @overload - def send(self, s: str, *, raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None, blocking: Literal[False]) -> FutureResult[None]: ... + def send(self, s: str, *, raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None, send_mode: Optional[SendMode] = None, blocking: Literal[False]) -> FutureResult[None]: ... @overload - def send(self, s: str, *, raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None, blocking: bool = True) -> Union[None, FutureResult[None]]: ... + def send(self, s: str, *, raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None, send_mode: Optional[SendMode] = None, blocking: bool = True) -> Union[None, FutureResult[None]]: ... # fmt: on def send( self, @@ -1176,6 +1195,7 @@ def send( raw: bool = False, key_delay: Optional[int] = None, key_press_duration: Optional[int] = None, + send_mode: Optional[SendMode] = None, blocking: bool = True, ) -> Union[None, FutureResult[None]]: """ @@ -1190,6 +1210,10 @@ def send( args.append(str(key_press_duration)) else: args.append('') + if send_mode: + args.append(send_mode) + else: + args.append('') if raw: raw_resp = self._transport.function_call('AHKSendRaw', args=args, blocking=blocking) @@ -2742,13 +2766,13 @@ def win_set_trans_color( # fmt: off @overload - def right_click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, coord_mode: Optional[CoordModeRelativeTo] = None) -> None: ... + def right_click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> None: ... @overload - def right_click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: Literal[True], coord_mode: Optional[CoordModeRelativeTo] = None) -> None: ... + def right_click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: Literal[True], coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> None: ... @overload - def right_click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: Literal[False], coord_mode: Optional[CoordModeRelativeTo] = None) -> FutureResult[None]: ... + def right_click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: Literal[False], coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> FutureResult[None]: ... @overload - def right_click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: bool = True, coord_mode: Optional[CoordModeRelativeTo] = None) -> Union[None, FutureResult[None]]: ... + def right_click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: bool = True, coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> Union[None, FutureResult[None]]: ... # fmt: on def right_click( self, @@ -2760,6 +2784,7 @@ def right_click( relative: Optional[bool] = None, blocking: bool = True, coord_mode: Optional[CoordModeRelativeTo] = None, + send_mode: Optional[SendMode] = None, ) -> Union[None, FutureResult[None]]: button = 'R' return self.click( @@ -2771,17 +2796,18 @@ def right_click( relative=relative, blocking=blocking, coord_mode=coord_mode, + send_mode=send_mode, ) # fmt: off @overload - def click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, coord_mode: Optional[CoordModeRelativeTo] = None) -> None: ... + def click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> None: ... @overload - def click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: Literal[True], coord_mode: Optional[CoordModeRelativeTo] = None) -> None: ... + def click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: Literal[True], coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> None: ... @overload - def click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: Literal[False], coord_mode: Optional[CoordModeRelativeTo] = None) -> FutureResult[None]: ... + def click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: Literal[False], coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> FutureResult[None]: ... @overload - def click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: bool = True, coord_mode: Optional[CoordModeRelativeTo] = None) -> Union[None, FutureResult[None]]: ... + def click(self, x: Optional[Union[int, Tuple[int, int]]] = None, y: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, click_count: Optional[int] = None, direction: Optional[Literal['U', 'D', 'Up', 'Down']] = None, *, relative: Optional[bool] = None, blocking: bool = True, coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> Union[None, FutureResult[None]]: ... # fmt: on def click( self, @@ -2794,6 +2820,7 @@ def click( relative: Optional[bool] = None, blocking: bool = True, coord_mode: Optional[CoordModeRelativeTo] = None, + send_mode: Optional[SendMode] = None, ) -> Union[None, FutureResult[None]]: """ Analog for `Click `_ @@ -2813,7 +2840,9 @@ def click( r = '' if coord_mode is None: coord_mode = '' - args = [str(x), str(y), button, str(click_count), direction or '', r, coord_mode] + if send_mode is None: + send_mode = '' + args = [str(x), str(y), button, str(click_count), direction or '', r, coord_mode, str(send_mode)] resp = self._transport.function_call('AHKClick', args, blocking=blocking) return resp @@ -2882,6 +2911,16 @@ def image_search( resp = self._transport.function_call('AHKImageSearch', args, blocking=blocking) return resp + # fmt: off + @overload + def mouse_drag(self, x: int, y: int, *, from_position: Optional[Tuple[int, int]] = None, speed: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, relative: Optional[bool] = None, coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> None: ... + @overload + def mouse_drag(self, x: int, y: int, *, from_position: Optional[Tuple[int, int]] = None, speed: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, relative: Optional[bool] = None, coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None, blocking: Literal[False]) -> FutureResult[None]: ... + @overload + def mouse_drag(self, x: int, y: int, *, from_position: Optional[Tuple[int, int]] = None, speed: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, relative: Optional[bool] = None, coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None, blocking: Literal[True]) -> None: ... + @overload + def mouse_drag(self, x: int, y: int, *, from_position: Optional[Tuple[int, int]] = None, speed: Optional[int] = None, button: Optional[Union[MouseButton, str]] = None, relative: Optional[bool] = None, blocking: bool = True, coord_mode: Optional[CoordModeRelativeTo] = None, send_mode: Optional[SendMode] = None) -> Union[None, FutureResult[None]]: ... + # fmt: on def mouse_drag( self, x: int, @@ -2889,14 +2928,19 @@ def mouse_drag( *, from_position: Optional[Tuple[int, int]] = None, speed: Optional[int] = None, - button: MouseButton = 'left', + button: Optional[Union[MouseButton, str]] = None, relative: Optional[bool] = None, blocking: bool = True, coord_mode: Optional[CoordModeRelativeTo] = None, - ) -> None: + send_mode: Optional[SendMode] = None, + ) -> Union[None, FutureResult[None]]: """ Analog for `MouseClickDrag `_ """ + if button is None: + button = 'Left' + else: + button = _resolve_button(button) if from_position: x1, y1 = from_position args = [str(button), str(x1), str(y1), str(x), str(y)] @@ -2918,7 +2962,13 @@ def mouse_drag( else: args.append('') - self._transport.function_call('AHKMouseClickDrag', args, blocking=blocking) + if send_mode: + args.append(send_mode) + else: + args.append('') + + resp = self._transport.function_call('AHKMouseClickDrag', args, blocking=blocking) + return resp # fmt: off @overload diff --git a/ahk/_sync/transport.py b/ahk/_sync/transport.py index 27cf3b7..97a7f27 100644 --- a/ahk/_sync/transport.py +++ b/ahk/_sync/transport.py @@ -87,6 +87,7 @@ def result(self, timeout: Optional[float] = None) -> T_SyncFuture: 'AHKGetClipboardAll', 'AHKGetCoordMode', 'AHKGetSendLevel', + 'AHKGetSendMode', 'AHKGetTitleMatchMode', 'AHKGetTitleMatchSpeed', 'AHKGetVolume', @@ -117,6 +118,7 @@ def result(self, timeout: Optional[float] = None) -> T_SyncFuture: 'AHKSetCoordMode', 'AHKSetDetectHiddenWindows', 'AHKSetSendLevel', + 'AHKSetSendMode', 'AHKSetTitleMatchMode', 'AHKSetVolume', 'AHKShowToolTip', @@ -395,7 +397,7 @@ def function_call(self, function_name: Literal['AHKClick'], args: Optional[List[ @overload def function_call(self, function_name: Literal['AHKMouseClickDrag'], args: Optional[List[str]] = None, *, blocking: bool = True, engine: Optional[AHK[Any]] = None) -> Union[None, FutureResult[None]]: ... @overload - def function_call(self, function_name: Literal['AHKKeyWait'], args: Optional[List[str]] = None, *, blocking: bool = True, engine: Optional[AHK[Any]] = None) -> Union[int, FutureResult[int]]: ... + def function_call(self, function_name: Literal['AHKKeyWait'], args: Optional[List[str]] = None, *, blocking: bool = True, engine: Optional[AHK[Any]] = None) -> Union[bool, FutureResult[bool]]: ... @overload def function_call(self, function_name: Literal['SetKeyDelay'], args: Optional[List[str]] = None, *, blocking: bool = True, engine: Optional[AHK[Any]] = None) -> Union[None, FutureResult[None]]: ... @overload @@ -513,6 +515,10 @@ def function_call(self, function_name: Literal['AHKSetCoordMode'], args: List[st @overload def function_call(self, function_name: Literal['AHKGetSendLevel']) -> int: ... @overload + def function_call(self, function_name: Literal['AHKSetSendMode'], args: List[str]) -> None: ... + @overload + def function_call(self, function_name: Literal['AHKGetSendMode']) -> str: ... + @overload def function_call(self, function_name: Literal['AHKSetSendLevel'], args: List[str]) -> None: ... @overload def function_call(self, function_name: Literal['AHKWinWait'], args: Optional[List[str]] = None, *, blocking: bool = True, engine: Optional[AHK[Any]] = None) -> Union[Window, FutureResult[Window]]: ... diff --git a/ahk/templates/daemon-v2.ahk b/ahk/templates/daemon-v2.ahk index 2742a6f..c371233 100644 --- a/ahk/templates/daemon-v2.ahk +++ b/ahk/templates/daemon-v2.ahk @@ -1809,11 +1809,23 @@ AHKMouseMove(args*) { y := args[2] speed := args[3] relative := args[4] + send_mode := args[5] + current_send_mode := Format("{}", A_SendMode) + + if (send_mode != "") { + SendMode send_mode + } + if (relative != "") { - MouseMove(x, y, speed, "R") + MouseMove(x, y, speed, "R") } else { - MouseMove(x, y, speed) + MouseMove(x, y, speed) + } + + if (send_mode != "") { + SendMode current_send_mode } + resp := FormatNoValueResponse() return resp {% endblock AHKMouseMove %} @@ -1828,7 +1840,13 @@ AHKClick(args*) { direction := args[5] r := args[6] relative_to := args[7] + send_mode := args[8] current_coord_rel := Format("{}", A_CoordModeMouse) + current_send_mode := Format("{}", A_SendMode) + + if (send_mode != "") { + SendMode send_mode + } if (relative_to != "") { CoordMode("Mouse", relative_to) @@ -1840,6 +1858,9 @@ AHKClick(args*) { CoordMode("Mouse", current_coord_rel) } + if (send_mode != "") { + SendMode current_send_mode + } return FormatNoValueResponse() {% endblock AHKClick %} @@ -1879,6 +1900,19 @@ AHKSetCoordMode(args*) { {% endblock AHKSetCoordMode %} } + +AHKGetSendMode(args*) { + return FormatResponse("ahk.message.StringResponseMessage", A_SendMode) +} + + +AHKSetSendMode(args*) { + mode := args[1] + SendMode mode + return FormatNoValueResponse() +} + + AHKMouseClickDrag(args*) { {% block AHKMouseClickDrag %} button := args[1] @@ -1889,8 +1923,13 @@ AHKMouseClickDrag(args*) { speed := args[6] relative := args[7] relative_to := args[8] - + send_mode := args[9] current_coord_rel := Format("{}", A_CoordModeMouse) + current_send_mode := Format("{}", A_SendMode) + + if (send_mode != "") { + SendMode send_mode + } if (relative_to != "") { CoordMode("Mouse", relative_to) @@ -1912,6 +1951,10 @@ AHKMouseClickDrag(args*) { CoordMode("Mouse", current_coord_rel) } + if (send_mode != "") { + SendMode current_send_mode + } + return FormatNoValueResponse() {% endblock AHKMouseClickDrag %} @@ -1964,13 +2007,19 @@ AHKKeyWait(args*) { {% block AHKKeyWait %} keyname := args[1] - if (args.Length = 2) { + options := args[2] + + if (options = "") { ret := KeyWait(keyname) } else { - options := args[2] ret := KeyWait(keyname, options) } - return FormatResponse("ahk.message.IntegerResponseMessage", ret) + + if (ret = 0) { + return FormatResponse("ahk.message.BooleanResponseMessage", 0) + } else { + return FormatResponse("ahk.message.BooleanResponseMessage", 1) + } {% endblock AHKKeyWait %} } @@ -1985,8 +2034,14 @@ AHKSend(args*) { str := args[1] key_delay := args[2] key_press_duration := args[3] + send_mode := args[4] current_delay := Format("{}", A_KeyDelay) current_key_duration := Format("{}", A_KeyDuration) + current_send_mode := Format("{}", A_SendMode) + + if (send_mode != "") { + SendMode send_mode + } if (key_delay != "" or key_press_duration != "") { SetKeyDelay(key_delay, key_press_duration) @@ -1995,6 +2050,10 @@ AHKSend(args*) { Send(str) + if (send_mode != "") { + SendMode current_send_mode + } + if (key_delay != "" or key_press_duration != "") { SetKeyDelay(current_delay, current_key_duration) } diff --git a/ahk/templates/daemon.ahk b/ahk/templates/daemon.ahk index 7017fe8..d194fc9 100644 --- a/ahk/templates/daemon.ahk +++ b/ahk/templates/daemon.ahk @@ -1725,14 +1725,25 @@ AHKClick(args*) { direction := args[5] r := args[6] relative_to := args[7] + send_mode := args[8] current_coord_rel := Format("{}", A_CoordModeMouse) + current_send_mode := Format("{}", A_SendMode) + + if (send_mode != "") { + SendMode, %send_mode% + } if (relative_to != "") { CoordMode, Mouse, %relative_to% } + Click, %x%, %y%, %button%, %direction%, %r% + if (send_mode != "") { + SendMode, %current_send_mode% + } + if (relative_to != "") { CoordMode, Mouse, %current_coord_rel% } @@ -1776,6 +1787,18 @@ AHKSetCoordMode(args*) { {% endblock AHKSetCoordMode %} } +AHKGetSendMode(args*) { + return FormatResponse("ahk.message.StringResponseMessage", A_SendMode) +} + + +AHKSetSendMode(args*) { + mode := args[1] + SendMode, %mode% + return FormatNoValueResponse() +} + + AHKMouseClickDrag(args*) { {% block AHKMouseClickDrag %} button := args[1] @@ -1786,6 +1809,11 @@ AHKMouseClickDrag(args*) { speed := args[6] relative := args[7] relative_to := args[8] + send_mode := args[8] + current_send_mode := Format("{}", A_SendMode) + if (send_mode != "") { + SendMode, %send_mode% + } current_coord_rel := Format("{}", A_CoordModeMouse) @@ -1799,6 +1827,10 @@ AHKMouseClickDrag(args*) { CoordMode, Mouse, %current_coord_rel% } + if (send_mode != "") { + SendMode, %current_send_mode% + } + return FormatNoValueResponse() {% endblock AHKMouseClickDrag %} @@ -1856,13 +1888,24 @@ AHKKeyWait(args*) { {% block AHKKeyWait %} keyname := args[1] - if (args.Length() = 2) { + options := args[2] + + if (options = "") { KeyWait,% keyname } else { - options := args[2] KeyWait,% keyname,% options } - return FormatResponse("ahk.message.IntegerResponseMessage", ErrorLevel) + ret := ErrorLevel + + if (ret = 1) { + return FormatResponse("ahk.message.BooleanResponseMessage", 0) + } else if (ret = 0) { + return FormatResponse("ahk.message.BooleanResponseMessage", 1) + } else { + ; Unclear if this is even reachable + return FormatResponse("ahk.message.ExceptionResponseMessage", Format("There was a problem. ErrorLevel: {}", ret)) + } + {% endblock AHKKeyWait %} } @@ -1877,18 +1920,29 @@ AHKSend(args*) { str := args[1] key_delay := args[2] key_press_duration := args[3] + send_mode := args[4] current_delay := Format("{}", A_KeyDelay) current_key_duration := Format("{}", A_KeyDuration) + current_send_mode := Format("{}", A_SendMode) if (key_delay != "" or key_press_duration != "") { SetKeyDelay, %key_delay%, %key_press_duration% } + if (send_mode != "") { + SendMode, %send_mode% + } + Send,% str if (key_delay != "" or key_press_duration != "") { SetKeyDelay, %current_delay%, %current_key_duration% } + + if (send_mode != "") { + SendMode, %current_send_mode% + } + return FormatNoValueResponse() {% endblock AHKSend %} } @@ -1898,18 +1952,30 @@ AHKSendRaw(args*) { str := args[1] key_delay := args[2] key_press_duration := args[3] + send_mode := args[4] current_delay := Format("{}", A_KeyDelay) current_key_duration := Format("{}", A_KeyDuration) + current_send_mode := Format("{}", A_SendMode) + if (key_delay != "" or key_press_duration != "") { SetKeyDelay, %key_delay%, %key_press_duration% } + if (send_mode != "") { + SendMode, %send_mode% + } + SendRaw,% str if (key_delay != "" or key_press_duration != "") { SetKeyDelay, %current_delay%, %current_key_duration% } + + if (send_mode != "") { + SendMode, %current_send_mode% + } + return FormatNoValueResponse() {% endblock AHKSendRaw %} } diff --git a/docs/README.md b/docs/README.md index 82f312a..211c946 100644 --- a/docs/README.md +++ b/docs/README.md @@ -153,8 +153,10 @@ ahk.key_press('a') # Press and release a key ahk.key_down('Control') # Press down (but do not release) Control key ahk.key_up('Control') # Release the key ahk.set_capslock_state("On") # Turn CapsLock on -ahk.key_wait('a', timeout=3) # Wait up to 3 seconds for the "a" key to be pressed. NOTE: This throws - # a TimeoutError if the key isn't pressed within the timeout window +if ahk.key_wait('x', timeout=3): # wait for a key to be pressed; returns a boolean + print('X was pressed within 3 seconds') +else: + print('X was not pressed within 3 seconds') ``` ## Windows @@ -192,29 +194,44 @@ from ahk import AHK ahk = AHK() ahk.run_script('Run Notepad') # Open notepad -win = ahk.find_window(title='Untitled - Notepad') # Find the opened window +win = ahk.find_window(title='Untitled - Notepad') # Find the opened window; returns a `Window` object -win.send('hello') # Send keys directly to the window (does not need focus!) +# Window object methods +win.send('hello', control='Edit1') # Send keys directly to the window (does not need focus!) +# OR ahk.control_send(title='Untitled - Notepad', control='Edit1') win.move(x=200, y=300, width=500, height=800) -win.activate() # Give the window focus -win.close() # Close the window -win.hide() # Hide the windwow -win.kill() # Kill the window -win.maximize() # Maximize the window -win.minimize() # Minimize the window -win.restore() # Restore the window -win.show() # Show the window -win.disable() # Make the window non-interactable -win.enable() # Enable it again -win.to_top() # Move the window on top of other windows -win.to_bottom() # Move the window to the bottom of the other windows +win.activate() # Give the window focus +win.close() # Close the window +win.hide() # Hide the window +win.kill() # Kill the window +win.maximize() # Maximize the window +win.minimize() # Minimize the window +win.restore() # Restore the window +win.show() # Show the window +win.disable() # Make the window non-interactable +win.enable() # Enable it again +win.to_top() # Move the window on top of other windows +win.to_bottom() # Move the window to the bottom of the other windows +win.get_class() # Get the class name of the window +win.get_minmax() # Get the min/max status +win.get_process_name() # Get the process name (e.g., "notepad.exe") +win.process_name # Property; same as `.get_process_name()` above +win.is_always_on_top() # Whether the window has the 'always on top' style applied +win.list_controls() # Get a list of controls (list of `Control` objects) +win.redraw() # Redraw the window +win.set_style("-0xC00000") # Set a style on the window (in this case, removing the title bar) +win.set_ex_style("^0x80") # Set an ExStyle on the window (in this case, removes the window from alt-tab list) +win.set_region("") # See: https://www.autohotkey.com/docs/v2/lib/WinSetRegion.htm +win.set_trans_color("White") # Makes all pixels of the chosen color invisible inside the specified window. +win.set_transparent(155) # Makes the specified window semi-transparent (or "Off" to turn off transparency) + win.always_on_top = 'On' # Make the window always on top # or win.set_always_on_top('On') -for window in ahk.list_windows(): +for window in ahk.list_windows(): # list all (non-hidden) windows -- ``detect_hidden_windows=True`` to include hidden print(window.title) # Some more attributes @@ -230,8 +247,20 @@ if win.active: # or win.is_active() if win.exist: # or win.exists() ... + +# Controls + +edit_control = win.list_controls()[0] # get the first control for the window, in this case "Edit1" for Notepad +edit_control.get_text() # get the text in Notepad +edit_control.get_position() # returns a `Postion` namedtuple: e.g. Position(x=6, y=49, width=2381, height=1013) + ``` +Various window methods can also be called directly without first creating a `Window` object by using the underlying `win_*` methods on the `AHK` class. +For example, instead of `win.close()` as above, one could call `ahk.win_close(title='Untitled - Notepad')` instead. + + + ## Screen ```python @@ -259,6 +288,10 @@ ahk = AHK() ahk.set_clipboard('hello \N{EARTH GLOBE AMERICAS}') # set clipboard text contents ahk.get_clipboard() # get clipboard text contents # 'hello 🌎' +ahk.set_clipboard("") # Clear the clipboard + +ahk.clip_wait(timeout=3) # Wait for clipboard contents to change (with text or file(s)) +ahk.clip_wait(timeout=3, wait_for_any_data=True) # wait for _any_ clipboard contents ``` You may also get/set `ClipboardAll` -- however, you should never try to call `set_clipboard_all` with any other @@ -275,6 +308,29 @@ ahk.set_clipboard('something else') ahk.set_clipboard_all(saved_clipboard) # restore saved content from earlier ``` +You can also set a callback to execute when the clipboard contents change. As with Hotkey methods mentioned above, +you can also set an exception handler. Like hotkeys, `on_clipboard_change` callbacks also require `.start_hotkeys()` +to be called to take effect. + +The callback function must accept one positional argument, which is an integer indicating the clipboard datatype. + +```python +from ahk import AHK +ahk = AHK() +def my_clipboard_callback(change_type: int): + if change_type == 0: + print('Clipboard is now empty') + elif change_type == 1: + print('Clipboard has text contents') + elif change_type == 2: + print('Clipboard has non-text contents') + +ahk.on_clipboard_change(my_clipboard_callback) +ahk.start_hotkeys() # like with hotkeys, must be called at least once for listening to start +# ... +ahk.set_clipboard("hello") # will cause the message "Clipboard has text contents" to be printed by the callback +ahk.set_clipboard("") # Clears the clipboard, causing the message "Clipboard is now empty" to be printed by the callback +``` ## Sound @@ -338,6 +394,7 @@ ahk.set_send_level(5) # Change send https://www.autohotkey.com/docs/v1/lib/Send ahk.set_title_match_mode('Slow') # change title match speed and/or mode ahk.set_title_match_mode('RegEx') ahk.set_title_match_mode(('RegEx', 'Slow')) # or both at the same time +ahk.set_send_mode('Event') # change the default SendMode ``` ## Add directives @@ -538,7 +595,8 @@ ahk.run_script(script_path) To use this package, you need the [AutoHotkey executable](https://www.autohotkey.com/download/) (e.g., `AutoHotkey.exe`). It's expected to be on PATH by default OR in a default installation location (`C:\Program Files\AutoHotkey\AutoHotkey.exe` for v1 or `C:\Program Files\AutoHotkey\v2\AutoHotkey64.exe` for v2) -AutoHotkey v1 is fully supported. AutoHotkey v2 support is available, but is considered to be in beta status. +AutoHotkey v1 and v2 are both fully supported, though some behavioral differences will occur depending on which version +you use. See notes below. The recommended way to supply the AutoHotkey binary (for both v1 and v2) is to install the `binary` extra for this package. This will provide the necessary executables and help ensure they are correctly placed on PATH. @@ -594,16 +652,22 @@ the `version` keyword is omitted, the version is determined automatically from t The API of this project is originally designed against AutoHotkey v1 and function signatures are the same, even when using AutoHotkey v2. While most of the behavior remains the same, some behavior does change when using AutoHotkey v2 compared to v1. This is mostly due to -underlying differences between the two versions. - -Some of the differences that you will experience when using AutoHotkey v2 include: +[underlying differences](https://www.autohotkey.com/docs/v2/v2-changes.htm) between the two versions. +Some of the notable differences that you may experience when using AutoHotkey v2 with this library include: 1. Functions that find and return windows will often raise an exception rather than returning `None` (as in AutoHotkey v2, a TargetError is thrown in most cases where the window or control cannot be found) 2. The behavior of `ControlSend` (`ahk.control_send` or `Window.send` or `Control.send`) differs in AutoHotkey v2 when the `control` parameter is not specified. In v1, keys are sent to the topmost controls, which is usually the correct behavior. In v2, keys are sent directly to the window. This means in many cases, you need to specify the control explicitly when using V2. 3. Some functionality is not supported in v2 -- specifically: the `secondstowait` paramater for `TrayTip` (`ahk.show_traytip`) was removed in v2. Specifying this parameter in the Python wrapper will cause a warning to be emitted and the parameter is ignored. 4. Some functionality that is present in v1 is not yet implemented in v2 -- this is expected to change in future versions. Specifically: some [sound functions](https://www.autohotkey.com/docs/v2/lib/Sound.htm) are not implemented. +5. The default SendMode changes in v2 to `Input` rather than `Event` in v1 (as a consequence, for example, mouse speed parameters to `mouse_move` and `mouse_drag` will be ignored in V2 unless the send mode is changed) + + +## Extending: add your own AutoHotkey code (beta) +You can develop extensions for extending functionality of `ahk` -- that is: writing your own AutoHotkey code and adding +additional methods to the AHK class. See the [extending docs](https://ahk.readthedocs.io/en/latest/extending.html) for +more information. # Contributing diff --git a/docs/api/methods.rst b/docs/api/methods.rst index b13177b..0f89ae0 100644 --- a/docs/api/methods.rst +++ b/docs/api/methods.rst @@ -82,8 +82,8 @@ Mouse and Keyboard - Implemented - :py:meth:`~ahk._sync.engine.AHK.set_send_level` * - `SendMode `_ - - Not Implemented - - + - Implemented + - :py:meth:`~ahk._sync.engine.AHK.set_send_mode` * - `SetCapsLockState `_ - Implemented - :py:meth:`~ahk._sync.engine.AHK.set_capslock_state` @@ -534,7 +534,7 @@ For example, to use the :py:class:`~ahk.directives.NoTrayIcon` directive * - `#MenuMaskKey `_ - * - `#NoEnv `_ - - + - Removed in ``ahk`` v1.0.0 -- Used by default when using AutoHotkey v1. Not available in AutoHotkey v2. * - `#NoTrayIcon `_ - If you use hotkeys or hotstrings, you probably also want to configure this as a hotkey transport option * - `#Persistent `_ diff --git a/tests/_async/test_keys.py b/tests/_async/test_keys.py index a5116c5..5615794 100644 --- a/tests/_async/test_keys.py +++ b/tests/_async/test_keys.py @@ -5,6 +5,8 @@ import time import unittest.mock +import pytest + from ahk import AsyncAHK from ahk import AsyncWindow @@ -78,6 +80,20 @@ async def test_hotstring_callback(self): await async_sleep(1) m.assert_called() + async def test_key_wait(self): + res = await self.ahk.key_wait('x', timeout=3, blocking=False) + await self.ahk.set_send_level(1) + await async_sleep(1) + await self.ahk.key_down('x') + await async_sleep(1) + await self.ahk.key_up('x') + result = await res.result() + assert result is True + + async def test_key_wait_timeout(self): + res = await self.ahk.key_wait('x', timeout=1) + assert res is False + class TestKeysAsyncV2(TestKeysAsync): async def asyncSetUp(self) -> None: diff --git a/tests/_async/test_mouse.py b/tests/_async/test_mouse.py index 26477e6..ea41271 100644 --- a/tests/_async/test_mouse.py +++ b/tests/_async/test_mouse.py @@ -57,7 +57,7 @@ async def test_mouse_move_rel(self): async def test_mouse_move_nonblocking(self): await self.ahk.mouse_move(100, 100) - res = await self.ahk.mouse_move(500, 500, speed=5, blocking=False) + res = await self.ahk.mouse_move(500, 500, speed=10, send_mode='Event', blocking=False) current_pos = await self.ahk.get_mouse_position() await async_sleep(0.1) pos = await self.ahk.get_mouse_position() @@ -65,6 +65,37 @@ async def test_mouse_move_nonblocking(self): assert pos != (500, 500) await res.result() + async def test_mouse_drag(self): + await self.ahk.mouse_move(x=100, y=100) + pos = await self.ahk.get_mouse_position() + assert pos == (100, 100) + await self.ahk.mouse_drag(x=200, y=200) + pos2 = await self.ahk.get_mouse_position() + assert pos2 == (200, 200) + + async def test_mouse_drag_relative(self): + await self.ahk.mouse_move(x=100, y=100) + await async_sleep(0.5) + pos = await self.ahk.get_mouse_position() + assert pos == (100, 100) + await self.ahk.mouse_drag(x=10, y=10, relative=True, button=1) + await async_sleep(0.5) + pos2 = await self.ahk.get_mouse_position() + x1, y1 = pos + x2, y2 = pos2 + assert abs(x1 - x2) == 10 + assert abs(y1 - y2) == 10 + + async def test_coord_mode(self): + await self.ahk.set_coord_mode(target='Mouse', relative_to='Client') + res = await self.ahk.get_coord_mode(target='Mouse') + assert res == 'Client' + + async def test_send_mode(self): + await self.ahk.set_send_mode('InputThenPlay') + res = await self.ahk.get_send_mode() + assert res == 'InputThenPlay' + class TestMouseAsyncV2(TestMouseAsync): async def asyncSetUp(self) -> None: diff --git a/tests/_sync/test_keys.py b/tests/_sync/test_keys.py index 9f68239..e690cde 100644 --- a/tests/_sync/test_keys.py +++ b/tests/_sync/test_keys.py @@ -5,6 +5,8 @@ import time import unittest.mock +import pytest + from ahk import AHK from ahk import Window @@ -77,6 +79,20 @@ def test_hotstring_callback(self): sleep(1) m.assert_called() + def test_key_wait(self): + res = self.ahk.key_wait('x', timeout=3, blocking=False) + self.ahk.set_send_level(1) + sleep(1) + self.ahk.key_down('x') + sleep(1) + self.ahk.key_up('x') + result = res.result() + assert result is True + + def test_key_wait_timeout(self): + res = self.ahk.key_wait('x', timeout=1) + assert res is False + class TestKeysAsyncV2(TestKeysAsync): def setUp(self) -> None: diff --git a/tests/_sync/test_mouse.py b/tests/_sync/test_mouse.py index e5616c6..a8b580c 100644 --- a/tests/_sync/test_mouse.py +++ b/tests/_sync/test_mouse.py @@ -56,7 +56,7 @@ def test_mouse_move_rel(self): def test_mouse_move_nonblocking(self): self.ahk.mouse_move(100, 100) - res = self.ahk.mouse_move(500, 500, speed=5, blocking=False) + res = self.ahk.mouse_move(500, 500, speed=10, send_mode='Event', blocking=False) current_pos = self.ahk.get_mouse_position() sleep(0.1) pos = self.ahk.get_mouse_position() @@ -64,6 +64,37 @@ def test_mouse_move_nonblocking(self): assert pos != (500, 500) res.result() + def test_mouse_drag(self): + self.ahk.mouse_move(x=100, y=100) + pos = self.ahk.get_mouse_position() + assert pos == (100, 100) + self.ahk.mouse_drag(x=200, y=200) + pos2 = self.ahk.get_mouse_position() + assert pos2 == (200, 200) + + def test_mouse_drag_relative(self): + self.ahk.mouse_move(x=100, y=100) + sleep(0.5) + pos = self.ahk.get_mouse_position() + assert pos == (100, 100) + self.ahk.mouse_drag(x=10, y=10, relative=True, button=1) + sleep(0.5) + pos2 = self.ahk.get_mouse_position() + x1, y1 = pos + x2, y2 = pos2 + assert abs(x1 - x2) == 10 + assert abs(y1 - y2) == 10 + + def test_coord_mode(self): + self.ahk.set_coord_mode(target='Mouse', relative_to='Client') + res = self.ahk.get_coord_mode(target='Mouse') + assert res == 'Client' + + def test_send_mode(self): + self.ahk.set_send_mode('InputThenPlay') + res = self.ahk.get_send_mode() + assert res == 'InputThenPlay' + class TestMouseAsyncV2(TestMouseAsync): def setUp(self) -> None: