def loop(dpy: Display, actions: dict): try: while True: evt = dpy.next_event() process(dpy, actions, evt) finally: dpy.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 XTestSendContext(object): """ This class manages a connection to the X-Server and allows to send key events. """ def __init__(self): # open a connection to the x server self.display = Display() # get the root window of the default screen self.root = self.display.screen().root def send_key_event(self, key_code, key_down): """ Sends a key event to the focused window using python-xlibs fake_input method. :param key_code: Integer code of the key event that should be sent :param key_down: True = key press event, False = key release event """ # select the right event type depending on key_down event_type = X.KeyPress if key_down else X.KeyRelease # fake keyboard event fake_input(self.display, event_type, key_code) # sync connection to ensure that the input is actually sent to the x server self.display.sync() def close(self): """ Closes the connection to the x server """ self.display.close()
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): 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()
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()
def stop(self): if self.record_thread.is_alive(): conn = Display() conn.record_disable_context(self.context) conn.close() print('display stopped recording') self.record_thread.join() print('recording thread ended')
def stop(self): # hack for unreleased context # https://github.com/SavinaRoja/PyUserInput/issues/48 from Xlib.display import Display display = Display() display.record_disable_context(self.ctx) display.close() return super().stop()
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()
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 Xdisplay(): disp = None try: disp = Display() yield disp except: if disp is not None: disp.close() raise
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 get_min_resolution(DISPLAY=None): if DISPLAY is None: DISPLAY = getenv('DISPLAY', ':0') display = Display(DISPLAY) try: n_screens = display.screen_count() heights = set() for idx in range(n_screens): screen = display.screen(idx) heights.add(screen['height_in_pixels']) return min(heights) finally: display.close()
class Record: def __init__(self): self.record_thread = threading.Thread( target=self._record, name='x keyboard listener thread') self.recording_connection = Display() self.recording_connection.set_error_handler( self._record_display_error_handler) if not self.recording_connection.has_extension("RECORD"): raise Exception("RECORD extension not found") r = self.recording_connection.record_get_version(0, 0) print("RECORD extension version %d.%d" % (r.major_version, r.minor_version)) self.context = self.recording_connection.record_create_context( 0, [record.AllClients], CONTEXT_FILTER) def start(self): self.recording_connection.sync() self.record_thread.start() def stop(self): if self.record_thread.is_alive(): conn = Display() conn.record_disable_context(self.context) conn.close() print('display stopped recording') self.record_thread.join() print('recording thread ended') # # xlib plugs # def _record(self): self.recording_connection.record_enable_context( self.context, self.handler) self.recording_connection.record_free_context(self.context) self.recording_connection.close() def _record_display_error_handler(self, exception, *args): print('Error at record display: {}'.format(exception), file=sys.stderr) def handler(self, reply): data = reply.data while len(data): event, data = rq.EventField(None).parse_binary_value( data, self.recording_connection.display, None, None) if event.type == X.KeyPress: format_key_event(event)
def main(argv): display = Display() thread = EventThread(display) thread.start() time.sleep(1) screen = display.screen() # The get_property call should not deadlock, despite the blocking next_event # call in the thread. atom = display.intern_atom('_XROOTPMAP_ID', True) response = screen.root.get_property(atom, Xatom.PIXMAP, 0, 1) print('get_property response: %r' % response) display.close()
def initMouseAndGetSquare(): """ init gtk window and grab mouse """ w = gtk.gdk.get_default_root_window() """change cursor""" cursor = gtk.gdk.Cursor(gtk.gdk.CROSSHAIR) gtk.gdk.pointer_grab(w, False, 0, None, cursor, 0) """ first grab all mouse events """ display = Display(':0') root = display.screen().root root.grab_pointer(True, X.ButtonPressMask | X.ButtonReleaseMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime) mouse_handle = listenToMouse() mouse_handle.run() display.close() """release mouse""" gtk.gdk.pointer_ungrab() del display return w
class XInputSendContext(object): """ This class manages a connection to the X-Server and allows to send key events. """ def __init__(self): # open a connection to the x server self.display = Display() # get the root window of the default screen self.root = self.display.screen().root def send_key_event(self, key_code, key_down): """ Sends a key event to the focused window using python-xlibs send_input method. :param key_code: Integer code of the key event that should be sent :param key_down: True = key press event, False = key release event """ # get the window that has the input focus focus_window = self.display.get_input_focus().focus # select the right event class depending on key_down event_class = KeyPress if key_down else KeyRelease # create event event = event_class(time=X.CurrentTime, root=self.root, window=focus_window, same_screen=0, child=0, root_x=0, root_y=0, event_x=0, event_y=0, state=0, detail=key_code) # send event to the window that has the input focus focus_window.send_event(event) def close(self): """ Closes the connection to the x server """ 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 WM(object): def __init__(self): self.display = Display() self.root_win = self.display.screen().root self.go_on = True # var to keep looping events (or not) self.notifs_on = False self.poweroff_time = 0 clear_log() # clear the log file once # kobo touch screen dimension: 600x800px (minus the dock) self.full_width = 600 self.full_height = 740 self.keyboard_height = 0 self.keyboard_on = False self.active_window = None # may be transient self.top_win_list = [] # list of the top window to cycle in self.top_win_pos = -1 # postition in the top win list, -1 means you have to start over self.transient_of = { } # key: win, val: set of windows transient for win # windows of wm-related apps self.wm_keyboard, self.wm_launcher, self.wm_dock, self.wm_notifs = None, None, None, None # codes for the keys to catch self.f1_codes = set( code for code, index in self.display.keysym_to_keycodes(XK.XK_F1)) self.f2_codes = set( code for code, index in self.display.keysym_to_keycodes(XK.XK_F2)) self.f3_codes = set( code for code, index in self.display.keysym_to_keycodes(XK.XK_F3)) self.f4_codes = set( code for code, index in self.display.keysym_to_keycodes(XK.XK_F4)) self.f9_codes = set( code for code, index in self.display.keysym_to_keycodes(XK.XK_F9)) XK.load_keysym_group('xf86') self.poweroff_codes = set( code for code, index in self.display.keysym_to_keycodes( XK.XK_XF86_PowerOff)) self.poweroff_codes.update( set(code for code, index in self.display.keysym_to_keycodes(XK.XK_F12))) self.launch1_codes = set( code for code, index in self.display.keysym_to_keycodes( XK.XK_XF86_Launch1)) self.launch1_codes.update( set(code for code, index in self.display.keysym_to_keycodes(XK.XK_F11))) # error catcher error_catcher = CatchError(BadAccess) self.root_win.change_attributes( event_mask=X.SubstructureRedirectMask, onerror=error_catcher, background_pixel=self.display.screen().white_pixel) self.display.sync() error = error_catcher.get_error() if error: sys.exit(1) # grab root window key events self.grab_root_key(self.poweroff_codes, X.NONE) self.grab_root_key(self.launch1_codes, X.NONE) self.grab_root_key(self.f1_codes, X.NONE) self.grab_root_key(self.f2_codes, X.NONE) self.grab_root_key(self.f3_codes, X.NONE) self.grab_root_key(self.f4_codes, X.NONE) self.grab_root_key(self.f9_codes, X.NONE) # handlers self.display.set_error_handler(self.x_error_handler) self.event_dispatch_table = { X.MapRequest: self.handle_map_request, X.ConfigureRequest: self.handle_configure_request, X.MappingNotify: self.handle_mapping_notify, X.UnmapNotify: self.handle_unmapping_notify, X.KeyPress: self.handle_key_press, X.KeyRelease: self.handle_key_release } def grab_root_key(self, codes, modifier): for code in codes: self.root_win.grab_key(code, modifier, 1, X.GrabModeAsync, X.GrabModeAsync) # top level window utility functions def win_show(self): window = self.top_win_list[self.top_win_pos] # assumes it exists # map the window window.map() # map transient window if any if window in self.transient_of: win = None for win in self.transient_of[window]: win.map() # use last transient as active window if win: self.active_window = win else: self.active_window = window else: # set the window as the active one self.active_window = window def win_hide(self): window = self.top_win_list[self.top_win_pos] # assumes it exists # unmap the window window.unmap() # un map transient window if any if window in self.transient_of: for win in self.transient_of[window]: win.unmap() # unset active window self.active_window = None def win_remove(self): window = self.top_win_list.pop(self.top_win_pos) # assumes it exists # remove entry in transient dict if any if window in self.transient_of: del self.transient_of[window] window.destroy() self.active_window = None # event handling functions def x_error_handler(self, err, request): log('X protocol error: {0}'.format(err)) def loop(self): # Load every wm app before starting the actual loop try: self.load_wmapps() except KeyboardInterrupt: raise # Loop until go_on, Ctrl+C or exceptions > MAX_EXCEPTION times. errors = 0 while self.go_on: try: self.handle_event() except KeyboardInterrupt: raise except: errors += 1 if errors > MAX_EXCEPTIONS: sys.exit(1) close_log() self.display.close() def load_wmapps(self): # launch the other apps: python_path = find_full_path('python') keyboard_path = find_full_path('matchbox-keyboard') if not keyboard_path or not python_path: raise KeyboardInterrupt system([python_path, kobowm_path + '/launcher.py']) system([python_path, kobowm_path + '/dock.py']) system([python_path, kobowm_path + '/notifzmq.py']) system([keyboard_path]) # catch the events until every wm app has a window while not all((self.wm_notifs, self.wm_keyboard, self.wm_launcher, self.wm_dock)): try: event = self.display.next_event() except ConnectionClosedError: log('Display connection closed by server') raise KeyboardInterrupt # ignore events that are not map requests if event.type == X.MapRequest: event.window.map() if str(event.window.get_wm_name()) == 'Keyboard': self.wm_keyboard = event.window self.keyboard_height = event.window.get_geometry().height event.window.configure(x=0, y=self.full_height - self.keyboard_height, width=self.full_width) # catch the keyboard events to later redirect them to the active window event.window.change_attributes(event_mask=X.KeyPressMask | X.KeyReleaseMask) event.window.unmap() elif str(event.window.get_wm_name()) == 'kobowm-dock': self.wm_dock = event.window event.window.configure(x=0, y=self.full_height, width=self.full_width, height=60) event.window.unmap() elif str(event.window.get_wm_name()) == 'kobowm-launcher': self.wm_launcher = event.window event.window.configure(x=0, y=0, height=self.full_height, width=self.full_width) event.window.unmap() elif str(event.window.get_wm_name()) == 'kobowm-notifications': self.wm_notifs = event.window event.window.configure(x=380, y=20, height=100, width=200) event.window.unmap() else: # there is an unexpexted window: stop raise KeyboardInterrupt # finally map again the dock self.wm_dock.map() def handle_event(self): try: event = self.display.next_event() except ConnectionClosedError: log('Display connection closed by server') raise KeyboardInterrupt if event.type in self.event_dispatch_table: self.event_dispatch_table[event.type](event) else: log('unhandled event: {event}'.format(event=event)) def handle_configure_request(self, event): window = event.window args = {'border_width': 1} if event.value_mask & X.CWX: args['x'] = event.x if event.value_mask & X.CWY: args['y'] = event.y if event.value_mask & X.CWWidth: args['width'] = event.width if event.value_mask & X.CWHeight: args['height'] = event.height if event.value_mask & X.CWSibling: args['sibling'] = event.above if event.value_mask & X.CWStackMode: args['stack_mode'] = event.stack_mode window.configure(**args) def handle_map_request(self, event): event.window.map() # handle transient windows transient_for = event.window.get_wm_transient_for() if transient_for: self.active_window = event.window if transient_for not in self.transient_of: self.transient_of[transient_for] = [] self.transient_of[transient_for].append(event.window) else: if self.active_window: self.active_window.unmap() self.active_window = event.window self.top_win_list.append(event.window) self.top_win_pos = len(self.top_win_list) - 1 # use all the available screen self.active_window.configure(x=-1, y=-1, width=self.full_width, height=self.full_height) # hide the keyboard if it was open if self.keyboard_on: self.wm_keyboard.unmap() self.keyboard_on = False def handle_mapping_notify(self, event): # necessary by documentation self.display.refresh_keyboard_mapping(event) def handle_unmapping_notify(self, event): # used to handle windows that get unmapped not by this wm directly transient_for = event.window.get_wm_transient_for() if transient_for: self.transient_of[transient_for].remove(event.window) if self.active_window == event.window: self.active_window = transient_for elif event.window in self.top_win_list: self.top_win_list.remove(event.window) if self.active_window == event.window: self.active_window = None self.top_win_pos = -1 def handle_key_press(self, event): if event.window == self.wm_keyboard: return # do nothing: will be sent to the active window on release if event.detail in self.poweroff_codes: self.go_on = False elif event.detail in self.launch1_codes: self.action_apps() elif event.detail in self.f1_codes: self.action_tasks() elif event.detail in self.f2_codes: self.action_keyboard() elif event.detail in self.f3_codes: system(XTERM_COMMAND) elif event.detail in self.f4_codes: self.action_close() elif event.detail in self.f9_codes: self.action_notifs() def handle_key_release(self, event): if event.window == self.wm_keyboard: # focus on the active window and emulate key press/release self.display.sync() self.display.set_input_focus(self.active_window, X.RevertToParent, X.CurrentTime) fake_input(self.display, X.KeyPress, event.detail) fake_input(self.display, X.KeyRelease, event.detail) self.display.sync() # action performer functions def action_apps(self): if not self.active_window: self.active_window = self.wm_launcher self.active_window.map() elif self.active_window == self.wm_launcher: self.active_window.unmap() self.active_window = None else: # hide the active window (and the stack if it's transient) self.win_hide() # start over with the cycling self.top_win_pos = -1 self.active_window = self.wm_launcher self.active_window.map() def action_tasks(self): if not self.top_win_list: return if self.top_win_pos != -1 and self.top_win_pos < len( self.top_win_list): self.win_hide() self.top_win_pos += 1 if self.top_win_pos >= len(self.top_win_list): self.top_win_pos = 0 self.win_show() def action_keyboard(self): if self.keyboard_on: self.wm_keyboard.unmap() self.active_window.configure(height=self.full_height) self.keyboard_on = False elif self.active_window and self.active_window != self.wm_launcher: self.wm_keyboard.map() self.active_window.configure(height=self.full_height - self.keyboard_height) self.wm_keyboard.configure(stack_mode=X.Above) self.keyboard_on = True def action_close(self): if not self.active_window: return # nothing to close here if self.active_window in self.top_win_list: self.win_remove() else: # it's a transient window transient_for = self.active_window.get_wm_transient_for() self.transient_of[transient_for].remove(self.active_window) self.active_window.destroy() self.active_window = transient_for # close the keyboard if open if self.keyboard_on: self.wm_keyboard.unmap() self.keyboard_on = False def action_notifs(self): if self.notifs_on: self.wm_notifs.unmap() log('Notification window hidden') else: self.wm_notifs.configure(stack_mode=X.Above) self.wm_notifs.map() log('Notification window visible') self.notifs_on = not self.notifs_on
'get_memory_bus_width': display.nvcontrol_get_memory_bus_width(gpu), 'get_total_dedicated_gpu_memory': display.nvcontrol_get_total_dedicated_gpu_memory(gpu), 'get_used_dedicated_gpu_memory': display.nvcontrol_get_used_dedicated_gpu_memory(gpu), 'get_curr_pcie_link_width': display.nvcontrol_get_curr_pcie_link_width(gpu), 'get_max_pcie_link_width': display.nvcontrol_get_max_pcie_link_width(gpu), 'get_curr_pcie_link_generation': display.nvcontrol_get_curr_pcie_link_generation(gpu), 'get_encoder_utilization': display.nvcontrol_get_encoder_utilization(gpu), 'get_decoder_utilization': display.nvcontrol_get_decoder_utilization(gpu), 'get_current_performance_level': display.nvcontrol_get_current_performance_level(gpu), 'get_gpu_nvclock_offset': display.nvcontrol_get_gpu_nvclock_offset(gpu, perf_level), 'get_mem_transfer_rate_offset': display.nvcontrol_get_mem_transfer_rate_offset(gpu, perf_level), 'get_cooler_manual_control_enabled': display.nvcontrol_get_cooler_manual_control_enabled(gpu), 'get_fan_duty': display.nvcontrol_get_fan_duty(fan), 'get_fan_rpm': display.nvcontrol_get_fan_rpm(fan), 'get_coolers_used_by_gpu': display.nvcontrol_get_coolers_used_by_gpu(gpu), 'get_max_displays': display.nvcontrol_get_max_displays(gpu), 'get_name': display.nvcontrol_get_name(gpu), 'get_driver_version': display.nvcontrol_get_driver_version(gpu), 'get_vbios_version': display.nvcontrol_get_vbios_version(gpu), 'get_gpu_uuid': display.nvcontrol_get_gpu_uuid(gpu), 'get_utilization_rates': display.nvcontrol_get_utilization_rates(gpu), 'get_performance_modes': display.nvcontrol_get_performance_modes(gpu), 'get_gpu_nvclock_offset_range': display.nvcontrol_get_gpu_nvclock_offset_range(gpu, perf_level), 'get_mem_transfer_rate_offset_range': display.nvcontrol_get_mem_transfer_rate_offset_range(gpu, perf_level), 'get_clock_info': display.nvcontrol_get_clock_info(gpu) } pprint(dic) display.close()
class TrayerWatch(EventInput): def __init__(self, args=None): command = ['trayer'] self.default_args = { 'edge': 'top', 'align': 'right', 'widthtype': 'request', 'expand': 'true', 'SetDockType': 'true', 'transparent': 'true', 'alpha': '0', 'height': '16', 'margin': '0', 'tint': '0x29b2e', } if args is not None: self.default_args.update(args) for key, val in self.default_args.items(): command += ["--%s" % (key), str(val)] super(TrayerWatch, self).__init__(command) self.proc.stdin.close() self.proc.stdout.close() # search for running trayer window self.display = Display() root = self.display.screen().root self.trayer = self.find_tray_window(root) assert self.trayer is not None, 'Panel not found!' # activate ConfigureNotify-Events for self.trayer self.trayer.change_attributes(event_mask=X.StructureNotifyMask) def find_tray_window(self, root, tray_name='trayer'): children = root.query_tree().children for window in children: if window.get_wm_class() and window.get_wm_class()[1] == tray_name: return window res = self.find_tray_window(window, tray_name) if res: return res return None def watch_trayer_non_blocking(self): while self.display.pending_events() > 0: event = self.display.next_event() if event.type != X.ConfigureNotify: continue if event.window != self.trayer: continue def get_width(self): self.width = self.trayer.get_geometry().width return self.width + int(self.default_args['margin']) def kill(self): self.proc.kill() self.display.close() def fileno(self): return self.display.fileno() def process(self): self.watch_trayer_non_blocking()
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 XwmSession(object): def __init__(self, winman=None, keybinder=None): self.winman = winman self.kb_map = keybinder self.current_event = None self.display = Display() self.root = self.display.screen().root self.width = self.root.get_geometry().width self.height = self.root.get_geometry().height self.root.change_attributes(event_mask=X.SubstructureRedirectMask) self.startup_ops = [self._init_keybinds] self.loop_ops = [self._handle_events] def startup(self, method): logger.info(f"Adding method ({method}) to startup operations") self.startup_ops.append(method) return method def onloop(self, method): logger.info(f"Adding method ({method}) to session loop operations") self.loop_ops.append(method) return method def run(self): try: logger.info("Running startup operations") for func in self.startup_ops: func() logger.info("Running session loops") while True: time.sleep(REFRESH_RATE) for func in self.loop_ops: func() except KeyboardInterrupt: logger.info("Keyboard interrupt receieved, closing the display") self.close_display() except Exception: logger.exception("Unkown exception has occured") self.close_display() def _handle_events(self): # handle events # Why are these events blacklisted? ignored_events = [3, 33, 34, 23] if self.display.pending_events() == 0: return self.current_event = self.display.next_event() logger.info(f"Handling event {self.current_event}") if self.current_event.type == X.MapRequest: self._handle_map() elif self.current_event.type == X.KeyPress: self._handle_key_press() elif self.current_event.type in ignored_events: pass else: pass def _handle_map(self): logger.info("Handling map event") window = self.current_event.window self.winman.spawn(window, window.get_wm_name(), active=True) window.map() def _init_keybinds(self): logger.info("Initializing keybindings") self.keybinds = {} mod = X.Mod1Mask for key, v in self.kb_map.items(): # Modifier is hardcoded in instead being taken from KeyBinder code = self.display.keysym_to_keycode(str_to_keysym[key]) self.root.grab_key(code, mod, 1, X.GrabModeAsync, X.GrabModeAsync) self.keybinds[code] = self.kb_map[key] def _handle_key_press(self): logger.info("Handling key press event") try: self.keybinds[self.current_event.detail]() except KeyError as e: logger.exception(e) logger.error("unable to process key press") except Exception as e: logger.exception(e) logger.error("something when wrong with keyfunc") def close_display(self): logger.info("Closing display") self.display.close()
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()
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
class TrayerWatch(EventInput): def __init__(self, cmd, args): command = [cmd] self.default_args = { 'edge': 'top', 'align': 'right', #widthtype': 'request', 'width': '4', 'expand': 'false', 'SetDockType': 'true', #'SetPartialStrut': 'true', 'transparent': 'true', 'alpha': '0', 'height': '20', 'margin': '0', 'tint': '0x00000', } if args is not None: self.default_args.update(args) for key, val in self.default_args.items(): command += ["--%s" % (key), str(val)] self.display = Display() root = self.display.screen().root # get root window's current event mask and replace it in order to wait # passively for the trayer window old_mask = root.get_attributes().your_event_mask root.change_attributes(event_mask=X.SubstructureNotifyMask) self.display.sync() super(TrayerWatch, self).__init__(command) self.proc.stdin.close() self.proc.stdout.close() # wait passively for trayer to create its window while True: event = self.display.next_event() self.trayer = self.find_tray_window(root, cmd) if self.trayer is not None: break # revert root window event_mask to remove unnecessary wakeups root.change_attributes(event_mask=old_mask) # activate ConfigureNotify-Events for self.trayer self.trayer.change_attributes(event_mask=X.StructureNotifyMask) def find_tray_window(self, root, tray_name): children = root.query_tree().children for window in children: if window.get_wm_class() and window.get_wm_class()[1] == tray_name: return window res = self.find_tray_window(window, tray_name) if res: return res return None def watch_trayer_non_blocking(self): while self.display.pending_events() > 0: event = self.display.next_event() if event.type != X.ConfigureNotify: continue if event.window != self.trayer: continue def get_width(self): self.width = self.trayer.get_geometry().width return self.width + int(self.default_args['margin']) def kill(self): self.proc.kill() self.display.close() def fileno(self): return self.display.fileno() def process(self): self.watch_trayer_non_blocking()
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() if catch.get_error(): return False catch = error.CatchError(error.BadCursor) if not self.modifiers: # We grab Super+click so that we can forward it to the window manager and allow Super+click bindings (window move, resize, etc.) self.window.grab_button(X.AnyButton, X.Mod4Mask, True, X.ButtonPressMask, X.GrabModeSync, X.GrabModeAsync, X.NONE, X.NONE) self.display.flush() 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 range(mask + 1) if not (x & ~mask)] def idle(self): self.emit("activate") return False def activate(self): GLib.idle_add(self.run) # Get which window manager we're currently using (Marco, Compiz, Metacity, etc...) def get_wm(self): name = '' wm_check = self.display.get_atom('_NET_SUPPORTING_WM_CHECK') win_id = self.window.get_full_property(wm_check, X.AnyPropertyType) if win_id: w = self.display.create_resource_object("window", win_id.value[0]) wm_name = self.display.get_atom('_NET_WM_NAME') prop = w.get_full_property(wm_name, X.AnyPropertyType) if prop: name = prop.value return name.lower() 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: # KeyPress 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 # KeyRelease elif event.type == X.KeyRelease and event.detail == self.keycode and wait_for_release: GLib.idle_add(self.idle) wait_for_release = False # Modifiers are often used with mouse events - don't let the system swallow those elif event.type == X.ButtonPress: self.display.allow_events(X.ReplayPointer, X.CurrentTime) # Compiz would rather not have the event sent to it and just read it from the replayed queue wm = self.get_wm() if wm != "compiz": self.display.ungrab_keyboard(X.CurrentTime) self.display.ungrab_pointer(X.CurrentTime) query_pointer = self.window.query_pointer() self.display.send_event(query_pointer.child, event, X.ButtonPressMask, True) wait_for_release = False # If the user presses another key in between the KeyPress and the KeyRelease, they # meant to use a different shortcut 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 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 KeyboardListener: def __init__(self, callback=None, on_error=None): 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.record_thread = threading.Thread( target=self._record, name='x keyboard listener thread') self.well_thread = threading.Thread(target=self._drop_key, daemon=True, name='hotkey well thread') self.recording_connection = Display() self.well_connection = Display() self.recording_connection.set_error_handler( self._record_display_error_handler) self.well_connection.set_error_handler( self._local_display_error_handler) if not self.recording_connection.has_extension("RECORD"): raise Exception("RECORD extension not found") r = self.recording_connection.record_get_version(0, 0) print("RECORD extension version %d.%d" % (r.major_version, r.minor_version)) self.context = self.recording_connection.record_create_context( 0, [record.AllClients], CONTEXT_FILTER) 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 = self.well_connection.screen().root self.root.change_attributes(event_mask=X.KeyPressMask | X.KeyReleaseMask) self.modifiers_count = self.modified_count = 0 self.code_map = {} self.composed_code_map = {} self.composed_mapping_first_code = None self.multiplier = '' # # API # def bind(self, key): if self.stopped: return if len(key.accelerators) == 1: self._bind_single_accelerator(key) elif len(key.accelerators) == 2: self._bind_composed_accelerator(key) self.well_connection.sync() if self.stopped: print('Unable to bind: {}'.format(', '.join(key.accelerators)), file=sys.stderr) def start(self): self.well_connection.sync() self.recording_connection.sync() if self.stopped: return self.well_thread.start() self.record_thread.start() def stop(self): self.stopped = True if self.record_thread.is_alive(): self.well_connection.record_disable_context(self.context) self.well_connection.close() print('display stopped recording') self.record_thread.join() print('recording thread ended') # # Thread targets # def _drop_key(self): while not self.stopped: self.well_connection.next_event() def _record(self): self.recording_connection.record_enable_context( self.context, self.handler) self.recording_connection.record_free_context(self.context) self.recording_connection.close() def _record_display_error_handler(self, exception, *args): print('Error at record display: {}'.format(exception), file=sys.stderr) if not self.stopped: self.stopped = True self.on_error() 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) def _bind_single_accelerator(self, key): gdk_keyval, code, mask = parse_accelerator(key.accelerators[0]) self._grab_keys(code, mask) if code not in self.code_map: self.code_map[code] = {} self.code_map[code][mask] = key def _bind_composed_accelerator(self, key): gdk_keyval, code, mask = parse_accelerator(key.accelerators[0]) second_gdk_keyval, second_code, second_mask = parse_accelerator( key.accelerators[1]) self._grab_keys(code, mask) if code not in self.composed_code_map: self.composed_code_map[code] = {} if mask not in self.composed_code_map[code]: self.composed_code_map[code][mask] = {} if second_code not in self.composed_code_map[code][mask]: self.composed_code_map[code][mask][second_code] = {} if second_mask in self.composed_code_map[code][mask][second_code]: raise Exception('key ({}) already mapped'.format(', '.join( key.accelerators))) self.composed_code_map[code][mask][second_code][second_mask] = key # # Event handling # def handler(self, reply): data = reply.data while len(data): event, data = rq.EventField(None).parse_binary_value( data, self.recording_connection.display, None, None) if event.detail in self.mod_keys_set: self.modifiers_count += 1 if event.type == X.KeyPress else -1 self.modified_count = 0 continue if self.modifiers_count: self.modified_count += 1 if event.type == X.KeyPress else -1 if event.type == X.KeyPress: self.handle_keypress(event) def handle_keypress(self, event): _wasmapped, keyval, egroup, level, consumed = Gdk.Keymap.get_default( ).translate_keyboard_state(event.detail, Gdk.ModifierType(event.state), 0) code = event.detail event.keyval = keyval # TODO: explain mask = normalize_state(event.state) if self.composed_mapping_first_code and self.composed_mapping_first_code != ( code, mask): key_name = Gdk.keyval_name(event.keyval) if not mask and key_name and key_name.isdigit(): self.multiplier = self.multiplier + key_name return second_code_map = self.composed_code_map[ self.composed_mapping_first_code[0]][ self.composed_mapping_first_code[1]] if code in second_code_map and mask in second_code_map[code]: multiplier_int = int(self.multiplier) if self.multiplier else 1 self.callback(second_code_map[code][mask], event, multiplier=multiplier_int) self.composed_mapping_first_code = None elif self.modified_count == 1: if code in self.code_map and mask in self.code_map[code]: self.callback(self.code_map[code][mask], event) if code in self.composed_code_map and mask in self.composed_code_map[ code]: self.composed_mapping_first_code = (code, mask) self.multiplier = ''
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 ()
class App(): def __init__(self, options): self.options = options self.reload_app = True self.xroot = None self.dpy = None self.menus, self.config = load_menus(self.options) self.create_menus() self.running = False def init_x_root(self): if self.dpy is None: self.dpy = Display() if self.xroot is None: self.xroot = self.dpy.screen().root def create_menus(self): self.keycode_to_char = {} self.menu_hot_keys = {} self.root = tk.Tk() self.menu_font = self.config['font'] self.init_x_root() self.root.bind("<Key>", self.on_key) self.buttons = [] for menu_name, fa_menu in self.menus.items(): self.add_menu(menu_name, fa_menu) self.root.config() def reload_menus(self): self.reload_app = True self.withdraw() self.xroot.destroy() self.dpy.close() self.menu_hot_keys = None self.root = None self.menu_font = None self.buttons = None self.xroot = None self.dpy = None gc.collect() self.menus, self.config = load_menus(self.options) self.create_menus() def key_spec_to_keycode_modifier_mask(self, key_spec): modifiers, key = parse_modifier_mask(key_spec) keysym = XK.string_to_keysym(key) keycode = self.dpy.keysym_to_keycode(keysym) if keycode not in self.keycode_to_char.keys(): self.keycode_to_char[keycode] = key modifier_mask = reduce(lambda x, y: x | y, modifiers, 0) return key, keycode, modifier_mask def add_menu(self, menu_name, fa_menu): max_width = max((len(item.item_name) for item in fa_menu.menu_content)) menu_frame = tk.Frame(self.root, borderwidth=2, relief=tk.RAISED) menu_frame.pack_propagate(True) menu_label = tk.Label(menu_frame, text=menu_name, font=self.menu_font, anchor="w", justify=tk.LEFT) menu_label.pack(fill="y") for menu_item in fa_menu.menu_content: jb = Button(menu_frame, menu_item, self.root, self, font=self.menu_font, borderwidth=2, relief=tk.RAISED, width=max_width) if menu_item.hot_key is not None: fa_menu.hot_keys[menu_item.hot_key] = jb.on_key jb.pack(fill="y") menu_item.button = jb fa_menu.menu_frame = menu_frame fa_menu.menu_buttons.append(jb) if fa_menu.menu_hot_key is not None: if self.menu_hot_keys.get(fa_menu.menu_hot_key) is None: hot_key, hot_key_states = self.bind_key(fa_menu.menu_hot_key) for state in hot_key_states: key_state_str = key_state(hot_key, state) self.menu_hot_keys[key_state_str] = fa_menu.menu_name fa_menu.select_button() def on_key(self, event): run = self.hot_keys.get(event.char) if run is None: if event.keysym == "Up": self.menus[self.menu_name].prev_button() elif event.keysym == "Down": self.menus[self.menu_name].next_button() elif event.keysym == "Return" or event.keysym == "space": run = self.menus[self.menu_name].button().on_key elif event.keysym == "Escape": self.withdraw() if run is not None: if run(event): self.withdraw() def withdraw(self): self.menus[self.menu_name].menu_frame.pack_forget() self.root.withdraw() self.root.quit() def switch_to_menu(self, menu_name): old_menu = self.menus[self.menu_name] old_menu.menu_frame.pack_forget() self.root.withdraw() new_menu = self.menus[menu_name] self.show_menu(new_menu) def show_menu(self, menu): self.menu_name = menu.menu_name if self.running: menu.menu_frame.pack() self.hot_keys = menu.hot_keys self.root.update() self.root.deiconify() else: self.running = True menu.menu_frame.pack() self.hot_keys = menu.hot_keys def do_loop(self): while not self.reload_app: ev = self.xroot.display.next_event() if ev.type == X.KeyPress: key = self.keycode_to_char.get(ev.detail) state = ev.state key_state_str = key_state(key, state) menu_name = self.menu_hot_keys.get(key_state_str) if menu_name is not None: menu = self.menus[menu_name] self.show_menu(menu) self.root.mainloop() def run(self): self.reload_app = False self.do_loop() def bind_key(self, key_spec): self.xroot.change_attributes(event_mask=X.KeyPressMask) key, keycode, modifier_mask = self.key_spec_to_keycode_modifier_mask(key_spec) states = [] for ignored in powerset(IGNORED_MODIFIERS): modmask = reduce(lambda x, y: x | y, ignored, 0) modmask |= modifier_mask states.append(modmask) self.xroot.grab_key(keycode, modmask, 1, X.GrabModeAsync, X.GrabModeAsync) return key, states
class GlobalKeyBinding(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): try: 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.showscreen = Wnck.Screen.get_default() self.ignored_masks = self.get_mask_combinations(X.LockMask | X.Mod2Mask | X.Mod5Mask) self.map_modifiers() self.raw_keyval = None self.keytext = "" except Exception as cause: print(("init keybinding error: \n", str(cause))) self.display = None return None 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 or self.get_keycode( key) == 134: 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): return self.keymap.get_entries_for_keyval(keyval).keys[0].keycode def grab(self, key): if self.display == None: return False 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) result = self.window.grab_key(134, 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.display == None: return if self.keycode: self.window.ungrab_key(self.keycode, X.AnyModifier, self.window) self.window.ungrab_key(134, 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): if self.display == None: return 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 range(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): if self.display == None: return self.running = True wait_for_release = False showdesktop = True 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) or ( event.detail == 134 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.SyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) elif (event.detail == self.keycode and wait_for_release) or (event.detail == 134 and wait_for_release): if event.type == X.KeyRelease: wait_for_release = False GLib.idle_add(self.idle) self.display.allow_events(X.SyncKeyboard, event.time) elif event.detail == 40 and event.type == X.KeyPress: #super+d self.display.allow_events(X.SyncKeyboard, event.time) elif event.detail == 40 and event.type == X.KeyRelease: #super+d if showdesktop: self.showscreen.toggle_showing_desktop(True) showdesktop = False else: self.showscreen.toggle_showing_desktop(False) showdesktop = True self.display.allow_events(X.ReplayKeyboard, event.time) elif event.detail == 33 and event.type == X.KeyPress: #super+p self.display.allow_events(X.SyncKeyboard, event.time) elif event.detail == 33 and event.type == X.KeyRelease: #super+p self.display.allow_events(X.ReplayKeyboard, event.time) elif event.detail == 26 and event.type == X.KeyPress: #super+e self.display.allow_events(X.SyncKeyboard, event.time) elif event.detail == 26 and event.type == X.KeyRelease: #super+e os.system("peony &") self.display.allow_events(X.ReplayKeyboard, 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.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 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 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
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 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) 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 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 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 wm(object): """Initialise WM variables and open display""" def __init__(self): self.windowList = [] # List of opened windows self.display = Display() # Initialise display self.colormap = self.display.screen().default_colormap # Initialise colourmap self.currentMode = None self.activeWindow = None self.rootWindow = self.display.screen().root # Get root window self.displayWidth = self.rootWindow.get_geometry().width # Get width of display self.displayHeight = self.rootWindow.get_geometry().height # Get height of display self.rootWindow.change_attributes(event_mask = X.SubstructureRedirectMask) # Redirect events from root window self.configureKeys() # Configure key bindings def mainLoop(self): self.updateFocus() self.updateBorders() self.handleEvents() log(3, "Mode:"+str(self.currentMode)) """Destroy an active window""" def destroyWindow(self, event): try: self.activeWindow.destroy() self.windowList.remove(self.activeWindow) self.activeWindow = None except: log(2, "No focused window!") def updateFocus(self): window = self.display.screen().root.query_pointer().child if window != 0: self.activeWindow = window def updateBorders(self): for window in self.windowList: #gc = self.rootWindow.create_gc() #window.fill_rectangle(gc, 0, window.get_geometry().y-5, window.get_geometry().width, 5) #window.draw_text(gc, window.get_geometry().width/2, 5, "Hello, World!", "ff0011") if window != self.activeWindow: borderColour = preferences.theme.border.inactiveColour else: borderColour = preferences.theme.border.activeColour borderColour = self.colormap.alloc_named_color(borderColour).pixel #borderColour).pixel window.configure(border_width = preferences.theme.border.borderWidth) window.change_attributes(None,border_pixel=borderColour) self.display.sync() """Handle WM events""" def handleEvents(self): ignoredEvents = [3, 33, 34, 23] # Blacklisted events if self.display.pending_events() > 0: event = self.display.next_event() # Get next event from display else: return if event.type == X.MapRequest: self.handleMap(event) # Send mapping events to the mapping handler elif event.type == X.KeyPress: self.handleKeyPress(event) # Send keypress event to the keypress handler elif event.type in ignoredEvents: log(3, "Ignoring event: "+str(event.type)) # Ignore event if it is a blacklisted event else: # Otherwise, if the event is not a currently handled event log(1, "Unhandled event: "+str(event.type)) # Warn of an unhandled event """Handle a mapping request""" def handleMap(self, event): self.windowList.append(event.window) # Add the window identifier to a list of open windows log(3, str(self.windowList)) # Show list of windows (DEBUG) self.activeWindow = event.window # Set the active window to the mapped window self.activeWindowName = event.window.get_wm_name() # Set the active window name to the window title event.window.map() # Map the window """Receive a KeyPressed event when a certain key is pressed""" def grabKey(self, codes, modifier): for code in codes: # For each code self.rootWindow.grab_key(code, modifier, 1, X.GrabModeAsync, X.GrabModeAsync) # Receive events when the key is pressed """Bind keys""" def configureKeys(self): self.left = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_Left)) # Assign a list of possible keycodes to the variable self.right = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_Right)) self.up = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_Up)) self.down = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_Down)) self.close = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_X)) self.t = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_T)) self.e = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_E)) self.x = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_X)) self.r = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_X)) self.grabbedKeys = [self.left, self.right, self.up, self.down, self.close, self.t, self.e, self.x, self.r] for key in self.grabbedKeys: # For each key to grab, self.grabKey(key, X.Mod1Mask) # Grab the key with the modifer of Alt #def resizeWindow(window, direction): # windowW = window.get_geometry().width # windowH = window.get_geometry().height # if direction == 0: window.configure(width=windowW-1) # elif direction == 1: window.configure(width=windowW+1) # elif direction == 2: window.configure(height=windowH-1) # elif direction == 3: window.configure(height=windowH+1) """Change the position of an active window""" def moveWindow(self, direction): try: if direction == "left": windowX = self.activeWindow.get_geometry().x # Get the current position of the active window self.activeWindow.configure(x=windowX-5) # Decrease the X position to move it left elif direction == "right": windowX = self.activeWindow.get_geometry().x self.activeWindow.configure(x=windowX+5) elif direction == "up": windowY = self.activeWindow.get_geometry().y self.activeWindow.configure(y=windowY-5) elif direction == "down": windowY = self.activeWindow.get_geometry().y self.activeWindow.configure(y=windowY+5) else: log(1, "Invalid movement direction!") except AttributeError: log(1, "No focused window!") """Handle key presses""" def handleKeyPress(self, event): # if self.currentMode == "resize": # If resize mode is enabled: #if event.detail in self.left: self.resizeWindow(event.window, 0) # Alt+Left Arrow: resize window (x-1) # elif event.detail in self.right: self.moveWindow(event.window, 1) # Alt+Right Arrow: resize window (x+1) # elif event.detail in self.up: self.moveWindow(event.window, 2) # Alt+Up Arrow: resize window (y-1) # elif event.detail in self.down: self.moveWindow(event.window, 3) # Alt+Down Arrow: resize window (y+1) # else: # Otherwise, if event.detail in self.left: self.moveWindow("left") # Alt+Left Arrow: move a window left elif event.detail in self.right: self.moveWindow("right") # Alt+Right Arrow: move a window right elif event.detail in self.up: self.moveWindow("up") # Alt+Up Arrow: move a window up elif event.detail in self.down: self.moveWindow("down") # Alt+Down Arrow: move a window down elif event.detail in self.t: self.runProcess(preferences.applicationDefaults.terminal) # Alt+T: Launch a terminal elif event.detail in self.e: self.runProcess(preferences.applicationDefaults.launcher) # Alt+E: Launch a program launcher elif event.detail in self.x: self.destroyWindow(event) # ALT+X: Close a window elif event.detail in self.r: self.currentMode = "resize" # ALT+R: Enable resize mode else: log(1, "Unhandled key event!") """Close connection to display server""" def closeDisplay(self): log(0, "Exiting...") self.display.close() """Run an application/process""" def runProcess(self, applicationInfo): try: name = applicationInfo["name"] # Get application name command = applicationInfo["command"] # Get aplication command except: raise ValueError try: log(0, "Running: "+name) subprocess.Popen(command) # Run the command and disown the child process except BaseException as error: log(2, "Failed to launch process: "+processCommand+"!") log(3, str(error))
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()
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 WindowWatch(EventInput): """ Run an external command that creates a window and then watch that window's size. """ def __init__(self, command, is_right_window, kill_old_instance=False): """ - command is a tokinzed command to invoke the process - is_right_window is a callback such that is_right_window(window) returns 'window' if it is a the window to watch and None otherwise. - if kill_old_instance is set, then all clients matching is_right_window() are killed before 'command' is invoked. """ self.display = Display() root = self.display.screen().root # get root window's current event mask and replace it in order to wait # passively for the trayer window old_mask = root.get_attributes().your_event_mask root.change_attributes(event_mask=X.SubstructureNotifyMask) self.display.sync() if kill_old_instance: while True: old_tray = self.find_tray_window(root, is_right_window) if old_tray is None: # no old instance break # force shutdown: old_tray.kill_client() # wait for an event, i.e. any kind of updater from X self.display.next_event() # start the process: super(WindowWatch, self).__init__(command) self.proc.stdin.close() self.proc.stdout.close() # wait passively for trayer to create its window while self.proc.poll() is None: event = self.display.next_event() self.trayer = self.find_tray_window(root, is_right_window) if self.trayer is not None: break if self.proc.poll() is not None: print("command »{}« exited unexpectedly.".format( ' '.join(command)), file=sys.stderr) return # revert root window event_mask to remove unnecessary wakeups root.change_attributes(event_mask=old_mask) # activate ConfigureNotify-Events for self.trayer self.trayer.change_attributes(event_mask=X.StructureNotifyMask) def find_tray_window(self, root, is_right_window): children = root.query_tree().children for window in children: try: found = is_right_window(window) if found is not None: return found res = self.find_tray_window(window, is_right_window) if res: return res except Xlib.error.BadWindow as e: # if a window disappeared while inspecting it, # just skip it. pass return None def watch_trayer_non_blocking(self): while self.display.pending_events() > 0: event = self.display.next_event() if event.type != X.ConfigureNotify: continue if event.window != self.trayer: continue def get_width(self): try: self.width = self.trayer.get_geometry().width except Xlib.error.BadWindow: self.width = 0 except Xlib.error.BadDrawable: self.width = 0 return self.width def kill(self): self.proc.kill() self.display.close() def fileno(self): return self.display.fileno() def process(self): self.watch_trayer_non_blocking()
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()
signal.alarm(10) while True: print("event...") event = display.next_event() print(event.type, event.detail, event.time) if event.type == X.KeyPress and event.detail == 10: print("pressed") if not counter % 2: swallow_keystroke(event) else: passthru_keystroke(event) elif event.type == X.KeyRelease and event.detail == 10: print("released") if not counter % 2: swallow_keystroke(event) else: passthru_keystroke(event) counter += 1 if event.type == X.KeyPress and event.detail == 15: swallow_keystroke(event) if event.type == X.KeyRelease and event.detail == 15: print("exit") break root.ungrab_key(10, 0) root.ungrab_key(15, 0) display.close()