Binding any key combination to move spaces shortcuts
You have (a few years ago) addressed a general issue with MacOS' AppleScript that's been haunting me forever…
So I started a web search –kind of feasibility study– naturally stumbled upon python and hunted for code samples to make AS aware of mouse events. Here is what I attempted to construct:
A: A simple AppleScript (> "AS") to start (and end) the "watchout":
on idle {}
set repCount to 4
repeat while repCount>0
set thescript to "python '/Users/someUser/someFolder/Mouse±Key.py'"
set xYz to do shell script thescript
set repCount to RepCount - 1
delay 0.1
end repeat
display dialog "Now: " & (repCount as string) & " runs left…"
end idle
… which will run "Mouse±Key.py" in a loop, limited by "repCount" to 5 runs.
Next I started to look into python and find ways to detect mouse+key events. It turned out that a module "pynput" would have to be installed to your (my) python installation:
python -m pip install pynput
was recommended for this, so I had to install pip first. Well …
The actual *.py script is not very long, due to my criminal neglect of error handling and others.
(As I mentioned: This is a FEASABILITY study! )
Here comes the script – BUT: as I'm no software engineer I won't be able to explain (with authority) why any one special expression is formulated or positioned the way it is. (trial-and-erroooors…)
from pynput import mouse, keyboard
from pynput.keyboard import Key, Listener
import os
def on_press(key):
global keyDown
if key == Key.shift:
keyDown = "57.shift"
else:
keyDown = "none"
def on_click(x, y, button, pressed):
global keyDown
if button.value[1] == 3 and keyDown == "57.shift":
cmd = """
osascript -e 'tell application "System Events" \n key code 123 using {control down}\n delay 0.5\n end tell'
"""
os.system(cmd)
elif button.value[1] == 4 and keyDown == "57.shift":
cmd = """
osascript -e 'tell application "System Events" \n key code 124 using {control down}\n delay 0.5\n end tell'
"""
os.system(cmd)
if not pressed:
listener_key.stop()
listener_mouse.stop()
listener_key = keyboard.Listener(on_press=on_press)
listener_key.start()
listener_mouse = mouse.Listener(on_click=on_click)
listener_mouse.start()
keyDown = "none "
key = "X"
if keyDown == "57.shift":
listener_key.stop()
listener_key.join()
listener_mouse.join()
Okay, what I –as layman– can say:
First 3 lines import pynput modules (not sure, why keyboard and mouse separately) & os.
Next, 2 crucial functions on_press and on_click are defined;
(keyboard also supports on_release, mouse can handle on_scroll and on_move events.)
Between these functions but also in the main thread (therefore global) my variable "keyDown" "transports" information whether the key recognized as Key.shift was pressed. I named its content "57.shift" like AS' "shift" key code.
I needed "keyDown" as I decided to let the on_press function only do the key recognition while on_click evaluates the key, decides what action to take and for the current "run" stops the Listeners.
The imported module os comes into play when the on_… functions call "back" to MacOS' application "System Events" via 2 osascripts (= Open Scripting Architecture language), as AppleScrips.
The osascripts instruct MacOS' own system to invoke 2 "integrated" Mission Control (arrow-key) shortcuts that will switch to left-side (key code 123) and right-side (124) spaces (s.t. called "Desktops").
At the end of the on_click function 2 different Listeners appear, actually to stop their listening (once the osa/AppleScript has worked).
Next, keyboard.Listener and mouse.Listener are eventually defined and start() ed;
these on the other hand are brought into connection with the 2 vital functions.
Remaining code first defines "keyDown" delivers a dummy starting value for key and finally "joins" both Listener threads so they "wait" for each other (simply said).
I mainly researched these codes from pure interest; for testing purposes (unfortunately I use a MacBook with no buttons 4 and 5) I used this line: print(key, "(", button.value[1], ")", keyDown+" >> ", button)
.
WARNING: As there is no error handling strange things may happen if you experiment my code!
( Anybody more softwarely educated than me is welcome to improve or comment my efforts, but please leave my bullet list mostly as is.)