ydotool 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ydotool/__init__.py +7 -0
- ydotool/client.py +123 -0
- ydotool/keycodes.py +219 -0
- ydotool/mousecodes.py +35 -0
- ydotool-0.1.0.dist-info/METADATA +201 -0
- ydotool-0.1.0.dist-info/RECORD +8 -0
- ydotool-0.1.0.dist-info/WHEEL +4 -0
- ydotool-0.1.0.dist-info/licenses/LICENSE +21 -0
ydotool/__init__.py
ADDED
ydotool/client.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Python wrapper for ydotool CLI commands.
|
|
3
|
+
|
|
4
|
+
Requires ydotool 1.0.4 to be installed and ydotoold daemon running.
|
|
5
|
+
|
|
6
|
+
See:
|
|
7
|
+
- https://github.com/ReimuNotMoe/ydotool
|
|
8
|
+
- https://github.com/ReimuNotMoe/ydotool/releases/tag/v1.0.4
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import subprocess
|
|
12
|
+
|
|
13
|
+
from .keycodes import get_keycodes
|
|
14
|
+
from .mousecodes import get_mousecode
|
|
15
|
+
|
|
16
|
+
def _run(*args: str) -> None:
|
|
17
|
+
subprocess.run(
|
|
18
|
+
["ydotool", *args],
|
|
19
|
+
check=True
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
def do_click(button: str, action: str, repeat: int | None = None, next_delay: int | None = None) -> None:
|
|
23
|
+
"""
|
|
24
|
+
Perform mouse click action.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
button: Button name ('left', 'right', 'middle', 'side',
|
|
28
|
+
'extra', 'forward', 'back', 'task').
|
|
29
|
+
action: Action name ('click', 'down', 'up').
|
|
30
|
+
repeat: Number of times to repeat the sequence (default: 1).
|
|
31
|
+
next_delay: Delay in milliseconds between events.
|
|
32
|
+
Full click takes 2x this value.
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
ValueError: If button or action is invalid.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
mousecode = get_mousecode(button, action)
|
|
39
|
+
|
|
40
|
+
if mousecode:
|
|
41
|
+
args = []
|
|
42
|
+
|
|
43
|
+
if repeat and repeat > 1:
|
|
44
|
+
args.append(f"--repeat={repeat}")
|
|
45
|
+
if next_delay and next_delay > 0:
|
|
46
|
+
args.append(f"--next-delay={next_delay}")
|
|
47
|
+
|
|
48
|
+
_run("click", *args, mousecode)
|
|
49
|
+
else:
|
|
50
|
+
raise ValueError("Invalid button or action")
|
|
51
|
+
|
|
52
|
+
def do_mousemove(x: int, y: int, absolute: bool = True) -> None:
|
|
53
|
+
"""
|
|
54
|
+
Move mouse cursor to coordinates.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
x: X coordinate in pixels.
|
|
58
|
+
y: Y coordinate in pixels.
|
|
59
|
+
absolute: If True, move to absolute position.
|
|
60
|
+
If False, move relative to current position.
|
|
61
|
+
|
|
62
|
+
Note:
|
|
63
|
+
For correct movement, disable mouse acceleration
|
|
64
|
+
and set mouse sensitivity to default
|
|
65
|
+
in your desktop environment settings.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
args = []
|
|
69
|
+
|
|
70
|
+
if absolute:
|
|
71
|
+
args.append("--absolute")
|
|
72
|
+
|
|
73
|
+
_run("mousemove", *args, str(x), str(y))
|
|
74
|
+
|
|
75
|
+
def do_type(text: str, key_delay: int | None = None, key_hold: int | None = None) -> None:
|
|
76
|
+
"""
|
|
77
|
+
Type text string.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
text: Text to type. Handles special characters automatically.
|
|
81
|
+
key_delay: Delay in milliseconds between key events (default: 20).
|
|
82
|
+
key_hold: Hold time in milliseconds for each key (default: 20).
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
args = []
|
|
86
|
+
|
|
87
|
+
if key_delay and key_delay > 0:
|
|
88
|
+
args.append(f"--key-delay={key_delay}")
|
|
89
|
+
if key_hold and key_hold > 0:
|
|
90
|
+
args.append(f"--key-hold={key_hold}")
|
|
91
|
+
|
|
92
|
+
_run("type", *args, text)
|
|
93
|
+
|
|
94
|
+
def do_key(keys: list[str]) -> None:
|
|
95
|
+
"""
|
|
96
|
+
Press keyboard keys in sequence.
|
|
97
|
+
Key names are defined in `keycodes.KEYCODES`
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
keys: List of 'key:state' strings where state is '1' (down)
|
|
101
|
+
or '0' (up). Order matters for combinations.
|
|
102
|
+
|
|
103
|
+
Raises:
|
|
104
|
+
ValueError: If any key is invalid or format is wrong.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
keycodes = get_keycodes(keys)
|
|
108
|
+
|
|
109
|
+
if keycodes:
|
|
110
|
+
_run("key", *keycodes)
|
|
111
|
+
else:
|
|
112
|
+
raise ValueError("Invalid keys")
|
|
113
|
+
|
|
114
|
+
def do_wheel(vertical: int = 0, horizontal: int = 0) -> None:
|
|
115
|
+
"""
|
|
116
|
+
Scroll mouse wheel.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
vertical: Scroll ticks (positive=up, negative=down).
|
|
120
|
+
horizontal: Scroll ticks (positive=right, negative=left).
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
_run("mousemove", "--wheel", str(horizontal), str(vertical))
|
ydotool/keycodes.py
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Keyboard key code mappings based on `/usr/include/linux/input-event-codes.h`
|
|
3
|
+
|
|
4
|
+
All codes are decimal integers corresponding to `KEY_*` constants
|
|
5
|
+
in the Linux kernel header.
|
|
6
|
+
|
|
7
|
+
Unusual keys have not been added here.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# exclamation = shift + 1 ( ! ) = ( shift ) + ( 1 )
|
|
11
|
+
# at = shift + 2 ( @ ) = ( shift ) + ( 2 )
|
|
12
|
+
# hash = shift + 3 ( # ) = ( shift ) + ( 3 )
|
|
13
|
+
# dollar = shift + 4 ( $ ) = ( shift ) + ( 4 )
|
|
14
|
+
# percent = shift + 5 ( % ) = ( shift ) + ( 5 )
|
|
15
|
+
# caret = shift + 6 ( ^ ) = ( shift ) + ( 6 )
|
|
16
|
+
# ampersand = shift + 7 ( & ) = ( shift ) + ( 7 )
|
|
17
|
+
# asterisk = shift + 8 ( * ) = ( shift ) + ( 8 )
|
|
18
|
+
# leftparenthesis = shift + 9 ( ( ) = ( shift ) + ( 9 )
|
|
19
|
+
# rightparenthesis = shift + 0 ( ) ) = ( shift ) + ( 0 )
|
|
20
|
+
# underscore = shift + minus ( _ ) = ( shift ) + ( - )
|
|
21
|
+
# plus = shift + equal ( + ) = ( shift ) + ( = )
|
|
22
|
+
# leftcurlybracket = shift + leftbracket ( { ) = ( shift ) + ( [ )
|
|
23
|
+
# rightcurlybracket = shift + rightbracket ( } ) = ( shift ) + ( ] )
|
|
24
|
+
# verticalbar = shift + backslash ( | ) = ( shift ) + ( \ )
|
|
25
|
+
# colon = shift + semicolon ( : ) = ( shift ) + ( ; )
|
|
26
|
+
# doublequote = shift + apostrophe ( " ) = ( shift ) + ( ' )
|
|
27
|
+
# less = shift + comma ( < ) = ( shift ) + ( , )
|
|
28
|
+
# greater = shift + dot ( > ) = ( shift ) + ( . )
|
|
29
|
+
# question = shift + slash ( ? ) = ( shift ) + ( / )
|
|
30
|
+
# tilde = shift + grave ( ~ ) = ( shift ) + ( ` )
|
|
31
|
+
|
|
32
|
+
KEYCODES = {
|
|
33
|
+
"0": "11",
|
|
34
|
+
"1": "2",
|
|
35
|
+
"2": "3",
|
|
36
|
+
"3": "4",
|
|
37
|
+
"4": "5",
|
|
38
|
+
"5": "6",
|
|
39
|
+
"6": "7",
|
|
40
|
+
"7": "8",
|
|
41
|
+
"8": "9",
|
|
42
|
+
"9": "10",
|
|
43
|
+
|
|
44
|
+
"a": "30",
|
|
45
|
+
"b": "48",
|
|
46
|
+
"c": "46",
|
|
47
|
+
"d": "32",
|
|
48
|
+
"e": "18",
|
|
49
|
+
"f": "33",
|
|
50
|
+
"g": "34",
|
|
51
|
+
"h": "35",
|
|
52
|
+
"i": "23",
|
|
53
|
+
"j": "36",
|
|
54
|
+
"k": "37",
|
|
55
|
+
"l": "38",
|
|
56
|
+
"m": "50",
|
|
57
|
+
"n": "49",
|
|
58
|
+
"o": "24",
|
|
59
|
+
"p": "25",
|
|
60
|
+
"q": "16",
|
|
61
|
+
"r": "19",
|
|
62
|
+
"s": "31",
|
|
63
|
+
"t": "20",
|
|
64
|
+
"u": "22",
|
|
65
|
+
"v": "47",
|
|
66
|
+
"w": "17",
|
|
67
|
+
"x": "45",
|
|
68
|
+
"y": "21",
|
|
69
|
+
"z": "44",
|
|
70
|
+
|
|
71
|
+
"esc": "1",
|
|
72
|
+
"backspace": "14",
|
|
73
|
+
"tab": "15",
|
|
74
|
+
"enter": "28",
|
|
75
|
+
"space": "57",
|
|
76
|
+
"capslock": "58",
|
|
77
|
+
|
|
78
|
+
"ctrl": "29",
|
|
79
|
+
"leftctrl": "29",
|
|
80
|
+
"rightctrl": "97",
|
|
81
|
+
|
|
82
|
+
"shift": "42",
|
|
83
|
+
"leftshift": "42",
|
|
84
|
+
"rightshift": "54",
|
|
85
|
+
|
|
86
|
+
"alt": "56",
|
|
87
|
+
"leftalt": "56",
|
|
88
|
+
"rightalt": "100",
|
|
89
|
+
|
|
90
|
+
"super": "125",
|
|
91
|
+
"leftsuper": "125",
|
|
92
|
+
"rightsuper": "126",
|
|
93
|
+
|
|
94
|
+
"menu": "127",
|
|
95
|
+
"compose": "127",
|
|
96
|
+
|
|
97
|
+
"f1": "59",
|
|
98
|
+
"f2": "60",
|
|
99
|
+
"f3": "61",
|
|
100
|
+
"f4": "62",
|
|
101
|
+
"f5": "63",
|
|
102
|
+
"f6": "64",
|
|
103
|
+
"f7": "65",
|
|
104
|
+
"f8": "66",
|
|
105
|
+
"f9": "67",
|
|
106
|
+
"f10": "68",
|
|
107
|
+
"f11": "87",
|
|
108
|
+
"f12": "88",
|
|
109
|
+
|
|
110
|
+
"up": "103",
|
|
111
|
+
"down": "108",
|
|
112
|
+
"left": "105",
|
|
113
|
+
"right": "106",
|
|
114
|
+
|
|
115
|
+
"home": "102",
|
|
116
|
+
"end": "107",
|
|
117
|
+
"pageup": "104",
|
|
118
|
+
"pagedown": "109",
|
|
119
|
+
|
|
120
|
+
"insert": "110",
|
|
121
|
+
"delete": "111",
|
|
122
|
+
|
|
123
|
+
"printscreen": "99",
|
|
124
|
+
|
|
125
|
+
"scrolllock": "70",
|
|
126
|
+
|
|
127
|
+
"pause": "119",
|
|
128
|
+
|
|
129
|
+
"numlock": "69",
|
|
130
|
+
|
|
131
|
+
"minus": "12",
|
|
132
|
+
"equal": "13",
|
|
133
|
+
"leftbracket": "26",
|
|
134
|
+
"rightbracket": "27",
|
|
135
|
+
"semicolon": "39",
|
|
136
|
+
"apostrophe": "40",
|
|
137
|
+
"grave": "41",
|
|
138
|
+
"backslash": "43",
|
|
139
|
+
"comma": "51",
|
|
140
|
+
"dot": "52",
|
|
141
|
+
"slash": "53",
|
|
142
|
+
|
|
143
|
+
"np0": "82",
|
|
144
|
+
"np1": "79",
|
|
145
|
+
"np2": "80",
|
|
146
|
+
"np3": "81",
|
|
147
|
+
"np4": "75",
|
|
148
|
+
"np5": "76",
|
|
149
|
+
"np6": "77",
|
|
150
|
+
"np7": "71",
|
|
151
|
+
"np8": "72",
|
|
152
|
+
"np9": "73",
|
|
153
|
+
|
|
154
|
+
"npasterisk": "55",
|
|
155
|
+
"npminus": "74",
|
|
156
|
+
"npplus": "78",
|
|
157
|
+
"npdot": "83",
|
|
158
|
+
"npenter": "96",
|
|
159
|
+
"npslash": "98",
|
|
160
|
+
"npequal": "117",
|
|
161
|
+
|
|
162
|
+
"mute": "113",
|
|
163
|
+
"volumedown": "114",
|
|
164
|
+
"volumeup": "115",
|
|
165
|
+
"micmute": "248",
|
|
166
|
+
|
|
167
|
+
"power": "116",
|
|
168
|
+
"sleep": "142",
|
|
169
|
+
"wakeup": "143",
|
|
170
|
+
|
|
171
|
+
"playpause": "164",
|
|
172
|
+
"nextsong": "163",
|
|
173
|
+
"previoussong": "165",
|
|
174
|
+
|
|
175
|
+
"back": "158",
|
|
176
|
+
"forward": "159",
|
|
177
|
+
"homepage": "172",
|
|
178
|
+
"search": "217",
|
|
179
|
+
"refresh": "173",
|
|
180
|
+
|
|
181
|
+
"stop": "166",
|
|
182
|
+
|
|
183
|
+
"brightnessup": "224",
|
|
184
|
+
"brightnessdown": "225",
|
|
185
|
+
|
|
186
|
+
"browser": "150",
|
|
187
|
+
"mail": "155",
|
|
188
|
+
"calc": "140",
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
def _split_key(key_str: str) -> tuple[str, str] | None:
|
|
192
|
+
if key_str.count(":") != 1:
|
|
193
|
+
return None
|
|
194
|
+
key, state = key_str.split(":")
|
|
195
|
+
if state in ("0", "1"):
|
|
196
|
+
return key, state
|
|
197
|
+
return None
|
|
198
|
+
|
|
199
|
+
def get_keycode(key: str) -> str | None:
|
|
200
|
+
return KEYCODES.get(key)
|
|
201
|
+
|
|
202
|
+
def get_keycodes(keys: list[str]) -> list[str] | None:
|
|
203
|
+
keycodes_list = []
|
|
204
|
+
|
|
205
|
+
for key_str in keys:
|
|
206
|
+
key_and_state = _split_key(key_str)
|
|
207
|
+
|
|
208
|
+
if not key_and_state:
|
|
209
|
+
return None
|
|
210
|
+
|
|
211
|
+
key, state = key_and_state
|
|
212
|
+
keycode = get_keycode(key)
|
|
213
|
+
|
|
214
|
+
if not keycode:
|
|
215
|
+
return None
|
|
216
|
+
|
|
217
|
+
keycodes_list.append(f"{keycode}:{state}")
|
|
218
|
+
|
|
219
|
+
return keycodes_list
|
ydotool/mousecodes.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Mouse button and action code mappings for ydotool.
|
|
3
|
+
|
|
4
|
+
ydotool 1.0.4 uses hexadecimal bit masks for mouse buttons:
|
|
5
|
+
- Lower bits (0x00-0x07): button identifier
|
|
6
|
+
- Upper bits (0x40=DOWN, 0x80=UP, 0xC0=CLICK): action
|
|
7
|
+
|
|
8
|
+
0x can be omitted.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
MOUSECODES = {
|
|
12
|
+
"left": "0",
|
|
13
|
+
"right": "1",
|
|
14
|
+
"middle": "2",
|
|
15
|
+
"side": "3",
|
|
16
|
+
"extra": "4",
|
|
17
|
+
"forward": "5",
|
|
18
|
+
"back": "6",
|
|
19
|
+
"task": "7",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
ACTIONCODES = {
|
|
23
|
+
"click": "C",
|
|
24
|
+
"down": "4",
|
|
25
|
+
"up": "8",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def get_mousecode(button: str, action: str) -> str | None:
|
|
29
|
+
buttoncode = MOUSECODES.get(button)
|
|
30
|
+
actioncode = ACTIONCODES.get(action)
|
|
31
|
+
|
|
32
|
+
if buttoncode and actioncode:
|
|
33
|
+
return f"{actioncode}{buttoncode}"
|
|
34
|
+
|
|
35
|
+
return None
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ydotool
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python wrapper for ydotool
|
|
5
|
+
Keywords: ydotool,wayland,uinput,automation
|
|
6
|
+
Author: astalunavortex
|
|
7
|
+
Author-email: astalunavortex <astalunavortex@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
17
|
+
Requires-Dist: pytest>=8 ; extra == 'dev'
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Project-URL: Homepage, https://github.com/astalunavortex/ydotool-py
|
|
20
|
+
Project-URL: Repository, https://github.com/astalunavortex/ydotool-py
|
|
21
|
+
Project-URL: Issues, https://github.com/astalunavortex/ydotool-py/issues
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# ydotool-py
|
|
26
|
+
|
|
27
|
+
[](https://pypi.org/project/ydotool/)
|
|
28
|
+
[](https://pypi.org/project/ydotool/)
|
|
29
|
+
[](https://opensource.org/licenses/MIT)
|
|
30
|
+
|
|
31
|
+
Python wrapper for [ydotool](https://github.com/ReimuNotMoe/ydotool) - a generic command-line automation tool that works on **Wayland**, **X11**, and even **TTY**.
|
|
32
|
+
|
|
33
|
+
Unlike `pyautogui`, `ydotool` operates at the kernel level via `/dev/uinput`, making it compatible with any Linux desktop environment.
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- **Mouse**: move, click, scroll
|
|
38
|
+
- **Keyboard**: type text, press keys, hotkey combinations
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
### 1. Install ydotool
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Debian/Ubuntu
|
|
46
|
+
sudo apt install ydotool
|
|
47
|
+
|
|
48
|
+
# Arch Linux
|
|
49
|
+
sudo pacman -S ydotool
|
|
50
|
+
|
|
51
|
+
# Fedora
|
|
52
|
+
sudo dnf install ydotool
|
|
53
|
+
```
|
|
54
|
+
If your distro doesn't have `ydotool`, you can build it from source [ydotool](https://github.com/ReimuNotMoe/ydotool)
|
|
55
|
+
|
|
56
|
+
### 2. Start the daemon
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Enable for current user
|
|
60
|
+
systemctl --user enable --now ydotool.service
|
|
61
|
+
|
|
62
|
+
# Or run manually
|
|
63
|
+
ydotoold
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 4. Install Python package
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Add with uv
|
|
70
|
+
uv add ydotool
|
|
71
|
+
|
|
72
|
+
# Or install with pip
|
|
73
|
+
pip install ydotool
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Quick Start
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from ydotool import do_click, do_mousemove, do_type, do_key, do_wheel
|
|
80
|
+
|
|
81
|
+
# Move mouse and click
|
|
82
|
+
do_mousemove(500, 300)
|
|
83
|
+
do_click('left', 'click')
|
|
84
|
+
|
|
85
|
+
# Type text
|
|
86
|
+
do_type("Hello, Wayland!")
|
|
87
|
+
|
|
88
|
+
# Press key combination (Ctrl+S)
|
|
89
|
+
do_key(['ctrl:1', 's:1', 's:0', 'ctrl:0'])
|
|
90
|
+
|
|
91
|
+
# Scroll down
|
|
92
|
+
do_wheel(vertical=-3)
|
|
93
|
+
|
|
94
|
+
# Double-click
|
|
95
|
+
do_click('left', 'click', repeat=2)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## API Reference
|
|
99
|
+
|
|
100
|
+
### Mouse Control
|
|
101
|
+
|
|
102
|
+
#### `do_click(button, action, repeat=None, next_delay=None)`
|
|
103
|
+
|
|
104
|
+
Perform mouse click action.
|
|
105
|
+
|
|
106
|
+
- **button**: `'left'`, `'right'`, `'middle'`, `'side'`, `'extra'`, `'forward'`, `'back'`
|
|
107
|
+
- **action**: `'click'`, `'down'`, `'up'`
|
|
108
|
+
- **repeat**: Number of repetitions
|
|
109
|
+
- **next_delay**: Delay between events in milliseconds
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
do_click('left', 'click') # Single left click
|
|
113
|
+
do_click('right', 'click', repeat=2) # Double right click
|
|
114
|
+
do_click('left', 'down') # Press and hold
|
|
115
|
+
do_click('left', 'up') # Release
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### `do_mousemove(x, y, absolute=True)`
|
|
119
|
+
|
|
120
|
+
Move mouse cursor.
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
do_mousemove(500, 300) # Absolute position
|
|
124
|
+
do_mousemove(100, -50, absolute=False) # Relative movement
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### `do_wheel(vertical=0, horizontal=0)`
|
|
128
|
+
|
|
129
|
+
Scroll mouse wheel.
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
do_wheel(vertical=3) # Scroll up
|
|
133
|
+
do_wheel(vertical=-5) # Scroll down
|
|
134
|
+
do_wheel(horizontal=2) # Scroll right
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Keyboard Control
|
|
138
|
+
|
|
139
|
+
#### `do_type(text, key_delay=None, key_hold=None)`
|
|
140
|
+
|
|
141
|
+
Type text string. Handles special characters automatically.
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
do_type("Hello, World!")
|
|
145
|
+
do_type("Slow typing", key_delay=100)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### `do_key(keys)`
|
|
149
|
+
|
|
150
|
+
Press keyboard keys in sequence.
|
|
151
|
+
|
|
152
|
+
Keys are specified as `'keyname:state'` where state is `'1'` (down) or `'0'` (up).
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
# Press Enter
|
|
156
|
+
do_key(['enter:1', 'enter:0'])
|
|
157
|
+
|
|
158
|
+
# Ctrl+S
|
|
159
|
+
do_key(['ctrl:1', 's:1', 's:0', 'ctrl:0'])
|
|
160
|
+
|
|
161
|
+
# Alt+Tab
|
|
162
|
+
do_key(['alt:1', 'tab:1', 'tab:0', 'alt:0'])
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
See [keycodes.py](src/ydotool/keycodes.py) for the complete list of key names.
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
## Troubleshooting
|
|
169
|
+
|
|
170
|
+
### "Failed to connect to ydotoold"
|
|
171
|
+
|
|
172
|
+
Make sure the daemon is running:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
systemctl --user status ydotool.service
|
|
176
|
+
# or
|
|
177
|
+
pgrep -a ydotoold
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### "Permission denied" on `/dev/uinput`
|
|
181
|
+
|
|
182
|
+
Check udev rules and log out/in:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
ls -l /dev/uinput
|
|
186
|
+
# Should show your user has access
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Mouse coordinates are wrong
|
|
190
|
+
|
|
191
|
+
Disable mouse acceleration and set mouse sensetivity to default in your desktop environment settings.
|
|
192
|
+
|
|
193
|
+
### Keys not working
|
|
194
|
+
|
|
195
|
+
Check that you're using the correct format: `'keyname:state'` (e.g., `'ctrl:1'`, not just `'ctrl'`).
|
|
196
|
+
|
|
197
|
+
## License
|
|
198
|
+
|
|
199
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) for details.
|
|
200
|
+
|
|
201
|
+
[ydotool](https://github.com/ReimuNotMoe/ydotool) is licensed under [AGPL-3.0](https://github.com/ReimuNotMoe/ydotool/blob/master/LICENSE).
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
ydotool/__init__.py,sha256=-OszjBgP8xWVGwjZ27vGWn4rbGkxRly9kMPA5GgAkVw,217
|
|
2
|
+
ydotool/client.py,sha256=C7LV_QurYgenqVZKb4ETgByU3wlBijjoAgHA3Jl92uU,2956
|
|
3
|
+
ydotool/keycodes.py,sha256=KfnSuv5SFdV6Xm6G_VQjCwfGMEzFShmZAfemN2xqmS4,4471
|
|
4
|
+
ydotool/mousecodes.py,sha256=d1i7FDzivDMOPE3zd4ommNEUkGk8NYAhg7qBz5H0ivM,653
|
|
5
|
+
ydotool-0.1.0.dist-info/licenses/LICENSE,sha256=YStTElrmK_rQ1i_Z1Ltz589jy9cdXTDvA8MRpGV1fyg,1071
|
|
6
|
+
ydotool-0.1.0.dist-info/WHEEL,sha256=cmC5s21ojypbVslldL7IJq3hjZH-tINy4rziKePFsG0,81
|
|
7
|
+
ydotool-0.1.0.dist-info/METADATA,sha256=gLYx7tFcDI4WIzcRpeJs3z_7ZsNx8fVcANKNpYayJYw,4996
|
|
8
|
+
ydotool-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 astalunavortex
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|