boppreh / mouse

Hook and simulate global mouse events in pure Python
MIT License
900 stars 135 forks source link

Pickling keyboard/mouse events unstable? #6

Open reckoner opened 6 years ago

reckoner commented 6 years ago

I have noticed that when I pickle a list of events, and then later try to load the pickle files, that the original list of events is not always recoverable? Are the events not pickle-able in this way?

boppreh commented 6 years ago

Keyboard events (plain object): https://github.com/boppreh/keyboard/blob/d9c616ca71c7013270c3b974b1ba1890283c92d6/keyboard/_keyboard_event.py#L13

Mouse events (named tuple): https://github.com/boppreh/mouse/blob/f369010b27593d74e0d2705cdc77e5208b43e36f/mouse/_mouse_event.py#L18

I don't see any reason why they wouldn't be pickle-able. Do you have an example code that fails?

reckoner commented 6 years ago

I am on a Windows 10 machine. Seems like you have to hook/unhook the keyboard for it to recognize and playback keyboard events loaded from a pickle file.

boppreh commented 6 years ago
import mouse, pickle
pickle.dump(mouse.record(), open('file.pkl', 'wb'))
mouse.play(pickle.load(open('file.pkl', 'rb')))

events = []
mouse.hook(events.append)
pickle.dump(events, open('file.pkl', 'wb'))
mouse.play(pickle.load(open('file.pkl', 'rb')))

Works fine for me in Windows 10. Keyboard events also worked. Do you have an example code that shows the problem?

reckoner commented 6 years ago

Did you start a new Python process to load the pickle file? In other words, you have to save the pickle file in one Python process. (2) Close that process. (3) start a new Python process and load the pickle file. (4) Run the loaded pickle file.

Then, I hope you see that the loaded items will not run in the new Python process without hooking/unhooking the keyboard.

boppreh commented 6 years ago

Hi @reckoner

No, I had tested within the same process, but using two interpreters or restarting one still works, without any hook/unhook. Are you sure this bug is not in your code? It doesn't make much sense for this to be a bug in keyboard/mouse, because:

  1. Events are just dumb objects. Mouse events specially are just namedtuples. I don't see how they can possibly fail to pickle.
  2. Hooking/unhooking has no relation to event playback. They are two different Windows APIs.

Can you make a small test case showing the issue? It's fine if you ask "# here you restart the interpreter".

reckoner commented 6 years ago

Using Python 2.7 on Windows 10 (Build 15063.674)

import keyboard
a  = keyboard.record()
# type some stuff
import cPickle
cPickle.dump(a,open('test.pkl','w'))
# close this Python session

Now, in a completely new Python session (Python 2.7)

import keyboard
import cPickle
a=cPickle.load(open('test.pkl'))
keyboard.play(a)

This will not work for me unless I do the hook/unhook dance.

boppreh commented 6 years ago

Ok, I can reproduce, the issue now, and the "hook/unhook dance" works by starting the event listener. Somehow Python2 requires an active Windows hook to be able to replay events, which makes no sense. Note that unhook doesn't unhook the low-level hook. Still investigating.

By the way, you can use keyboard._listener.start_if_necessary() as a replacement for the hook/unhook dance.