Skip to content

Instantly share code, notes, and snippets.

Created February 26, 2016 21:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/14c860939fd4d818ab18 to your computer and use it in GitHub Desktop.
Save anonymous/14c860939fd4d818ab18 to your computer and use it in GitHub Desktop.
Grab Control-c-c globally
#!/usr/bin/python3
import sys
import signal
from Xlib.display import Display
from Xlib import X, XK
from Xlib.ext import record
from Xlib.protocol import rq
import threading
globs = {'HotkeyCaught':False}
def catch_control_c(*args):
pass
signal.signal(signal.SIGINT,catch_control_c) # do not quit when Control-c is pressed
def toggle_hotkey(SetBool=True):
globs['HotkeyCaught'] = SetBool
# Определить нажатие горячих клавиш глобально в системе
class KeyListener(threading.Thread):
''' Использование:
keylistener = KeyListener()
keylistener.addKeyListener("Control_L+c+c, callable)
Обратить внимание, что необходимо присвоить все возможные комбинации, поскольку порядок нажатия может быть иной, например, "L_CTRL+y+L_SHIFT"
'''
def __init__(self):
threading.Thread.__init__(self)
self.finished = threading.Event()
self.contextEventMask = [X.KeyPress,X.MotionNotify]
# Give these some initial values
# Hook to our display.
self.local_dpy = Display()
self.record_dpy = Display()
self.pressed = []
self.listeners = {}
#--------------------------------------------------------------------------
# need the following because XK.keysym_to_string() only does printable chars
# rather than being the correct inverse of XK.string_to_keysym()
def lookup_keysym(self, keysym):
for name in dir(XK):
if name.startswith("XK_") and getattr(XK, name) == keysym:
return name.lstrip("XK_")
return "[%d]" % keysym
#--------------------------------------------------------------------------
def processevents(self, reply):
if reply.category != record.FromServer:
return
if reply.client_swapped:
print("* received swapped protocol data, cowardly ignored")
return
# Добавил str, иначе получаем ошибку
if not len(str(reply.data)) or ord(str(reply.data[0])) < 2:
# not an event
return
data = reply.data
while len(data):
event, data = rq.EventField(None).parse_binary_value(data, self.record_dpy.display, None, None)
keycode = event.detail
keysym = self.local_dpy.keycode_to_keysym(event.detail, 0)
character = self.lookup_keysym(keysym)
if character:
if event.type == X.KeyPress:
self.press(character)
elif event.type == X.KeyRelease:
self.release(character)
#--------------------------------------------------------------------------
def run(self):
# Check if the extension is present
if not self.record_dpy.has_extension("RECORD"):
print("RECORD extension not found")
sys.exit(1)
r = self.record_dpy.record_get_version(0, 0)
print("RECORD extension version %d.%d" % (r.major_version, r.minor_version))
# Create a recording context; we only want key events
self.ctx = self.record_dpy.record_create_context(
0,
[record.AllClients],
[{
'core_requests': (0, 0),
'core_replies': (0, 0),
'ext_requests': (0, 0, 0, 0),
'ext_replies': (0, 0, 0, 0),
'delivered_events': (0, 0),
'device_events': tuple(self.contextEventMask), #(X.KeyPress, X.ButtonPress),
'errors': (0, 0),
'client_started': False,
'client_died': False,
}])
# Enable the context; this only returns after a call to record_disable_context,
# while calling the callback function in the meantime
self.record_dpy.record_enable_context(self.ctx, self.processevents)
# Finally free the context
self.record_dpy.record_free_context(self.ctx)
#--------------------------------------------------------------------------
def cancel(self):
self.finished.set()
self.local_dpy.record_disable_context(self.ctx)
self.local_dpy.flush()
#--------------------------------------------------------------------------
def press(self, character):
if len(self.pressed) == 3:
self.pressed = []
if character == 'Control_L' or character == 'Control_R':
if len(self.pressed) > 0:
self.pressed = []
self.pressed.append(character)
elif character == 'c':
if len(self.pressed) > 0:
if self.pressed[0] == 'Control_L' or self.pressed[0] == 'Control_R':
self.pressed.append(character)
action = self.listeners.get(tuple(self.pressed), False)
#print('Current action:', str(tuple(self.pressed)))
if action:
action()
#--------------------------------------------------------------------------
def release(self, character):
"""must be called whenever a key release event has occurred."""
# Не засчитывает отпущенный Control
# Кириллическую 'с' распознает как латинскую
if character != 'c':
self.pressed = []
#--------------------------------------------------------------------------
def addKeyListener(self, hotkeys, callable):
keys = tuple(hotkeys.split("+"))
print("Added new keylistener for :",str(keys))
self.listeners[keys] = callable
#--------------------------------------------------------------------------
def result(self):
if globs['HotkeyCaught']:
print('Control-c-c detected!')
globs['HotkeyCaught'] = False
return True
else:
return False
def wait_example():
from time import sleep
while not keylistener.result():
sleep(.5)
keylistener.cancel()
keylistener = KeyListener()
keylistener.addKeyListener("Control_L+c+c",toggle_hotkey)
keylistener.start()
if __name__ == '__main__':
wait_example()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment