Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OS X support possible? #11

Closed
crazyquark opened this issue Oct 20, 2016 · 34 comments
Closed

OS X support possible? #11

crazyquark opened this issue Oct 20, 2016 · 34 comments

Comments

@crazyquark
Copy link
Contributor

crazyquark commented Oct 20, 2016

Do you think an OS X port would be possible?

@boppreh
Copy link
Owner

boppreh commented Oct 20, 2016

The codebase is organized to easily allow supporting new systems. It is cleanly divided in frontend (numerous methods, flexible calls, OS independent, high level) and backend (e.g. _winkeyboard, very few methods, strict parameters, one module for each OS, low level).

Unfortunately I have zero access or experience with any Apple machine. I would gladly accept contributions, but I'm afraid I cannot provide OS X support myself.

@boppreh
Copy link
Owner

boppreh commented Nov 5, 2016

So, no news on this issue. Unfortunately I still don't have access to any OS X computer. I'll have to close this issue for the time being, but I would be extremely happy if somebody stepped in with a pull request. Mimicking the API of the _winkeyboard and _nixkeyboard.py should be enough.

@boppreh boppreh closed this as completed Nov 5, 2016
@boppreh
Copy link
Owner

boppreh commented Nov 13, 2016

I'm reopening this issue in hopes of attracting contributors.

Suggestions on how to add OS X support, part 1 (part 2)

To integrate an OS X module two things are needed:

  1. Create a backend module, like the _nixkeyboard and _winkeyboard.
  2. Import the correct backend depending on platform.

A backend is just a module that exposes four low-level functions, namely:

  • press(scan_code), release(scan_code): given a numeric scan code (e.g. 56), emit a "fake" event pressing or releasing that key.
  • map_char(name): given a key name (e.g. "enter") or character (e.g. "5") return a tuple (scan_code, modifiers). For example map_char('%') -> (6, ['shift']), which means the character "%" can be typed by holding shift and pressing the key with scan code 6. Raise ValueError if there's no such key.
  • listen(queue): listens for global keys, creates equivalent KeyboardEvent objects and puts then in queue. Can be blocking or not, your decision.

These four functions will be used by the higher-level part of the library, where functions like add_hotkey are implemented.

Keeping in line with the rest of the library, it would be best if this module had no dependencies, required no compilation step and supported internationalized layouts, but I'm willing to compromise because having something is better than nothing. Feel free to use as much ctypes as struct as you want, though.

While I cannot help with any OS X specific code I would be extremely glad to answer any questions or clarify any points that could help you implement it.

@boppreh boppreh reopened this Nov 13, 2016
@lazka
Copy link

lazka commented Nov 13, 2016

(drive-by comment)

We have some Python only code (depending on pyobjc) for multimedia keys on osx: https://github.com/quodlibet/quodlibet/blob/master/quodlibet/quodlibet/mmkeys/osx.py

maybe it helps as a starting point...

@boppreh
Copy link
Owner

boppreh commented Nov 14, 2016

Suggestions on how to add OS X support, part 2 (part 1)

Part 1 has some details on how to interface with the rest of the library. Now, how would you actually implement that?

I see three main possibilities:

  1. Link to a system library using ctypes. This is how the Windows part hooks keys, for example. Pros: efficient, made for library consumption. Cons: may cause segfaults, care has to be taken to support 32 and 64 bits.
  2. Call another program and parse their input. This is how the Linux part gets its table of key names. Pros: all the heavy lifting is done for you. Cons: may not be available on every system, slower, less flexible.
  3. Reading from a device file. You are just reading a file and parsing the data blocks. This is how the Linux part reads key events. All the benefits of hooking directly into the OS, with none of the drawbacks. Unfortunately this may not be available on OS X, so look around.

@boppreh
Copy link
Owner

boppreh commented Nov 15, 2016

This may be helpful: https://github.com/willwade/PyUserInput/blob/master/pykeyboard/mac.py

@willwade
Copy link

willwade commented Nov 15, 2016

and this.. SavinaRoja/PyUserInput#39

For the record (having looked at this myself..) I don't recommend @boppreh suggestion of 2 - and I don't think 3 would be a massive gain over 1. I think you are stuck with ctypes. Its fast and it works...

@glitchassassin
Copy link
Collaborator

@boppreh, I've just acquired a Macbook and am in the process of adding OSX support to my automation library. I'll see what I can put together for keyboard while I'm at it.

@boppreh
Copy link
Owner

boppreh commented May 2, 2017

Awesome, that's great news. If you need anything please don't hesitate to contact me.

@glitchassassin
Copy link
Collaborator

The PyUserInput code is licensed under the GPL, so I don't want to just copy-paste it into this project. Currently studying it for re-implementation.

@glitchassassin
Copy link
Collaborator

Looks like the API specified above may be incomplete. I added a method init() to re-initialize the key tables.

I have basic key mapping and key emulation functionality implemented. Working on the event listener. It looks like listening to global keypresses will require root.

@glitchassassin
Copy link
Collaborator

Event listener set up. I also completed the mouse back-end (I think). I'll do some more testing and then submit a pull request.

@bandrel
Copy link

bandrel commented May 22, 2017

@glitchassassin do you have a fork I can take a look at and maybe assist with? I see your actively working on it.

@glitchassassin
Copy link
Collaborator

Sure, this fork should be up to date with all my changes.

The testing that I have done so far seems to have been successful. Just working on translating the test cases for my project so I can verify that everything I rely on works well. Let me know if you find any issues!

@bandrel
Copy link

bandrel commented May 22, 2017

Would you mind enabling issues in the fork?

@glitchassassin
Copy link
Collaborator

Done.

@glitchassassin
Copy link
Collaborator

Running into a hitch with double-clicking and dragging. In OSX, both are distinct actions that require specific flags to be set. It's not as simple as "mouse down, mouse move, mouse up".

I am now tracking mouse button state, so if a mouse button is held down, move_to triggers a drag event instead of a mouse move event. Double-clicking might be a little more complicated. Checking.

@boppreh
Copy link
Owner

boppreh commented May 23, 2017

@glitchassassin We could also adapt the other platforms to behave more like this, and let them simulate the appropriate actions when they receive a "composite" event.

@glitchassassin
Copy link
Collaborator

I've been able to implement the "smart" event detection to send drag-and-drop events and double-click events. I'm doing further testing to make sure it's robust, but I think this will avoid restructuring the other platform handlers.

@glitchassassin
Copy link
Collaborator

Fixed all the issues my tests uncovered. Waiting to see if @bandrel finds any more, and then I'll create a pull request.

@skys215
Copy link

skys215 commented Aug 3, 2017

2 month has passed, any progress?
I'm waiting for support macOS.

@boppreh
Copy link
Owner

boppreh commented Aug 3, 2017

Hi @skys215

OS X support was introduced by #72 . This issue was only open because I'm waiting to get more feedback, since I can't test it myself. But I figure it's best to close it now to avoid confusing people.

If you could try and report back if it worked for you, I would be very grateful.

@boppreh boppreh closed this as completed Aug 3, 2017
@skys215
Copy link

skys215 commented Aug 4, 2017

Aha! I have a problem with using keyboard on macOS.
Here's the traceback:

  File "main.py", line 68, in <module>
    keyboard.add_hotkey('control+option+d', activateEvent)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/keyboard/__init__.py", line 319, in add_hotkey
    return hook(handler)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/keyboard/__init__.py", line 340, in hook
    _listener.add_handler(callback)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/keyboard/_generic.py", line 68, in add_handler
    self.start_if_necessary()
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/keyboard/_generic.py", line 36, in start_if_necessary
    self.init()
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/keyboard/__init__.py", line 112, in init
    _os_keyboard.init()
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/keyboard/_nixkeyboard.py", line 110, in init
    build_device()
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/keyboard/_nixkeyboard.py", line 107, in build_device
    device = aggregate_devices('kbd')
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/keyboard/_nixcommon.py", line 141, in aggregate_devices
    uinput = make_uinput()
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/keyboard/_nixcommon.py", line 27, in make_uinput
    uinput = open("/dev/uinput", 'wb')
PermissionError: [Errno 1] Operation not permitted: '/dev/uinput'

I'm running my script with sudo.

@glitchassassin
Copy link
Collaborator

Looks like you're using an older version of the keyboard library. It's using _nixkeyboard instead of _darwinkeyboard. Use pip install --upgrade keyboard to make sure you're running the latest version.

If you're already on 0.10.0, please post the output of sys.platform on your system.

Thanks!

@skys215
Copy link

skys215 commented Aug 10, 2017

Something weird happened, once I upgrade the keyboard I can use the hot key which I set.
But few days after, the hot key is not working anymore. (Under macOS, in a small PyQt program)

I was planning to make a minimal example to reproduce the problem, but it says module 'keyboard' has no attribute 'add_hotkey'.
I'm not so familiar with python, so maybe I ignored something.
Here's the small example which cannot run.

import sys
import keyboard

if __name__ == '__main__':
    keyboard.add_hotkey("alt+shift+c", lambda: print('hello'), suppress=True)
    keyboard.wait()

@boppreh
Copy link
Owner

boppreh commented Aug 10, 2017

@skys215

I don't have OS X to test, but your code seems correct and is working as expected in Windows and Linux. Also, add_hotkey is a high-level function declared in __init__.py, so I don't see how the darwin backend can make it unavailable.

I would suggest checking if your system is ok and reinstalling the package from scratch..

@boppreh
Copy link
Owner

boppreh commented Aug 10, 2017

@skys215

Also, make sure you don't have any python programs or folders named keyboard in your current directory or path. In this case Python could be importing the wrong module.

@skys215
Copy link

skys215 commented Aug 11, 2017

Aha! The filename of this small example was keyboard.py! That's why I was not able to import keyboard. 😅
Sorry for my stupidity.
I'll continue testing on macOS.

@skys215
Copy link

skys215 commented Aug 11, 2017

The hot key works if I run it from python like: python main.py.
But after I packed is as a macOS app with pyinstaller, I can't use the hot key.
I'll report the issue to them.

One problem left with me, is win equivalent with command? And alt with option, control with ctrl? Or I need to set both shortcut depends on which system the program is running on?

@boppreh
Copy link
Owner

boppreh commented Aug 11, 2017

@skys215

Aha! The filename of this small example was keyboard.py! That's why I was not able to import keyboard. 😅
Sorry for my stupidity.
I'll continue testing on macOS.

No problem, glad to help.

The hot key works if I run it from python like: python main.py.
But after I packed is as a macOS app with pyinstaller, I can't use the hot key.
I'll report the issue to them.

Thank you.

One problem left with me, is win equivalent with command? And alt with option, control with ctrl? Or I need to set both shortcut depends on which system the program is running on?

I have never used a mac; is there a well known 1:1 correspondence? If yes, I can easily add aliases to those names. The downside is that every event name is normalized against these aliases, so hooked events will always report as alt even in OS X, and match(event, 'option') will work everywhere. Is that ok? (control -> ctrl is already there).

If you prefer, feel free to open a new issue about this. Or continue posting here. I don't mind, except that I can't mark it as "solved" anymore because this is a closed issue.

@skys215
Copy link

skys215 commented Aug 11, 2017

I will try to run then same file under Windows when I can.
And I'll let you know the result.
Thanks for the help. 😃

@glitchassassin
Copy link
Collaborator

glitchassassin commented Aug 14, 2017

One problem left with me, is win equivalent with command? And alt with option, control with ctrl? Or I need to set both shortcut depends on which system the program is running on?

I have never used a mac; is there a well known 1:1 correspondence? If yes, I can easily add aliases to those names. The downside is that every event name is normalized against these aliases, so hooked events will always report as alt even in OS X, and match(event, 'option') will work everywhere. Is that ok? (control -> ctrl is already there).

When I use RDP from my Mac to a Windows machine, command automatically maps to the Windows key; option maps to the alt key; and control maps to the ctrl key. I think it's safe to say that's the established pattern.

@boppreh
Copy link
Owner

boppreh commented Aug 14, 2017

Ok then, I'll setup the aliases when I get home.

@boppreh
Copy link
Owner

boppreh commented Aug 15, 2017

v0.10.4 should have the Mac->Windows aliases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants