This repository was archived by the owner on Feb 13, 2026. It is now read-only.
Repository navigation
Expand file tree
/
Copy path__init__.py
More file actions
309 lines (249 loc) · 10.5 KB
/
Copy path__init__.py
File metadata and controls
309 lines (249 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# -*- coding: utf-8 -*-
"""
mouse
=====
Take full control of your mouse with this small Python library. Hook global events, register hotkeys, simulate mouse movement and clicks, and much more.
_Huge thanks to [Kirill Pavlov](http://kirillpavlov.com/) for donating the package name. If you are looking for the Cheddargetter.com client implementation, [`pip install mouse==0.5.0`](https://pypi.python.org/pypi/mouse/0.5.0)._
## Features
- Global event hook on all mice devices (captures events regardless of focus).
- **Listen** and **sends** mouse events.
- Works with **Windows** and **Linux** (requires sudo).
- Works with **MacOS** (requires granting accessibility permissions to terminal/python in System Preferences -> Security)
- **Pure Python**, no C modules to be compiled.
- **Zero dependencies** on Windows and Linux. Trivial to install and deploy, just copy the files.
- **Python 2 and 3**.
- Includes **high level API** (e.g. [record](#mouse.record) and [play](#mouse.play).
- Events automatically captured in separate thread, doesn't block main program.
- Tested and documented.
This program makes no attempt to hide itself, so don't use it for keyloggers.
## Usage
Install the [PyPI package](https://pypi.python.org/pypi/mouse/):
$ sudo pip install mouse
or clone the repository (no installation required, source files are sufficient):
$ git clone https://github.com/boppreh/mouse
Then check the [API docs](https://github.com/boppreh/mouse#api) to see what features are available.
## Known limitations:
- Events generated under Windows don't report device id (`event.device == None`). [#21](https://github.com/boppreh/keyboard/issues/21)
- To avoid depending on X the Linux parts reads raw device files (`/dev/input/input*`) but this requires root.
- Other applications, such as some games, may register hooks that swallow all key events. In this case `mouse` will be unable to report events.
"""
# TODO
# - infinite wait
# - mouse.on_move
version = '0.7.1'
import time as _time
import platform as _platform
if _platform.system() == 'Windows':
from. import _winmouse as _os_mouse
elif _platform.system() == 'Linux':
from. import _nixmouse as _os_mouse
elif _platform.system() == 'Darwin':
from. import _darwinmouse as _os_mouse
else:
raise OSError("Unsupported platform '{}'".format(_platform.system()))
from ._mouse_event import ButtonEvent, MoveEvent, WheelEvent, LEFT, RIGHT, MIDDLE, X, X2, UP, DOWN, DOUBLE
from ._generic import GenericListener as _GenericListener
_pressed_events = set()
class _MouseListener(_GenericListener):
def init(self):
_os_mouse.init()
def pre_process_event(self, event):
if isinstance(event, ButtonEvent):
if event.event_type in (UP, DOUBLE):
_pressed_events.discard(event.button)
else:
_pressed_events.add(event.button)
return True
def listen(self):
_os_mouse.listen(self.queue)
_listener = _MouseListener()
def is_pressed(button=LEFT):
""" Returns True if the given button is currently pressed. """
_listener.start_if_necessary()
return button in _pressed_events
def press(button=LEFT):
""" Presses the given button (but doesn't release). """
_os_mouse.press(button)
def release(button=LEFT):
""" Releases the given button. """
_os_mouse.release(button)
def click(button=LEFT):
""" Sends a click with the given button. """
_os_mouse.press(button)
_os_mouse.release(button)
def double_click(button=LEFT):
""" Sends a double click with the given button. """
click(button)
click(button)
def right_click():
""" Sends a right click with the given button. """
click(RIGHT)
def wheel(delta=1):
""" Scrolls the wheel `delta` clicks. Sign indicates direction. """
_os_mouse.wheel(delta)
def move(x, y, absolute=True, duration=0, steps_per_second=120.0):
"""
Moves the mouse. If `absolute`, to position (x, y), otherwise move relative
to the current position. If `duration` is non-zero, animates the movement.
The steps_per_second is only an approximation. Due to the internal sleep's
unreliability it cannot be followed strictly. The less its value is, the more
valid the number becomes.
"""
x = int(x)
y = int(y)
# Requires an extra system call on Linux, but `move_relative` is measured
# in millimeters so we would lose precision.
position_x, position_y = get_position()
if not absolute:
x = position_x + x
y = position_y + y
if not duration:
_os_mouse.move_to(x, y)
return
start_x = position_x
start_y = position_y
dx = x - start_x
dy = y - start_y
if dx == 0 and dy == 0:
_time.sleep(duration)
return
interval_time = 1.0/steps_per_second
start_time = _time.perf_counter()
end_time = start_time + float(duration)
step_start_time = start_time
iteration_start_time = start_time
while iteration_start_time < end_time:
# Sleep to enforce the fps cap, considering the last step's duration and remaining time
last_step_duration = iteration_start_time - step_start_time
remaining_time = end_time - iteration_start_time
corrected_sleep_time = interval_time - last_step_duration
actual_sleep_time = min(remaining_time, corrected_sleep_time)
if actual_sleep_time > 0:
_time.sleep(actual_sleep_time)
step_start_time = _time.perf_counter()
# Move based on the elapsed time to ensure that the duration is valid
current_time = step_start_time - start_time
progress = current_time / duration
_os_mouse.move_to(start_x + dx*progress, start_y + dy*progress)
iteration_start_time = _time.perf_counter()
# Move to the destination to ensure the final position
_os_mouse.move_to(start_x + dx, start_y + dy)
def drag(start_x, start_y, end_x, end_y, absolute=True, duration=0):
"""
Holds the left mouse button, moving from start to end position, then
releases. `absolute` and `duration` are parameters regarding the mouse
movement.
"""
if is_pressed():
release()
move(start_x, start_y, absolute, 0)
press()
move(end_x, end_y, absolute, duration)
release()
def on_button(callback, args=(), buttons=(LEFT, MIDDLE, RIGHT, X, X2), types=(UP, DOWN, DOUBLE)):
""" Invokes `callback` with `args` when the specified event happens. """
if not isinstance(buttons, (tuple, list)):
buttons = (buttons,)
if not isinstance(types, (tuple, list)):
types = (types,)
def handler(event):
if isinstance(event, ButtonEvent):
if event.event_type in types and event.button in buttons:
callback(*args)
_listener.add_handler(handler)
return handler
def on_pressed(callback, args=()):
""" Invokes `callback` with `args` when the left button is pressed. """
return on_button(callback, args, [LEFT], [DOWN])
def on_click(callback, args=()):
""" Invokes `callback` with `args` when the left button is clicked. """
return on_button(callback, args, [LEFT], [UP])
def on_double_click(callback, args=()):
"""
Invokes `callback` with `args` when the left button is double clicked.
"""
return on_button(callback, args, [LEFT], [DOUBLE])
def on_middle_double_click(callback, args=()):
"""
Invokes `callback` with `args` when the left button is double clicked.
"""
return on_button(callback, args, [MIDDLE], [DOUBLE])
def on_right_click(callback, args=()):
""" Invokes `callback` with `args` when the right button is clicked. """
return on_button(callback, args, [RIGHT], [UP])
def on_middle_click(callback, args=()):
""" Invokes `callback` with `args` when the middle button is clicked. """
return on_button(callback, args, [MIDDLE], [UP])
def wait(button=LEFT, target_types=(UP, DOWN, DOUBLE)):
"""
Blocks program execution until the given button performs an event.
"""
from threading import Lock
lock = Lock()
lock.acquire()
handler = on_button(lock.release, (), [button], target_types)
lock.acquire()
_listener.remove_handler(handler)
def get_position():
""" Returns the (x, y) mouse position. """
return _os_mouse.get_position()
def hook(callback):
"""
Installs a global listener on all available mouses, invoking `callback`
each time it is moved, a key status changes or the wheel is spun. A mouse
event is passed as argument, with type either `mouse.ButtonEvent`,
`mouse.WheelEvent` or `mouse.MoveEvent`.
Returns the given callback for easier development.
"""
_listener.add_handler(callback)
return callback
def unhook(callback):
"""
Removes a previously installed hook.
"""
_listener.remove_handler(callback)
def unhook_all():
"""
Removes all hooks registered by this application. Note this may include
hooks installed by high level functions, such as `record`.
"""
del _listener.handlers[:]
def record(button=RIGHT, target_types=(DOWN,)):
"""
Records all mouse events until the user presses the given button.
Then returns the list of events recorded. Pairs well with `play(events)`.
Note: this is a blocking function.
Note: for more details on the mouse hook and events see `hook`.
"""
recorded = []
hook(recorded.append)
wait(button=button, target_types=target_types)
unhook(recorded.append)
return recorded
def play(events, speed_factor=1.0, include_clicks=True, include_moves=True, include_wheel=True):
"""
Plays a sequence of recorded events, maintaining the relative time
intervals. If speed_factor is <= 0 then the actions are replayed as fast
as the OS allows. Pairs well with `record()`.
The parameters `include_*` define if events of that type should be included
in the replay or ignored.
"""
last_time = None
for event in events:
if speed_factor > 0 and last_time is not None:
_time.sleep((event.time - last_time) / speed_factor)
last_time = event.time
if isinstance(event, ButtonEvent) and include_clicks:
if event.event_type == UP:
_os_mouse.release(event.button)
else:
_os_mouse.press(event.button)
elif isinstance(event, MoveEvent) and include_moves:
_os_mouse.move_to(event.x, event.y)
elif isinstance(event, WheelEvent) and include_wheel:
_os_mouse.wheel(event.delta)
replay = play
hold = press
if __name__ == '__main__':
print('Recording... Double click to stop and replay.')
play(record())