class XlibInterface: def __init__(self): pass self.display = Display() self.root = self.display.screen().root def grab_keyboard(self): self.root.change_attributes(event_mask=KeyPressMask | KeyReleaseMask) self.root.grab_keyboard(False, GrabModeAsync, GrabModeAsync, CurrentTime) @staticmethod def event_is_keypress(event) -> bool: if event.type is KeyPress: return True else: return False def events_pending(self) -> bool: return self.display.pending_events() def get_next_event(self): return self.display.next_event() def stop_listening(self): self.display.ungrab_keyboard(CurrentTime) self.display.flush()
def mouse_move(x, y): from Xlib.display import Display from Xlib import X from Xlib.ext.xtest import fake_input d = Display() fake_input(d, X.MotionNotify, x=x, y=y) d.flush()
class PointerMonitor(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): GObject.GObject.__init__ (self) threading.Thread.__init__ (self) self.setDaemon (True) self.display = Display() self.root = self.display.screen().root self.windows = [] # Receives GDK windows def addWindowToMonitor(self, window): gdk.gdk_x11_drawable_get_xid.argtypes = [c_void_p] xWindow = self.display.create_resource_object("window", gdk.gdk_x11_drawable_get_xid(hash(window))) self.windows.append(xWindow) def grabPointer(self): self.root.grab_button(X.AnyButton, X.AnyModifier, True, X.ButtonPressMask, X.GrabModeSync, X.GrabModeAsync, 0, 0) self.display.flush() def ungrabPointer(self): self.root.ungrab_button(X.AnyButton, X.AnyModifier) self.display.flush() def idle(self): self.emit("activate") return False def activate(self): GLib.idle_add(self.run) def run(self): self.running = True while self.running: event = self.display.next_event() try: if event.type == X.ButtonPress: # Check if pointer is inside monitored windows for w in self.windows: p = w.query_pointer() g = w.get_geometry() if p.win_x >= 0 and p.win_y >= 0 and p.win_x <= g.width and p.win_y <= g.height: break else: # Is outside, so activate GLib.idle_add(self.idle) self.display.allow_events(X.ReplayPointer, event.time) else: self.display.allow_events(X.ReplayPointer, X.CurrentTime) except Exception as e: print "Unexpected error: " + str(e) def stop(self): self.running = False self.root.ungrab_button(X.AnyButton, X.AnyModifier) self.display.close()
class PointerMonitor(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): GObject.GObject.__init__ (self) threading.Thread.__init__ (self) self.setDaemon (True) self.display = Display() self.root = self.display.screen().root self.windows = [] # Receives GDK windows def addWindowToMonitor(self, window): xWindow = self.display.create_resource_object("window", gdk.gdk_x11_drawable_get_xid(hash(window))) self.windows.append(xWindow) def grabPointer(self): self.root.grab_button(X.AnyButton, X.AnyModifier, True, X.ButtonPressMask, X.GrabModeSync, X.GrabModeAsync, 0, 0) self.display.flush() def ungrabPointer(self): self.root.ungrab_button(X.AnyButton, X.AnyModifier) self.display.flush() def idle(self): self.emit("activate") return False def activate(self): GLib.idle_add(self.run) def run(self): self.running = True while self.running: event = self.display.next_event() try: if event.type == X.ButtonPress: # Check if pointer is inside monitored windows for w in self.windows: p = w.query_pointer() g = w.get_geometry() if p.win_x >= 0 and p.win_y >= 0 and p.win_x <= g.width and p.win_y <= g.height: break else: # Is outside, so activate GLib.idle_add(self.idle) self.display.allow_events(X.ReplayPointer, event.time) else: self.display.allow_events(X.ReplayPointer, X.CurrentTime) except Exception as e: print "Unexpected error: " + str(e) def stop(self): self.running = False self.root.ungrab_button(X.AnyButton, X.AnyModifier) self.display.close()
class PyMouseEvent(PyMouseEventMeta): def __init__(self): PyMouseEventMeta.__init__(self) self.display = Display() self.display2 = Display() self.ctx = self.display2.record_create_context( 0, [record.AllClients ], [{ 'core_requests': (0, 0), 'core_replies': (0, 0), 'ext_requests': (0, 0, 0, 0), 'ext_replies': (0, 0, 0, 0), 'delivered_events': (0, 0), 'device_events': (X.KeyPress, X.MotionNotify), 'errors': (0, 0), 'client_started': False, 'client_died': False, }]) def run(self): if self.capture: self.display2.screen().root.grab_pointer( True, X.ButtonPressMask | X.ButtonReleaseMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime) self.display2.record_enable_context(self.ctx, self.handler) self.display2.record_free_context(self.ctx) def stop(self): self.display.record_disable_context(self.ctx) self.display.ungrab_pointer(X.CurrentTime) self.display.flush() self.display2.record_disable_context(self.ctx) self.display2.ungrab_pointer(X.CurrentTime) self.display2.flush() def handler(self, reply): data = reply.data while len(data): event, data = rq.EventField(None).parse_binary_value( data, self.display.display, None, None) if event.type == X.KeyPress: keysym = self.display.keycode_to_keysym(event.detail, 0) self.keypress(keysym) if event.type == X.KeyRelease: keysym = self.display.keycode_to_keysym(event.detail, 0) self.keyrelease(keysym) if event.type == X.ButtonPress: self.click(event.root_x, event.root_y, (None, 1, 3, 2, 3, 3, 3)[event.detail], True) elif event.type == X.ButtonRelease: self.click(event.root_x, event.root_y, (None, 1, 3, 2, 3, 3, 3)[event.detail], False) else: self.move(event.root_x, event.root_y)
class PyMouseEvent(PyMouseEventMeta): ## XXX Mario: hack for mouse wheel support! ## - may incompatible with other platforms, but works for me. ## - there seems to be good solution on github but waiting for acceptance (since 2 years..) # _BUTTONS = (None, 1, 3, 2, 3, 3, 3) ## original _BUTTONS = (0, 1, 3, 2, 4, 5, 0) ## with mouse wheel def __init__(self, display=':0'): PyMouseEventMeta.__init__(self) self.display = Display(display) self.display2 = Display(display) self.ctx = self.display2.record_create_context( 0, [record.AllClients], [{ 'core_requests': (0, 0), 'core_replies': (0, 0), 'ext_requests': (0, 0, 0, 0), 'ext_replies': (0, 0, 0, 0), 'delivered_events': (0, 0), 'device_events': (X.ButtonPressMask, X.ButtonReleaseMask), 'errors': (0, 0), 'client_started': False, 'client_died': False, }]) def run(self): if self.capture: self.display2.screen().root.grab_pointer(True, X.ButtonPressMask | X.ButtonReleaseMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime) self.display2.record_enable_context(self.ctx, self.handler) self.display2.record_free_context(self.ctx) def stop(self): self.display.record_disable_context(self.ctx) self.display.ungrab_pointer(X.CurrentTime) self.display.flush() self.display2.record_disable_context(self.ctx) self.display2.ungrab_pointer(X.CurrentTime) self.display2.flush() def handler(self, reply): data = reply.data while len(data): event, data = rq.EventField(None).parse_binary_value(data, self.display.display, None, None) if event.type == X.ButtonPress: self.click(event.root_x, event.root_y, self._BUTTONS[event.detail], True) elif event.type == X.ButtonRelease: self.click(event.root_x, event.root_y, self._BUTTONS[event.detail], False) else: self.move(event.root_x, event.root_y)
class PyMouseEvent(PyMouseEventMeta): def __init__(self): PyMouseEventMeta.__init__(self) self.display = Display() self.display2 = Display() self.ctx = self.display2.record_create_context ( 0, [record.AllClients], [{ 'core_requests': (0, 0), 'core_replies': (0, 0), 'ext_requests': (0, 0, 0, 0), 'ext_replies': (0, 0, 0, 0), 'delivered_events': (0, 0), 'device_events': (X.KeyPress, X.MotionNotify), 'errors': (0, 0), 'client_started': False, 'client_died': False, }]) def run(self): if self.capture: self.display2.screen().root.grab_pointer(True, X.ButtonPressMask | X.ButtonReleaseMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime) self.display2.record_enable_context(self.ctx, self.handler) self.display2.record_free_context(self.ctx) def stop(self): self.display.record_disable_context(self.ctx) self.display.ungrab_pointer(X.CurrentTime) self.display.flush() self.display2.record_disable_context(self.ctx) self.display2.ungrab_pointer(X.CurrentTime) self.display2.flush() def handler(self, reply): data = reply.data while len(data): event, data = rq.EventField(None).parse_binary_value(data, self.display.display, None, None) if event.type == X.KeyPress: keysym = self.display.keycode_to_keysym(event.detail, 0) self.keypress(keysym) if event.type == X.KeyRelease: keysym = self.display.keycode_to_keysym(event.detail, 0) self.keyrelease(keysym) if event.type == X.ButtonPress: self.click(event.root_x, event.root_y, (None, 1, 3, 2, 3, 3, 3)[event.detail], True) elif event.type == X.ButtonRelease: self.click(event.root_x, event.root_y, (None, 1, 3, 2, 3, 3, 3)[event.detail], False) else: self.move(event.root_x, event.root_y)
def block_keyboard(self): self.lock_keyboard = True display = Display() root = display.screen().root # Grap the keyboard root.grab_keyboard(owner_events = False, pointer_mode = X.GrabModeAsync, keyboard_mode = X.GrabModeAsync, time = X.CurrentTime) # Consume keyboard events self.key_lock_condition.acquire() while self.lock_keyboard: self.key_lock_condition.wait() self.key_lock_condition.release() # Ungrap the keyboard display.ungrab_keyboard(X.CurrentTime) display.flush()
def __init__(self, display=None): if display is None: display = Display() self.d = display self.screen = self.d.screen() bgsize = 20 bgpm = self.screen.root.create_pixmap(bgsize, bgsize, self.screen.root_depth) bggc = self.screen.root.create_gc(foreground=self.screen.black_pixel, background=self.screen.black_pixel) bgpm.fill_rectangle(bggc, 0, 0, bgsize, bgsize) bggc.change(foreground=self.screen.white_pixel) bgpm.arc(bggc, -bgsize // 2, 0, bgsize, bgsize, 0, 360 * 64) bgpm.arc(bggc, bgsize // 2, 0, bgsize, bgsize, 0, 360 * 64) bgpm.arc(bggc, 0, -bgsize // 2, bgsize, bgsize, 0, 360 * 64) bgpm.arc(bggc, 0, bgsize // 2, bgsize, bgsize, 0, 360 * 64) self.window = self.screen.root.create_window( 100, 100, 400, 300, 0, self.screen.root_depth, X.InputOutput, X.CopyFromParent, background_pixmap=bgpm, event_mask=(X.StructureNotifyMask | X.ButtonReleaseMask), colormap=X.CopyFromParent) self.WM_DELETE_WINDOW = self.d.intern_atom('WM_DELETE_WINDOW') self.WM_PROTOCOLS = self.d.intern_atom('WM_PROTOCOLS') self.window.set_wm_name('i3 test window') self.window.set_wm_class('i3win', 'i3win') self.window.set_wm_protocols([self.WM_DELETE_WINDOW]) self.window.set_wm_hints(flags=Xutil.StateHint, initial_state=Xutil.NormalState) self.window.set_wm_normal_hints(flags=(Xutil.PPosition | Xutil.PSize | Xutil.PMinSize), min_width=50, min_height=50) self.window.map() display.flush()
def block_keyboard(self): logging.info("Lock the keyboard") self.lock_keyboard = True display = Display() root = display.screen().root # Grap the keyboard root.grab_keyboard(owner_events = False, pointer_mode = X.GrabModeAsync, keyboard_mode = X.GrabModeAsync, time = X.CurrentTime) # Consume keyboard events self.key_lock_condition.acquire() while self.lock_keyboard: self.key_lock_condition.wait() self.key_lock_condition.release() # Ungrap the keyboard logging.info("Unlock the keyboard") display.ungrab_keyboard(X.CurrentTime) display.flush()
class KeyListener(threading.Thread): ''' Usage: keylistener = KeyListener() Initially: keylistener.addKeyListener("L_CTRL+L_SHIFT+y", callable) Note that it is necessary to bind all possible combinations because an order of key presses can be different, for example, "L_CTRL+y+L_SHIFT" Now: keylistener.addKeyListener("Control_L+c+c", callable) ''' def __init__(self): threading.Thread.__init__(self) self.finished = threading.Event() self.contextEventMask = [X.KeyPress, X.MotionNotify] # Give these some initial values # Hook to our display. self.local_dpy = Display() self.record_dpy = Display() self.pressed = [] self.listeners = {} self.character = None ''' 0: Nothing caught; 1: Read buffer and call main module; 2: Call main module ''' self.status = 0 ''' need the following because XK.keysym_to_string() only does printable chars rather than being the correct inverse of XK.string_to_keysym() ''' def lookup_keysym(self, keysym): for name in dir(XK): if name.startswith("XK_") and getattr(XK, name) == keysym: return name.lstrip("XK_") return '[%d]' % keysym def processevents(self, reply): if reply.category != record.FromServer: return if reply.client_swapped: print_v('* received swapped protocol data, cowardly ignored') return # I added 'str', since we receive an error without it if not len(str(reply.data)) or ord(str(reply.data[0])) < 2: # not an event return data = reply.data while len(data): event, data = rq.EventField(None).parse_binary_value( data, self.record_dpy.display, None, None) keycode = event.detail keysym = self.local_dpy.keycode_to_keysym(event.detail, 0) self.character = self.lookup_keysym(keysym) if self.character: if event.type == X.KeyPress: self.press() elif event.type == X.KeyRelease: self.release() def run(self): # Check if the extension is present if not self.record_dpy.has_extension('RECORD'): print_v('RECORD extension not found') sys.exit(1) r = self.record_dpy.record_get_version(0, 0) mes = 'RECORD extension version {}.{}'.format(r.major_version, r.minor_version) print_v(mes) # Create a recording context; we only want key events self.ctx = self.record_dpy.record_create_context( 0, [record.AllClients], [{ 'core_requests': (0, 0), 'core_replies': (0, 0), 'ext_requests': (0, 0, 0, 0), 'ext_replies': (0, 0, 0, 0), 'delivered_events': (0, 0) # (X.KeyPress, X.ButtonPress) , 'device_events': tuple(self.contextEventMask), 'errors': (0, 0), 'client_started': False, 'client_died': False, }]) ''' Enable the context; this only returns after a call to record_disable_context, while calling the callback function in the meantime ''' self.record_dpy.record_enable_context(self.ctx, self.processevents) # Finally free the context self.record_dpy.record_free_context(self.ctx) def cancel(self): self.finished.set() self.local_dpy.record_disable_context(self.ctx) self.local_dpy.flush() def append(self): if len(self.pressed) > 0: if self.pressed[0] in ('Control_L', 'Control_R', 'Alt_L', 'Alt_R'): self.pressed.append(self.character) def press(self): if len(self.pressed) == 2: if self.pressed[1] == 'grave': self.pressed = [] elif len(self.pressed) == 3: self.pressed = [] if self.character in ('Control_L', 'Control_R', 'Alt_L', 'Alt_R'): self.pressed = [self.character] elif self.character in ('c', 'Insert', 'grave'): self.append() action = self.listeners.get(tuple(self.pressed), False) print_v('Current action:', str(tuple(self.pressed))) if action: action() def release(self): """must be called whenever a key release event has occurred.""" # A released Control key is not taken into account # A cyrillic 'с' symbol is recognized as Latin 'c' if not self.character in ('c', 'Insert', 'grave'): self.pressed = [] def addKeyListener(self, hotkeys, callable): keys = tuple(hotkeys.split('+')) print_v('Added new keylistener for :', str(keys)) self.listeners[keys] = callable def check(self): # Returns 0..2 if self.status: print_v('Hotkey has been caught!') status = self.status self.status = 0 return status def set_status(self, status=0): self.status = status print_v('Setting status to %d!' % self.status)
class mwsd: def __init__(self, args): self.verbose = args.verbose self.ip = "localhost" self.port = args.port self.ignore = args.ignore self.disp = Display() self.screen = self.disp.screen() self.root = self.screen.root self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.setup_socket() self.ids = [] self.windows = [] self.status = False self.running = True self.lock = threading.Lock() # Communication to daemon def setup_socket(self): # self.socket.setblocking(False) self.socket.bind((self.ip, self.port)) self.socket.listen(1) # Running the server in a other thread self.running = True self.thread = threading.Thread(target=self.server_listen) self.thread.daemon = True self.thread.start() def server_listen(self): while self.running is True: connection, client_address = self.socket.accept() try: msg = "" while True: data = connection.recv(16) if data: msg += data.decode() else: break if msg != "": msg_list = msg.split() if self.verbose is True: print(msg) if msg_list[0] == "add": for id in msg_list[1:]: self.add(int(id)) connection.sendall("done".encode()) elif msg_list[0] == "rm": for id in msg_list[1:]: self.rm(int(id)) connection.sendall("done".encode()) elif msg_list[0] == "clear": self.clear() elif msg_list[0] == "active": self.active() elif msg_list[0] == "deactive": self.deactive() elif msg_list[0] == "toggle": if self.status is True: self.active() else: self.deactive() elif msg_list[0] == "stop": self.terminate() else: print("Unknown command") connection.sendall("UnknownCommand".encode()) finally: connection.close() # Actions def add(self, id: int): self.lock.acquire(blocking=True) self.ids.append(id) self.windows.append(self.disp.create_resource_object('window', id)) self.grab(self.windows[-1]) self.lock.release() def rm(self, id: int): self.lock.acquire(blocking=True) index = self.ids.index(id) self.ids.remove(id) self.windows.remove(self.windows[index]) self.lock.release() def clear(self): self.lock.acquire(blocking=True) self.windows.clear() self.ids.clear() self.lock.release() def active(self): self.status = True def deactive(self): self.status = False def terminate(self): self.running = False # Daemon main function def listen(self): while self.running: # X11 if len(self.windows) != 0 and len(self.ids) != 0: evt = self.disp.next_event() if evt.type in [X.KeyPress]: keycode = evt.detail if self.verbose is True: print("Keycode:", keycode) self.disp.allow_events(X.ReplayKeyboard, X.CurrentTime) if self.status is True: for window in self.windows: self.press(window, keycode, evt.state) else: index = self.ids.index(evt.window.id) self.press(self.windows[index], keycode, evt.state) if evt.type == X.DestroyNotify: try: self.rm(evt.window.id) except ValueError: pass # X11 def event(self, name, window, detail, state): return name(time=X.CurrentTime, root=self.root, window=window, same_screen=0, child=Xlib.X.NONE, root_x=0, root_y=0, event_x=0, event_y=0, state=state, detail=detail) def press(self, window, keycode, mask=X.NONE): window.send_event(self.event(event.KeyPress, window, keycode, mask), propagate=True) window.send_event(self.event(event.KeyRelease, window, keycode, mask), propagate=True) self.disp.flush() self.disp.sync() def grab(self, window): window.grab_key(X.AnyKey, X.AnyModifier, True, X.GrabModeAsync, X.GrabModeAsync) # Ungrab window manager shortcuts (Super + ...) for key in self.ignore: window.ungrab_key(key, X.AnyModifier, True) window.change_attributes(event_mask=X.KeyReleaseMask | X.KeyPressMask | X.StructureNotifyMask) def ungrab(self, window): window.ungrab_key(X.AnyKey, X.AnyModifier, True) # Cleanup def cleanup(self): self.running = False
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 PyMouseEvent(PyMouseEventMeta): def __init__(self, capture=False, capture_move=False, display=None): PyMouseEventMeta.__init__(self, capture=capture, capture_move=capture_move) self.display = Display(display) self.display2 = Display(display) self.ctx = self.display2.record_create_context( 0, [record.AllClients ], [{ 'core_requests': (0, 0), 'core_replies': (0, 0), 'ext_requests': (0, 0, 0, 0), 'ext_replies': (0, 0, 0, 0), 'delivered_events': (0, 0), 'device_events': (X.ButtonPressMask, X.ButtonReleaseMask), 'errors': (0, 0), 'client_started': False, 'client_died': False, }]) def run(self): try: if self.capture and self.capture_move: capturing = X.ButtonPressMask | X.ButtonReleaseMask | X.PointerMotionMask elif self.capture: capturing = X.ButtonPressMask | X.ButtonReleaseMask elif self.capture_move: capturing = X.PointerMotionMask else: capturing = False if capturing: self.display2.screen().root.grab_pointer( True, capturing, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime) self.display.screen().root.grab_pointer( True, capturing, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime) self.display2.record_enable_context(self.ctx, self.handler) self.display2.record_free_context(self.ctx) except KeyboardInterrupt: self.stop() def stop(self): self.display.flush() self.display.record_disable_context(self.ctx) self.display.ungrab_pointer(X.CurrentTime) self.display2.flush() self.display2.record_disable_context(self.ctx) self.display2.ungrab_pointer(X.CurrentTime) def handler(self, reply): data = reply.data while len(data): event, data = rq.EventField(None).parse_binary_value( data, self.display.display, None, None) #In X11, the button numbers are: leftclick=1, middleclick=2, # rightclick=3, scrollup=4, scrolldown=5, scrollleft=6, # scrollright=7 # For the purposes of the cross-platform interface of PyMouse, we # invert the button number values of the right and middle buttons if event.type == X.ButtonPress: self.click(event.root_x, event.root_y, (None, 1, 3, 2, 4, 5, 6, 7)[event.detail], True) elif event.type == X.ButtonRelease: self.click(event.root_x, event.root_y, (None, 1, 3, 2, 4, 5, 6, 7)[event.detail], False) else: self.move(event.root_x, event.root_y)
for x in range(10): #print 'Wait count: ', x bus = dbus.SessionBus() fm = bus.get_object('org.freedesktop.FileManager1', '/org/freedesktop/FileManager1') locs = fm.Get('org.freedesktop.FileManager1', 'XUbuntuOpenLocationsXids', dbus_interface='org.freedesktop.DBus.Properties') if len(locs) == len(lines): break sleep(1) # Wait for nautilus windows to get reparented sleep(5) # Position the windows for windid, folders in locs.items(): args = winds[folders[0]] wnd = getWin(windid) # Change the gravity to static so that we can set an absolute position hints = wnd.get_wm_normal_hints() hints['win_gravity'] = StaticGravity wnd.set_wm_normal_hints(hints) wnd.configure(x=int(args[0]), y=int(args[1]), width=int(args[2]), height=int(args[3])) dpy.flush()
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 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 BreakScreen: """ The fullscreen window which prevents users from using the computer. This class reads the break_screen.glade and build the user interface. """ def __init__(self, context, on_skip, on_postpone, style_sheet_path): self.context = context self.count_labels = [] self.display = Display() self.enable_postpone = False self.enable_shortcut = False self.is_pretified = False self.keycode_shortcut_postpone = 65 self.keycode_shortcut_skip = 9 self.on_postpone = on_postpone self.on_skip = on_skip self.shortcut_disable_time = 2 self.strict_break = False self.windows = [] # Initialize the theme css_provider = Gtk.CssProvider() css_provider.load_from_path(style_sheet_path) Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) def initialize(self, config): """ Initialize the internal properties from configuration """ logging.info("Initialize the break screen") self.enable_postpone = config.get('allow_postpone', False) self.keycode_shortcut_postpone = config.get('shortcut_postpone', 65) self.keycode_shortcut_skip = config.get('shortcut_skip', 9) self.shortcut_disable_time = config.get('shortcut_disable_time', 2) self.strict_break = config.get('strict_break', False) def skip_break(self): """ Skip the break from the break screen """ logging.info("User skipped the break") # Must call on_skip before close to lock screen before closing the break screen self.on_skip() self.close() def postpone_break(self): """ Postpone the break from the break screen """ logging.info("User postponed the break") self.on_postpone() self.close() def on_window_delete(self, *args): """ Window close event handler. """ logging.info("Closing the break screen") self.close() def on_skip_clicked(self, button): """ Skip button press event handler. """ self.skip_break() def on_postpone_clicked(self, button): """ Postpone button press event handler. """ self.postpone_break() def show_count_down(self, countdown, seconds): """ Show/update the count down on all screens. """ self.enable_shortcut = self.shortcut_disable_time <= seconds mins, secs = divmod(countdown, 60) timeformat = '{:02d}:{:02d}'.format(mins, secs) GLib.idle_add(lambda: self.__update_count_down(timeformat)) def show_message(self, break_obj, widget, tray_actions=[]): """ Show the break screen with the given message on all displays. """ message = break_obj.name image_path = break_obj.image self.enable_shortcut = self.shortcut_disable_time <= 0 GLib.idle_add(lambda: self.__show_break_screen(message, image_path, widget, tray_actions)) def close(self): """ Hide the break screen from active window and destroy all other windows """ logging.info("Close the break screen(s)") self.__release_keyboard() # Destroy other windows if exists GLib.idle_add(lambda: self.__destroy_all_screens()) def __tray_action(self, button, tray_action): """ Tray action handler. Hides all toolbar buttons for this action and call the action provided by the plugin. """ tray_action.reset() tray_action.action() def __show_break_screen(self, message, image_path, widget, tray_actions): """ Show an empty break screen on all screens. """ # Lock the keyboard utility.start_thread(self.__lock_keyboard) screen = Gtk.Window().get_screen() no_of_monitors = screen.get_n_monitors() logging.info("Show break screens in %d display(s)", no_of_monitors) for monitor in range(no_of_monitors): monitor_gemoetry = screen.get_monitor_geometry(monitor) x = monitor_gemoetry.x y = monitor_gemoetry.y builder = Gtk.Builder() builder.add_from_file(BREAK_SCREEN_GLADE) builder.connect_signals(self) window = builder.get_object("window_main") lbl_message = builder.get_object("lbl_message") lbl_count = builder.get_object("lbl_count") lbl_widget = builder.get_object("lbl_widget") img_break = builder.get_object("img_break") box_buttons = builder.get_object("box_buttons") toolbar = builder.get_object("toolbar") for tray_action in tray_actions: toolbar_button = None if tray_action.system_icon: toolbar_button = Gtk.ToolButton.new_from_stock( tray_action.get_icon()) else: toolbar_button = Gtk.ToolButton.new( tray_action.get_icon(), tray_action.name) tray_action.add_toolbar_button(toolbar_button) toolbar_button.connect( "clicked", lambda button, action: self.__tray_action(button, action), tray_action) toolbar_button.set_tooltip_text(_(tray_action.name)) toolbar.add(toolbar_button) toolbar_button.show() # Add the buttons if self.enable_postpone: # Add postpone button btn_postpone = Gtk.Button(_('Postpone')) btn_postpone.get_style_context().add_class('btn_postpone') btn_postpone.connect('clicked', self.on_postpone_clicked) btn_postpone.set_visible(True) box_buttons.pack_start(btn_postpone, True, True, 0) if not self.strict_break: # Add the skip button btn_skip = Gtk.Button(_('Skip')) btn_skip.get_style_context().add_class('btn_skip') btn_skip.connect('clicked', self.on_skip_clicked) btn_skip.set_visible(True) box_buttons.pack_start(btn_skip, True, True, 0) # Set values if image_path: img_break.set_from_file(image_path) lbl_message.set_label(message) lbl_widget.set_markup(widget) self.windows.append(window) self.count_labels.append(lbl_count) # Set visual to apply css theme. It should be called before show method. window.set_visual(window.get_screen().get_rgba_visual()) if self.context['desktop'] == 'kde': # Fix flickering screen in KDE by setting opacity to 1 window.set_opacity(0.9) # In Unity, move the window before present window.move(x, y) window.resize(monitor_gemoetry.width, monitor_gemoetry.height) window.stick() window.set_keep_above(True) window.present() # In other desktop environments, move the window after present window.move(x, y) window.resize(monitor_gemoetry.width, monitor_gemoetry.height) logging.info("Moved break screen to Display[%d, %d]", x, y) window.fullscreen() def __update_count_down(self, count): """ Update the countdown on all break screens. """ for label in self.count_labels: label.set_text(count) def __lock_keyboard(self): """ Lock the keyboard to prevent the user from using keyboard shortcuts """ logging.info("Lock the keyboard") self.lock_keyboard = True # Grab the keyboard root = self.display.screen().root root.change_attributes(event_mask=X.KeyPressMask | X.KeyReleaseMask) root.grab_keyboard(True, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime) # Consume keyboard events while self.lock_keyboard: if self.display.pending_events() > 0: # Avoid waiting for next event by checking pending events event = self.display.next_event() if self.enable_shortcut and event.type == X.KeyPress: if event.detail == self.keycode_shortcut_skip and not self.strict_break: self.skip_break() break elif self.enable_postpone and event.detail == self.keycode_shortcut_postpone: self.postpone_break() break else: # Reduce the CPU usage by sleeping for a second time.sleep(1) def __release_keyboard(self): """ Release the locked keyboard. """ logging.info("Unlock the keyboard") self.lock_keyboard = False self.display.ungrab_keyboard(X.CurrentTime) self.display.flush() def __destroy_all_screens(self): """ Close all the break screens. """ for win in self.windows: win.destroy() del self.windows[:] del self.count_labels[:]
class GlobalKeyBinding(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): GObject.GObject.__init__(self) threading.Thread.__init__(self) self.setDaemon(True) self.keymap = capi.get_widget(gdk.gdk_keymap_get_default()) self.display = Display() self.screen = self.display.screen() self.window = self.screen.root self.ignored_masks = self.get_mask_combinations(X.LockMask | X.Mod2Mask | X.Mod5Mask) self.map_modifiers() self.raw_keyval = None self.keytext = "" def is_hotkey(self, key, modifier): keymatch = False modmatch = False modifier = modifier & ~Gdk.ModifierType.SUPER_MASK modint = int(modifier) if self.get_keycode(key) == self.keycode: keymatch = True for ignored_mask in self.ignored_masks: if self.modifiers | ignored_mask == modint | ignored_mask: modmatch = True break return keymatch and modmatch def map_modifiers(self): gdk_modifiers = (Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.SHIFT_MASK, Gdk.ModifierType.MOD1_MASK, Gdk.ModifierType.MOD2_MASK, Gdk.ModifierType.MOD3_MASK, Gdk.ModifierType.MOD4_MASK, Gdk.ModifierType.MOD5_MASK, Gdk.ModifierType.SUPER_MASK, Gdk.ModifierType.HYPER_MASK) self.known_modifiers_mask = 0 for modifier in gdk_modifiers: if "Mod" not in Gtk.accelerator_name( 0, modifier) or "Mod4" in Gtk.accelerator_name( 0, modifier): self.known_modifiers_mask |= modifier def get_keycode(self, keyval): count = c_int() array = (KeymapKey * 10)() keys = cast(array, POINTER(KeymapKey)) gdk.gdk_keymap_get_entries_for_keyval(hash(self.keymap), keyval, byref(keys), byref(count)) return keys[0].keycode def grab(self, key): accelerator = key accelerator = accelerator.replace("<Super>", "<Mod4>") keyval, modifiers = Gtk.accelerator_parse(accelerator) if not accelerator or (not keyval and not modifiers): self.keycode = None self.modifiers = None return False self.keytext = key self.keycode = self.get_keycode(keyval) self.modifiers = int(modifiers) catch = error.CatchError(error.BadAccess) for ignored_mask in self.ignored_masks: mod = modifiers | ignored_mask result = self.window.grab_key(self.keycode, mod, True, X.GrabModeAsync, X.GrabModeSync, onerror=catch) self.display.flush() # sync has been blocking. Don't know why. #self.display.sync() if catch.get_error(): return False return True def ungrab(self): if self.keycode: self.window.ungrab_key(self.keycode, X.AnyModifier, self.window) def rebind(self, key): self.ungrab() if key != "": self.grab(key) else: self.keytext = "" def set_focus_window(self, window=None): self.ungrab() if window is None: self.window = self.screen.root else: self.window = self.display.create_resource_object( "window", gdk.gdk_x11_drawable_get_xid(hash(window))) self.grab(self.keytext) def get_mask_combinations(self, mask): return [x for x in xrange(mask + 1) if not (x & ~mask)] def idle(self): self.emit("activate") return False def activate(self): GLib.idle_add(self.run) def run(self): self.running = True wait_for_release = False while self.running: event = self.display.next_event() try: self.current_event_time = event.time if event.detail == self.keycode and event.type == X.KeyPress and not wait_for_release: modifiers = event.state & self.known_modifiers_mask if modifiers == self.modifiers: wait_for_release = True self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) elif event.detail == self.keycode and wait_for_release: if event.type == X.KeyRelease: wait_for_release = False GLib.idle_add(self.idle) self.display.allow_events(X.AsyncKeyboard, event.time) else: self.display.allow_events(X.ReplayKeyboard, event.time) except AttributeError: continue def stop(self): self.running = False self.ungrab() self.display.close()
class GridMouseVooDoo(Thread): def __init__(self, wm, button=10): Thread.__init__(self) self.button = button self.display = Display() self.ctx = self.display.record_create_context( 0, [record.AllClients], [{ 'core_requests': (0, 0), 'core_replies': (0, 0), 'ext_requests': (0, 0, 0, 0), 'ext_replies': (0, 0, 0, 0), 'delivered_events': (0, 0), 'device_events': (X.ButtonPressMask, X.ButtonReleaseMask), 'errors': (0, 0), 'client_started': False, 'client_died': False, }]) self.wm = wm self.area = None def run(self): logger.info("Starting Thread") self.display.record_enable_context(self.ctx, self.handler) self.display.record_free_context(self.ctx) def handler(self, reply): logger.debug("Handler called") data = reply.data while len(data): event, data = rq.EventField(None).parse_binary_value(data, self.display.display, None, None) if event.type == X.ButtonPress: if event.detail == self.button: self.press(event.root_x, event.root_y) elif event.type == X.ButtonRelease: if event.detail == self.button: self.release(event.root_x, event.root_y) else: self.move(event.root_x, event.root_y) def stop(self): logger.info("Stopping Thread") self.display.record_disable_context(self.ctx) self.display.ungrab_pointer(X.CurrentTime) self.display.flush() logger.debug("Thread stopped") def press(self, x, y): self.area = Area(x, y) logger.info("Mouse button pressed") self.move(x, y) def release(self, x, y): self.move(x, y) logger.info("%s" % self.area) self.wm.move_window_to_area(self.area) self.area = None logger.info("Mouse button released") def move(self, x, y): if not self.area: logger.debug("Mouse moved to %d, %d but no button pressed" % (x, y)) return self.area.add_point(x, y) logger.debug("Mouse moved to %d, %d" % (x, y))
class PyKeyboardEvent(PyKeyboardEventMeta): """ The PyKeyboardEvent implementation for X11 systems (mostly linux). This allows one to listen for keyboard input. """ def __init__(self, display=None): PyKeyboardEventMeta.__init__(self) self.display = Display(display) self.display2 = Display(display) self.ctx = self.display2.record_create_context( 0, [record.AllClients], [{ 'core_requests': (0, 0), 'core_replies': (0, 0), 'ext_requests': (0, 0, 0, 0), 'ext_replies': (0, 0, 0, 0), 'delivered_events': (0, 0), 'device_events': (X.KeyPress, X.KeyRelease), 'errors': (0, 0), 'client_started': False, 'client_died': False, }]) self.shift_state = 0 # 0 is off, 1 is on self.alt_state = 0 # 0 is off, 2 is on self.mod_keycodes = self.get_mod_keycodes() def run(self): """Begin listening for keyboard input events.""" self.state = True if self.capture: self.display2.screen().root.grab_keyboard(True, X.KeyPressMask | X.KeyReleaseMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime) self.display2.record_enable_context(self.ctx, self.handler) self.display2.record_free_context(self.ctx) def stop(self): """Stop listening for keyboard input events.""" self.state = False self.display.record_disable_context(self.ctx) self.display.ungrab_keyboard(X.CurrentTime) self.display.flush() self.display2.record_disable_context(self.ctx) self.display2.ungrab_keyboard(X.CurrentTime) self.display2.flush() def handler(self, reply): """Upper level handler of keyboard events.""" data = reply.data while len(data): event, data = rq.EventField(None).parse_binary_value(data, self.display.display, None, None) if event.type == X.KeyPress: if self.escape_code(event): # Quit if this returns True self.stop() else: self._key_press(event.detail) elif event.type == X.KeyRelease: self._key_release(event.detail) else: print('WTF: {0}'.format(event.type)) def _key_press(self, keycode): """A key has been pressed, do stuff.""" #Alter modification states if keycode in self.mod_keycodes['Shift'] or keycode in self.mod_keycodes['Lock']: self.toggle_shift_state() elif keycode in self.mod_keycodes['Alt']: self.toggle_alt_state() else: self.key_press(keycode) def _key_release(self, keycode): """A key has been released, do stuff.""" #Alter modification states if keycode in self.mod_keycodes['Shift']: self.toggle_shift_state() elif keycode in self.mod_keycodes['Alt']: self.toggle_alt_state() else: self.key_release(keycode) def escape_code(self, event): if event.detail == self.lookup_character_value('Escape'): return True return False def lookup_char_from_keycode(self, keycode): keysym =self.display.keycode_to_keysym(keycode, self.shift_state + self.alt_state) if keysym: char = self.display.lookup_string(keysym) return char else: return None def get_mod_keycodes(self): """ Detects keycodes for modifiers and parses them into a dictionary for easy access. """ modifier_mapping = self.display.get_modifier_mapping() modifier_dict = {} nti = [('Shift', X.ShiftMapIndex), ('Control', X.ControlMapIndex), ('Mod1', X.Mod1MapIndex), ('Alt', X.Mod1MapIndex), ('Mod2', X.Mod2MapIndex), ('Mod3', X.Mod3MapIndex), ('Mod4', X.Mod4MapIndex), ('Mod5', X.Mod5MapIndex), ('Lock', X.LockMapIndex)] for n, i in nti: modifier_dict[n] = list(modifier_mapping[i]) return modifier_dict def lookup_character_value(self, character): """ Looks up the keysym for the character then returns the keycode mapping for that keysym. """ ch_keysym = string_to_keysym(character) if ch_keysym == 0: ch_keysym = string_to_keysym(special_X_keysyms[character]) return self.display.keysym_to_keycode(ch_keysym) def toggle_shift_state(self): '''Does toggling for the shift state.''' if self.shift_state == 0: self.shift_state = 1 elif self.shift_state == 1: self.shift_state = 0 else: return False return True def toggle_alt_state(self): '''Does toggling for the alt state.''' if self.alt_state == 0: self.alt_state = 2 elif self.alt_state == 2: self.alt_state = 0 else: return False return True
class GlobalKeyBinding(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): GObject.GObject.__init__(self) threading.Thread.__init__(self) self.setDaemon(True) self.keymap = Gdk.Keymap().get_default() self.display = Display() self.screen = self.display.screen() self.window = self.screen.root self.ignored_masks = self.get_mask_combinations(X.LockMask | X.Mod2Mask | X.Mod5Mask) self.map_modifiers() self.raw_keyval = None self.keytext = "" def map_modifiers(self): gdk_modifiers = (Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.SHIFT_MASK, Gdk.ModifierType.MOD1_MASK, Gdk.ModifierType.MOD2_MASK, Gdk.ModifierType.MOD3_MASK, Gdk.ModifierType.MOD4_MASK, Gdk.ModifierType.MOD5_MASK, Gdk.ModifierType.SUPER_MASK, Gdk.ModifierType.HYPER_MASK) self.known_modifiers_mask = 0 for modifier in gdk_modifiers: if "Mod" not in Gtk.accelerator_name( 0, modifier) or "Mod4" in Gtk.accelerator_name( 0, modifier): self.known_modifiers_mask |= modifier def grab(self, key): accelerator = key accelerator = accelerator.replace("<Super>", "<Mod4>") keyval, modifiers = Gtk.accelerator_parse(accelerator) if not accelerator or (not keyval and not modifiers): self.keycode = None self.modifiers = None return False self.keytext = key try: self.keycode = self.keymap.get_entries_for_keyval( keyval).keys[0].keycode except AttributeError: # In older Gtk3 the get_entries_for_keyval() returns an unnamed tuple... self.keycode = self.keymap.get_entries_for_keyval( keyval)[1][0].keycode self.modifiers = int(modifiers) # Request to receive key press/release reports from other windows that may not be using modifiers catch = error.CatchError(error.BadWindow) if self.modifiers: self.window.change_attributes(onerror=catch, event_mask=X.KeyPressMask | X.KeyReleaseMask) else: self.window.change_attributes(onerror=catch, event_mask=X.NoEventMask) if catch.get_error(): return False catch = error.CatchError(error.BadAccess) for ignored_mask in self.ignored_masks: mod = modifiers | ignored_mask result = self.window.grab_key(self.keycode, mod, True, X.GrabModeAsync, X.GrabModeAsync, onerror=catch) self.display.flush() # sync has been blocking. Don't know why. #self.display.sync() if catch.get_error(): return False return True def ungrab(self): if self.keycode: self.window.ungrab_key(self.keycode, X.AnyModifier, self.window) def rebind(self, key): self.ungrab() if key != "": self.grab(key) else: self.keytext = "" def set_focus_window(self, window=None): self.ungrab() if window is None: self.window = self.screen.root else: self.window = self.display.create_resource_object( "window", window.get_xid()) self.grab(self.keytext) def get_mask_combinations(self, mask): return [x for x in xrange(mask + 1) if not (x & ~mask)] def idle(self): self.emit("activate") return False def activate(self): GLib.idle_add(self.run) def run(self): self.running = True wait_for_release = False while self.running: event = self.display.next_event() if self.modifiers: # Use simpler logic when using traditional combined keybindings modifiers = event.state & self.known_modifiers_mask if event.type == X.KeyPress and event.detail == self.keycode and modifiers == self.modifiers: GLib.idle_add(self.idle) else: try: if event.type == X.KeyPress and event.detail == self.keycode and not wait_for_release: modifiers = event.state & self.known_modifiers_mask if modifiers == self.modifiers: wait_for_release = True elif event.type == X.KeyRelease and event.detail == self.keycode and wait_for_release: GLib.idle_add(self.idle) wait_for_release = False else: self.display.ungrab_keyboard(X.CurrentTime) # Send the event up in case another window is listening to it self.display.send_event( event.window, event, X.KeyPressMask | X.KeyReleaseMask, True) wait_for_release = False except AttributeError: continue def stop(self): self.running = False self.ungrab() self.display.close()
class Prenestr(object): def __init__(self): self.ratio = 0.5 self.wborder = 10 self.hborder = 30 self.disp = Display() self.root = self.disp.screen().root # we tell the X server we want to catch keyPress event self.root.change_attributes(event_mask=X.KeyPressMask) self.grab_key(K_L) self.grab_key(K_H) self.grab_key(K_L, X.Mod4Mask | X.ShiftMask) self.grab_key(K_H, X.Mod4Mask | X.ShiftMask) self.grab_key(K_T) self.grab_key(K_ENTER) while True: event = self.root.display.next_event() if event.type == X.KeyPress: self.keypress(event) def get_active(self): id = self.root.get_full_property( self.disp.intern_atom("_NET_ACTIVE_WINDOW"), 0).value[0] obj = self.disp.create_resource_object('window', id) return (id, obj) def _send_event(self, win, ctype, data, mask=None): data = (data + ([0] * (5 - len(data))))[:5] ev = protocol.event.ClientMessage(window=win, client_type=ctype, data=(32, (data))) self.root.send_event(ev, event_mask=X.SubstructureRedirectMask) def workarea(self): v = self.root.get_full_property( self.disp.intern_atom("_NET_WORKAREA"), 0).value return v[0], v[1], v[2], v[3] def move(self, win, to='left', y_pos=0, y_nbwin=1): id, obj = win rx, ry, rw, rh = self.workarea() if to == 'left': x = rx y = ry w = rw * self.ratio - self.wborder h = rh elif to == 'right': x = rx + rw * self.ratio + self.wborder y = ry + y_pos * (rh / y_nbwin) w = rw * (1 - self.ratio) - self.wborder h = rh / y_nbwin if y_nbwin > 1: h = h - self.hborder # Reset state self._send_event(id, self.disp.intern_atom("_NET_WM_STATE"), [0, self.disp.intern_atom("_NET_WM_STATE_MAXIMIZED_VERT"), self.disp.intern_atom("_NET_WM_STATE_MAXIMIZED_HORZ")]) obj.configure(x=x, y=y, width=w, height=h, stack_mode=X.Above) self._send_event(id, self.disp.intern_atom("_NET_ACTIVE_WINDOW"), []) self.disp.flush() def grab_key(self, key, mask=X.Mod4Mask, ungrab=False): self.root.grab_key(key, mask, 1, X.GrabModeAsync, X.GrabModeAsync) if ungrab: self.ungrab_list.append(key) def ungrab_key(self, key, mask=X.Mod4Mask): self.root.ungrab_key(key, mask, 1) def tile(self, master='position'): current_window = self.get_active() win_list = self.root.get_full_property( self.disp.intern_atom("_NET_CLIENT_LIST"), Xatom.WINDOW).value current_desktop = self.root.get_full_property( self.disp.intern_atom("_NET_CURRENT_DESKTOP"), 0).value[0] desk_list = [] for win_id in win_list: obj = self.disp.create_resource_object('window', win_id) windesk = obj.get_full_property( self.disp.intern_atom("_NET_WM_DESKTOP"), 0).value[0] if windesk == current_desktop: # This window is on the current desktop # Skip if transient transient = obj.get_wm_transient_for() if transient and transient != self.root: continue # Skip if hidden state = obj.get_full_property( self.disp.intern_atom("_NET_WM_STATE"), Xatom.ATOM) dock = obj.get_full_property( self.disp.intern_atom("_NET_WM_WINDOW_TYPE"), Xatom.ATOM) if (state and self.disp.intern_atom("_NET_WM_STATE_HIDDEN") in state.value or self.disp.intern_atom("_NET_WM_STATE_SKIP_TASKBAR") in state.value or self.disp.intern_atom("_NET_WM_STATE_SKIP_PAGER") in state.value): # hidden continue if (dock and self.disp.intern_atom("_NET_WM_WINDOW_TYPE_DOCK") in dock.value and self.disp.intern_atom("_NET_WM_WINDOW_TYPE_TOOLBAR") in dock.value and self.disp.intern_atom("_NET_WM_WINDOW_TYPE_MENU") in dock.value and self.disp.intern_atom("_NET_ACTIVE_WINDOW_TYPE_SPLASH") in dock.value and self.disp.intern_atom("_NET_ACTIVE_WINDOW_TYPE_DIALOG") in dock.value): # hidden continue desk_list.append((win_id, obj)) if not desk_list: return def get_geom(window): wg = window.get_geometry() tl = window.translate_coords(self.root, wg.x, wg.y) return (-tl.x, -tl.y, wg.width, wg.height) geom = [(w, get_geom(w[1])) for w in desk_list] if master == 'position': left = min(geom, key=lambda l: l[1][0])[0] else: left = current_window self.move(left, to="left") others = [w for w in geom if w[0][0] != left[0]] others.sort(key=lambda l: l[1][1]) for pos, win in enumerate(others): self.move(win[0], to="right", y_pos=pos, y_nbwin=len(others)) # Reactivate window self._send_event(current_window[0], self.disp.intern_atom("_NET_ACTIVE_WINDOW"), []) return def keypress(self, event): if event.detail == K_H: if event.state & X.ShiftMask: self.ratio -= 0.1 if self.ratio < 0: self.ratio = 0 self.tile() else: self.move(self.get_active()) elif event.detail == K_L: if event.state & X.ShiftMask: self.ratio += 0.1 if self.ratio > 1: self.ratio = 1 self.tile() else: self.move(self.get_active(), to='right') elif event.detail == K_T: self.tile() elif event.detail == K_ENTER: self.tile(master='active')
but[i] = d.keysym_to_keycode(XK.string_to_keysym(but[i])) ser = serial.Serial(sys.argv[1], 9600) while 1: ser.write('a'); try: actual = ser.read() while (actual == -1): actual = ser.read() actual += ser.readline() except: print "Something is wrong. Restarting..." ser.close() ser = serial.Serial(sys.argv[1], 9600) if len(actual) != 9: continue #Bad for i in range(0, 8): if list(actual)[i] == '0': xtest.fake_input(d, X.KeyPress, but[i]) d.sync() d.flush() else: xtest.fake_input(d, X.KeyRelease, but[i]) d.sync() d.flush() sleep(delay)
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 Detect_Double_Ctrl(object): def show_app(self): DoubleCtrlSignal.instance().show_signal.emit() def handler(self, reply): """ This function is called when a xlib event is fired """ data = reply.data while len(data): event, data = rq.EventField(None).parse_binary_value( data, self.disp.display, None, None) keysym = self.loc_disp.keycode_to_keysym(event.detail, 0) character = lookup_keysym(keysym) if event.type == X.KeyPress: self.keylistener.press(character) if event.type == X.KeyRelease: self.keylistener.release(character) # if event.detail == 1 and event.type == 5: # delta = 2 # if self.first_click == 0.0: # self.first_click = time.time() # print 'a', delta # else: # self.second_click = time.time() # delta = self.second_click - self.first_click # self.first_click, self.second_click = 0.0, 0.0 # print delta # if delta < 1: # with os.popen('xsel') as xsel: # word = xsel.read() # emit = True if re.search(r'[a-zA-z]', word) else False # if emit:#double click emit signal only when there exists words selected # x = event._data['root_x'] # y = event._data['root_y'] # # DoubleCtrlSignal.instance().doublle_ctrl_signal.emit(word, x, y) if character == 'Caps_Lock' and event.type == X.KeyPress: delta = 2 if self.first_click == 0.0: self.first_click = time.time() logging.debug('#a %s %s %s', self.first_click, self.second_click, delta) else: self.second_click = time.time() delta = self.second_click - self.first_click self.first_click, self.second_click = 0.0, 0.0 logging.debug('#b %s %s %s', self.first_click, self.second_click, delta) if delta < 1: with os.popen('xsel') as xsel: word = xsel.read() emit = True if re.search(r'[a-zA-z]', word) else False if emit: logging.debug(word) x = event._data['root_x'] y = event._data['root_y'] DoubleCtrlSignal.instance().doublle_ctrl_signal.emit( word, x, y) # if event.detail == 66: # if event.type == X.KeyPress: # self.flag +=1 # elif event.type == X.KeyRelease: # pass # if event.type == X.KeyPress and self.flag % 2 == 0: # temp = os.popen('xsel') # word = temp.read() # temp.close() # # double capslock emit signal even though there no words selected to show the window # x = event._data['root_x'] # y = event._data['root_y'] # DoubleCtrlSignal.instance().doublle_ctrl_signal.emit(word, x, y) if character == 'Escape': DoubleCtrlSignal.instance().esc_signal.emit() def detect_double_ctrl(self): root = self.disp.screen().root # Monitor keypress and button press self.ctx = self.disp.record_create_context( 0, [record.AllClients ], [{ 'core_requests': (0, 0), 'core_replies': (0, 0), 'ext_requests': (0, 0, 0, 0), 'ext_replies': (0, 0, 0, 0), 'delivered_events': (0, 0), 'device_events': (X.KeyReleaseMask, X.ButtonReleaseMask), 'errors': (0, 0), 'client_started': False, 'client_died': False, }]) self.disp.record_enable_context(self.ctx, self.handler) self.disp.record_free_context(self.ctx) def __init__(self): self.disp = Display() self.loc_disp = Display() self.ctx = None self.flag = 0 self.first_click = 0.0 self.second_click = 0.0 self.keylistener = KeyListener() self.keylistener.addKeyListener('Alt_L+l', self.show_app) self.keylistener.addKeyListener('l+Alt_L', self.show_app) self.mythread = threading.Thread(target=self.detect_double_ctrl) self.mythread.daemon = True self.mythread.start() def terminate(self): self.loc_disp.record_disable_context(self.ctx) self.loc_disp.flush() self.mythread.join()
class PyKeyboardEvent(PyKeyboardEventMeta): """ The PyKeyboardEvent implementation for X11 systems (mostly linux). This allows one to listen for keyboard input. """ def __init__(self, display=None): self.display = Display(display) self.display2 = Display(display) self.ctx = self.display2.record_create_context( 0, [record.AllClients], [{ 'core_requests': (0, 0), 'core_replies': (0, 0), 'ext_requests': (0, 0, 0, 0), 'ext_replies': (0, 0, 0, 0), 'delivered_events': (0, 0), 'device_events': (X.KeyPress, X.KeyRelease), 'errors': (0, 0), 'client_started': False, 'client_died': False, }]) self.lock_meaning = None #Get these dictionaries for converting keysyms and strings self.keysym_to_string, self.string_to_keysym = self.get_translation_dicts() #Identify and register special groups of keys self.modifier_keycodes = {} self.all_mod_keycodes = [] self.keypad_keycodes = [] #self.configure_keys() #Direct access to the display's keycode-to-keysym array #print('Keycode to Keysym map') #for i in range(len(self.display._keymap_codes)): # print('{0}: {1}'.format(i, self.display._keymap_codes[i])) PyKeyboardEventMeta.__init__(self) def run(self): """Begin listening for keyboard input events.""" self.state = True if self.capture: self.display2.screen().root.grab_keyboard(True, X.KeyPressMask | X.KeyReleaseMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime) self.display2.record_enable_context(self.ctx, self.handler) self.display2.record_free_context(self.ctx) def stop(self): """Stop listening for keyboard input events.""" self.state = False self.display.record_disable_context(self.ctx) self.display.ungrab_keyboard(X.CurrentTime) self.display.flush() self.display2.record_disable_context(self.ctx) self.display2.ungrab_keyboard(X.CurrentTime) self.display2.flush() def handler(self, reply): """Upper level handler of keyboard events.""" data = reply.data while len(data): event, data = rq.EventField(None).parse_binary_value(data, self.display.display, None, None) if self.escape(event): # Quit if this returns True self.stop() else: self._tap(event) def _tap(self, event): keycode = event.detail press_bool = (event.type == X.KeyPress) #Detect modifier states from event.state for mod, bit in self.modifier_bits.items(): self.modifiers[mod] = event.state & bit if keycode in self.all_mod_keycodes: keysym = self.display.keycode_to_keysym(keycode, 0) character = self.keysym_to_string[keysym] else: character = self.lookup_char_from_keycode(keycode) #All key events get passed to self.tap() self.tap(keycode, character, press=press_bool) def lookup_char_from_keycode(self, keycode): """ This will conduct a lookup of the character or string associated with a given keycode. """ #TODO: Logic should be strictly adapted from X11's src/KeyBind.c #Right now the logic is based off of #http://tronche.com/gui/x/xlib/input/keyboard-encoding.html #Which I suspect is not the whole story and may likely cause bugs keysym_index = 0 #TODO: Display's Keysyms per keycode count? Do I need this? #If the Num_Lock is on, and the keycode corresponds to the keypad if self.modifiers['Num_Lock'] and keycode in self.keypad_keycodes: if self.modifiers['Shift'] or self.modifiers['Shift_Lock']: keysym_index = 0 else: keysym_index = 1 elif not self.modifiers['Shift'] and self.modifiers['Caps_Lock']: #Use the first keysym if uppercase or uncased #Use the uppercase keysym if the first is lowercase (second) keysym_index = 0 keysym = self.display.keycode_to_keysym(keycode, keysym_index) #TODO: Support Unicode, Greek, and special latin characters if keysym & 0x7f == keysym and chr(keysym) in 'abcdefghijklmnopqrstuvwxyz': keysym_index = 1 elif self.modifiers['Shift'] and self.modifiers['Caps_Lock']: keysym_index = 1 keysym = self.display.keycode_to_keysym(keycode, keysym_index) #TODO: Support Unicode, Greek, and special latin characters if keysym & 0x7f == keysym and chr(keysym) in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ': keysym_index = 0 elif self.modifiers['Shift'] or self.modifiers['Shift_Lock']: keysym_index = 1 if self.modifiers['Mode_switch']: keysym_index += 2 #Finally! Get the keysym keysym = self.display.keycode_to_keysym(keycode, keysym_index) #If the character is ascii printable, return that character if keysym & 0x7f == keysym and self.ascii_printable(keysym): return chr(keysym) #If the character was not printable, look for its name try: char = self.keysym_to_string[keysym] except KeyError: print('Unable to determine character.') print('Keycode: {0} KeySym {1}'.format(keycode, keysym)) return None else: return char def escape(self, event): if event.detail == self.lookup_character_keycode('Escape'): return True return False def configure_keys(self): """ This function locates the keycodes corresponding to special groups of keys and creates data structures of them for use by the PyKeyboardEvent instance; including the keypad keys and the modifiers. The keycodes pertaining to the keyboard modifiers are assigned by the modifier name in a dictionary. This dictionary can be accessed in the following manner: self.modifier_keycodes['Shift'] # All keycodes for Shift Masking It also assigns certain named modifiers (Alt, Num_Lock, Super), which may be dynamically assigned to Mod1 - Mod5 on different platforms. This should generally allow the user to do the following lookups on any system: self.modifier_keycodes['Alt'] # All keycodes for Alt Masking self.modifiers['Alt'] # State of Alt mask, non-zero if "ON" """ modifier_mapping = self.display.get_modifier_mapping() all_mod_keycodes = [] mod_keycodes = {} mod_index = [('Shift', X.ShiftMapIndex), ('Lock', X.LockMapIndex), ('Control', X.ControlMapIndex), ('Mod1', X.Mod1MapIndex), ('Mod2', X.Mod2MapIndex), ('Mod3', X.Mod3MapIndex), ('Mod4', X.Mod4MapIndex), ('Mod5', X.Mod5MapIndex)] #This gets the list of all keycodes per Modifier, assigns to name for name, index in mod_index: codes = [v for v in list(modifier_mapping[index]) if v] mod_keycodes[name] = codes all_mod_keycodes += codes def lookup_keycode(string): keysym = self.string_to_keysym[string] return self.display.keysym_to_keycode(keysym) #Dynamically assign Lock to Caps_Lock, Shift_Lock, Alt, Num_Lock, Super, #and mode switch. Set in both mod_keycodes and self.modifier_bits #Try to assign Lock to Caps_Lock or Shift_Lock shift_lock_keycode = lookup_keycode('Shift_Lock') caps_lock_keycode = lookup_keycode('Caps_Lock') if shift_lock_keycode in mod_keycodes['Lock']: mod_keycodes['Shift_Lock'] = [shift_lock_keycode] self.modifier_bits['Shift_Lock'] = self.modifier_bits['Lock'] self.lock_meaning = 'Shift_Lock' elif caps_lock_keycode in mod_keycodes['Lock']: mod_keycodes['Caps_Lock'] = [caps_lock_keycode] self.modifier_bits['Caps_Lock'] = self.modifier_bits['Lock'] self.lock_meaning = 'Caps_Lock' else: self.lock_meaning = None #print('Lock is bound to {0}'.format(self.lock_meaning)) #Need to find out which Mod# to use for Alt, Num_Lock, Super, and #Mode_switch num_lock_keycodes = [lookup_keycode('Num_Lock')] alt_keycodes = [lookup_keycode(i) for i in ['Alt_L', 'Alt_R']] super_keycodes = [lookup_keycode(i) for i in ['Super_L', 'Super_R']] mode_switch_keycodes = [lookup_keycode('Mode_switch')] #Detect Mod number for Alt, Num_Lock, and Super for name, keycodes in list(mod_keycodes.items()): for alt_key in alt_keycodes: if alt_key in keycodes: mod_keycodes['Alt'] = keycodes self.modifier_bits['Alt'] = self.modifier_bits[name] for num_lock_key in num_lock_keycodes: if num_lock_key in keycodes: mod_keycodes['Num_Lock'] = keycodes self.modifier_bits['Num_Lock'] = self.modifier_bits[name] for super_key in super_keycodes: if super_key in keycodes: mod_keycodes['Super'] = keycodes self.modifier_bits['Super'] = self.modifier_bits[name] for mode_switch_key in mode_switch_keycodes: if mode_switch_key in keycodes: mod_keycodes['Mode_switch'] = keycodes self.modifier_bits['Mode_switch'] = self.modifier_bits[name] #Assign the mod_keycodes to a local variable for access self.modifier_keycodes = mod_keycodes self.all_mod_keycodes = all_mod_keycodes #TODO: Determine if this might fail, perhaps iterate through the mapping #and identify all keycodes with registered keypad keysyms? #Acquire the full list of keypad keycodes self.keypad_keycodes = [] keypad = ['Space', 'Tab', 'Enter', 'F1', 'F2', 'F3', 'F4', 'Home', 'Left', 'Up', 'Right', 'Down', 'Prior', 'Page_Up', 'Next', 'Page_Down', 'End', 'Begin', 'Insert', 'Delete', 'Equal', 'Multiply', 'Add', 'Separator', 'Subtract', 'Decimal', 'Divide', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] for keyname in keypad: keypad_keycode = self.lookup_character_keycode('KP_' + keyname) self.keypad_keycodes.append(keypad_keycode) def lookup_character_keycode(self, character): """ Looks up the keysym for the character then returns the keycode mapping for that keysym. """ keysym = self.string_to_keysym.get(character, 0) if keysym == 0: keysym = self.string_to_keysym.get(special_X_keysyms[character], 0) return self.display.keysym_to_keycode(keysym) def get_translation_dicts(self): """ Returns dictionaries for the translation of keysyms to strings and from strings to keysyms. """ keysym_to_string_dict = {} string_to_keysym_dict = {} #XK loads latin1 and miscellany on its own; load latin2-4 and greek Xlib.XK.load_keysym_group('latin2') Xlib.XK.load_keysym_group('latin3') Xlib.XK.load_keysym_group('latin4') Xlib.XK.load_keysym_group('greek') #Make a standard dict and the inverted dict for string, keysym in Xlib.XK.__dict__.items(): if string.startswith('XK_'): string_to_keysym_dict[string[3:]] = keysym keysym_to_string_dict[keysym] = string[3:] return keysym_to_string_dict, string_to_keysym_dict def ascii_printable(self, keysym): """ If the keysym corresponds to a non-printable ascii character this will return False. If it is printable, then True will be returned. ascii 11 (vertical tab) and ascii 12 are printable, chr(11) and chr(12) will return '\x0b' and '\x0c' respectively. """ if 0 <= keysym < 9: return False elif 13 < keysym < 32: return False elif keysym > 126: return False else: return True
class KeyPointer: def __init__(self): self.display = Display () self.screen = self.display.screen() self.overlay = None self.root = self.screen.root self.keymap = gtk.gdk.keymap_get_default() self.click_keyval = gtk.keysyms.space self.click_keycode = self.keymap.get_entries_for_keyval(self.click_keyval)[0][0] self.finish_keyval = gtk.keysyms.Return self.finish_keycode = self.keymap.get_entries_for_keyval(self.finish_keyval)[0][0] self.setup_movementkeys(DEFAULT_MVMT) self.setup_keymapping(DEFAULT_MAP) self.init_gconf(GCONF_DIR) self.launch_cb() def init_gconf(self, app_dir): self.gconf = gconf.client_get_default () self.gconf.add_dir (app_dir, gconf.CLIENT_PRELOAD_NONE) self.gconf.notify_add (LAYOUT_KEY, self.gconf_cb) self.gconf.notify_add (MOVEMENT_KEY, self.gconf_cb) self.read_gconf(app_dir) def read_gconf(self, app_dir): gconf_keymappings = self.gconf.get_list(LAYOUT_KEY, gconf.VALUE_STRING) gconf_font_size = self.gconf.get_int(FONT_SIZE_KEY) gconf_font_name = self.gconf.get_string(FONT_NAME_KEY) gconf_block_hint = self.gconf.get_bool(BLOCK_HINT_KEY) keymappings = [] for line in gconf_keymappings: keymappings.append(line.strip()) self.setup_keymapping(keymappings) self.overlay.setup_fonts(gconf_font_name, gconf_font_size) self.overlay.set_block_hint(gconf_block_hint) def setup_movementkeys(self, mapping_dict): self.movement_dict = mapping_dict self.movement_keycodes = {} print mapping_dict for key in mapping_dict: keyval = gtk.gdk.unicode_to_keyval(ord(key)) keyval_entries = self.keymap.get_entries_for_keyval(keyval) for keyval_tuple in keyval_entries: self.movement_keycodes[keyval_tuple] = mapping_dict[key] def setup_keymapping(self, mapping_array): # Map the keys to an x,y pair of where the key falls on the keyboard self.keyboard_keyvals = {} self.keymapping_array = mapping_array self.max_height = len(mapping_array) self.max_width = max([ len(x) for x in mapping_array ]) for y in xrange(len(mapping_array)): for x in xrange(len(mapping_array[y])): keyval = gtk.gdk.unicode_to_keyval(ord(mapping_array[y][x])) self.keyboard_keyvals[keyval] = (x, y) if self.overlay: self.overlay.destroy() self.overlay = Overlay(mapping_array) def gconf_cb(self, *args): # One of our settings changed, probably should re-read gconf data self.read_gconf(GCONF_DIR) def launch_cb(self, keybinding=None): t = threading.Thread(target=self.handle_screen) t.start() def handle_screen(self): w = self.screen.width_in_pixels h = self.screen.height_in_pixels gobject.idle_add(self.overlay.show, w, h) print 'Grabbing Keyboard Focus' self.root.grab_keyboard(True, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime) print 'Placing pointer' try: self.handle_keypresses() self.display.ungrab_keyboard(X.CurrentTime) self.display.flush() print 'Finished placing pointer' except: print "Couldn't handle keypresses" gtk.main_quit() gobject.idle_add(self.overlay.hide) def handle_keypress(self, e): # Find the coordinates of the key pressed # Check if this is a keypress event (not a release or button, etc) if e.__class__ is not Xlib.protocol.event.KeyPress: print "Not a KeyPress event" return # Check if this a movement or absolute mapping. gtk.gdk.threads_enter() keyval_tuple = self.keymap.translate_keyboard_state(e.detail, e.state, e.type) gtk.gdk.threads_leave() keyval, group, level, modifiers = keyval_tuple keycode = e.detail state = e.state if e.detail == self.click_keycode: # button= 1 left, 2 middle, 3 right def mouse_click(button): self.overlay.hide() time.sleep(0.10) Xlib.ext.xtest.fake_input(self.display,Xlib.X.ButtonPress, button) self.display.sync() Xlib.ext.xtest.fake_input(self.display,Xlib.X.ButtonRelease, button) self.display.sync() self.overlay.show(self.overlay.old_w, self.overlay.old_h) mouse_click(1) if keyval not in self.keyboard_keyvals and \ keycode not in self.movement_keycodes: print "Keycode and keyval not in dictionary" return e.detail == self.finish_keycode w = self.screen.width_in_pixels h = self.screen.height_in_pixels print "012" # Find out if there are any modifiers being pressed # If ctrl + movement key is being pressed, move over by some amount if state & X.ControlMask and (keycode, group, level) in self.movement_keycodes: print "Control Hold Movement Keys" movement = self.movement_keycodes[(keycode, group, level)] # move the cursor a little # Not sure how to calculate the amount to move by? # Maybe take the smallest h_block, w_block we have and divide into thirds? h_block = float(h) / self.max_height / 3 w_block = float(w) / self.max_width / 3 cursor_position = self.root.query_pointer() x = cursor_position.root_x y = cursor_position.root_y to_x, to_y = x, y if movement == 'left': to_x = x - w_block if movement == 'down': to_y = y + h_block if movement == 'up': to_y = y - w_block if movement == 'right': to_x = x + w_block else: print 'Moving Pointer' x, y = self.keyboard_keyvals[keyval] h_block = float(h) / len(self.keymapping_array) w_block = float(w) / len(self.keymapping_array[y]) # divide the width by the number of rows we have and multiply by x to # figure out where the cursor goes to_x = w_block * x + (w_block / 2) to_y = h_block * y + (h_block / 2) print to_x, to_y self.root.warp_pointer(to_x, to_y) return e.detail == self.finish_keycode def handle_keypresses(self): self.root.change_attributes(event_mask = X.KeyPressMask) self.screen = self.display.screen() while True: print "Handling Keyboard Event" event = self.root.display.next_event() print event try: if self.handle_keypress(event): break except Exception, e: traceback.print_exc() break self.root.change_attributes(event_mask = X.NoEventMask) self.display.allow_events(X.AsyncKeyboard, X.CurrentTime)
class KnoX: Geometry = namedtuple("Geometry", "x y width height") FrameExtents = namedtuple("FrameExtents", "left right top bottom") def __init__(self): #self.display = Display(os.environ.get("DISPLAY", ":0.0")) self.display = Display() print("Connected to X DISPLAY %r" % self.display.get_display_name()) self.display.set_error_handler(self.knox_error_handler) self.screen = self.display.screen() self.root = self.screen.root self.atoms = dict() self.atom_names = dict() self.keysyms = Keysyms() self.modifiers = Modifiers(self) self._supported_properties = None self._acceptable_error_sequence = 0 self._acceptable_errors = dict() self._silenced_errors = set() def fileno(self): """This function is here to make select work with this object""" return self.display.fileno() @contextmanager def silenced_error(self, error): silencer = self.silence_error(error) try: yield silencer finally: self.remove_silencer(silencer) def silence_error(self, error): k = self._acceptable_error_sequence self._acceptable_errors[k] = error self._acceptable_error_sequence += 1 self._silenced_errors = set(self._acceptable_errors.values()) return k def remove_silencer(self, key): if key in self._acceptable_errors: del self._acceptable_errors[key] self._silenced_errors = set(self._acceptable_errors.values()) def knox_error_handler(self, err, *args): if type(err) not in self._silenced_errors: print("X protocol error: %s" % err) traceback.print_stack() # def wait_for_event(self, timeout_seconds): # """ Wait up to `timeout_seconds` seconds for an event to be queued. # Return True, if a xevent is available. # Return False, if the timeout was reached. # from https://gist.github.com/fphammerle/d81ca3ff0a169f062a9f28e57b18f04d""" # rlist = select.select( # [self.display], # rlist # [], # wlist # [], # xlist # timeout_seconds, # timeout [seconds] # )[0] # return len(rlist) > 0 def next_event(self, wait=True): if (wait or self.display.pending_events()): return self.display.next_event() else: return None # def next_event(self, event_loop): # event_loop.register_reader(self.display, def atom(self, name, only_if_exists=False): if isinstance(name, int): a = name elif name not in self.atoms: a = self.display.get_atom(name, only_if_exists=only_if_exists) self.atoms[name] = a else: a = self.atoms[name] return a def atom_name(self, atom): if atom in self.atom_names: return self.atom_names[atom] name = self.display.get_atom_name(atom) if name: self.atom_names[atom] = name if name not in self.atoms: self.atoms[name] = atom return name def get_prop(self, window, name): prop_name = self.atom(name, only_if_exists=True) if not prop_name: return None if isinstance(window, int): window = self.get_window(window) p = window.get_full_property(prop_name, X.AnyPropertyType) if p: return p.value def get_text_prop(self, window, name): prop_name = self.atom(name, only_if_exists=True) if not prop_name: return None s = window.get_full_text_property(prop_name, Xatom.STRING) if not s: t = self.atom("UTF8_STRING", only_if_exists=True) if t: s = window.get_full_text_property(prop_name, t) return s def onerror(self, *args, **kwargs): print("ERROR: something bad happened about %r and %r" % (args, kwargs)) raise Exception("Error is bad...") def set_prop(self, window, name, type_name, value): if isinstance(window, int): window = self.get_window(window) if isinstance(type_name, int): prop_type_name = type_name #type_name = self.atom_name(prop_type_name) else: prop_type_name = self.atom(type_name, only_if_exists=False) prop_name = self.atom(name, only_if_exists=False) if value is None: window.delete_property(prop_name) else: window.change_property(prop_name, prop_type_name, 32, value, mode=X.PropModeReplace, onerror=self.onerror) def send_prop_change_event( self, property_name, data, target=None, window=None, ): if target is None: target = self.root if window is None: window = target ev = protocol.event.ClientMessage(window=window, client_type=self.atom(property_name), data=data) target.send_event(ev, event_mask=X.SubstructureNotifyMask | X.SubstructureRedirectMask, propagate=False, onerror=self.onerror) def current_desktop(self, desktop=None, wait=True): prop_name = "_NET_CURRENT_DESKTOP" if desktop is None: pv = self.get_prop(self.root, prop_name) if pv: return pv[0] else: v = array('I', [desktop]) #self.set_prop(self.root, prop_name, Xatom.CARDINAL, v) self.send_prop_change_event( prop_name, (32, [desktop, X.CurrentTime, 0, 0, 0])) self.flush() w = Waiter(wait) while w.wait(): print("DESKTOPCHECK", hex(desktop)) if self.current_desktop() == desktop: print("DESKTOP OK") break def get_wm_pid(self, window): pid_prop = self.get_prop(window, "_NET_WM_PID") if pid_prop: return pid_prop[0] return None def get_wm_name(self, window): if isinstance(window, int): window = self.get_window(window) # window.get_wm_name gets only STRING property and returns nothing # if it's UTF8_STRING return self.get_text_prop(window, Xatom.WM_NAME) def active_window(self, window=None, wait=3, id_only=False): prop_name = "_NET_ACTIVE_WINDOW" if window is None: pv = self.get_prop(self.root, prop_name) if pv and pv[0]: window = self.get_window(pv[0]) if window and window.get_wm_name() != 'Desktop': if id_only: return window.id else: return window else: if isinstance(window, int): window = self.get_window(window) desktop = self.get_desktop_for_window(window) self.current_desktop(desktop) #v = array('I', [ window.id, 0 ]) #self.set_prop(self.root, prop_name, Xatom.WINDOW, v) # data[0]: source indication # 1: when the request comes from an application # 2: from a pager # 0: no spec. self.send_prop_change_event(prop_name, (32, [2, X.CurrentTime, 0, 0, 0]), window=window) self.flush() #self.raise_window(window) # it won't become active until it's focused focused = self.set_focused_window(window, wait=1) w = Waiter(wait) while w.wait(): a = self.active_window() self.flush() if not focused: focused = self.set_focused_window(window, wait=1) self.flush() if a and a.id == window.id: print("Activated %r!" % window.id) return True self.send_prop_change_event(prop_name, (32, [2, X.CurrentTime, 0, 0, 0]), window=window) self.flush() print("Can't activate %d" % window.id) return False def get_focused_window(self, toplevel=True): f = self.display.get_input_focus() #f = protocol.request.GetInputFocus(display=self.display.display) if f.focus in [X.NONE, X.PointerRoot]: return None if toplevel: w = self.get_client_window(f.focus) if w is not None: return w.id return f.focus.id def raise_window(self, window): if isinstance(window, int): window = self.get_window(window) elif window is None: return window.raise_window() def focus_error(self, *args, **kwargs): print("Cannot set_input_focus: %r %r" % (args, kwargs)) def set_focused_window(self, window, wait=3): if window is None: self.display.set_input_focus(X.NONE, X.RevertToParent, X.CurrentTime, onerror=self.focus_error) return True elif not wait: self.display.set_input_focus(window, X.RevertToParent, X.CurrentTime) return True else: with self.silenced_error(error.BadMatch): if isinstance(window, int): window = self.get_window(window) self.display.set_input_focus(window, X.RevertToParent, X.CurrentTime) self.flush() w = Waiter(wait) while w.wait(): if w.timeout: if w.progressed: print("WAITING %.3f seconds more for focus on %r" % (w.remaining, window.id)) else: print( "READY TO WAIT %.3f seconds for focus on %r" % (w.remaining, window.id)) focused_win_id = self.get_focused_window() if focused_win_id == window.id: print("FOCUSED %r" % window.id) return True # many times it's needed to repeat the command, esp. when mouse is # not inside the target window self.display.set_input_focus(window, X.RevertToParent, X.CurrentTime) self.flush() #self.display.set_input_focus(window, X.RevertToParent, X.CurrentTime) #self.display.flush() return False def get_desktop_for_window(self, window): pv = self.get_prop(window, "_NET_WM_DESKTOP") if pv: return pv[0] def set_desktop_for_window(self, window, desktop): if desktop is None: return name = self.atom("_NET_WM_DESKTOP", only_if_exists=True) if name in self.supported_properties: pv = self.set_prop(window, name, Xatom.CARDINAL, array('I', [desktop])) def save_state(self): state = { "Current Desktop": self.current_desktop(), "Active Window": self.active_window(id_only=True), "Focused Window": self.get_focused_window() } return state def restore_state(self, state): a = self.supported_properties self.current_desktop(state["Current Desktop"]) self.flush() try: self.set_focused_window(state["Focused Window"]) except error.BadWindow: print("Sorry, the old focused window went away...") # self.active_window(state["Active Window"]) def keysym_to_string(self, keysym, friendly=False, very_friendly=False): if keysym not in self.keysyms.keysyms: return chr(keysym) if very_friendly: return self.keysyms.friendly_name(keysym, simplest=True) if friendly: return self.keysyms.friendly_name(keysym, simplest=False) else: return self.keysyms[keysym] def keycode_to_keysym(self, keycode, idx=None): if idx is None: syms = set() for i in range(4): keysym = self.display.keycode_to_keysym(keycode, i) if keysym: syms.add(keysym) return syms else: return self.display.keycode_to_keysym(event.detail, i) def keysym_to_keycode(self, keysym): return self.display.keysym_to_keycode(keysym) def string_to_keysym(self, s): k = self.keysyms[s] if not k: k = self.keysyms["XK_" + s] if k: return k k = XK.string_to_keysym(s) return k # allow simpler names, like AudioRaiseVolume? # if s.startswith("XF86_"): # s = "XF86" + s[5:] # return XK.string_to_keysym(s) def error_handler(self, fn, *args, **kwargs): return functools.partial(fn, *args, **kwargs) def toggle_frame(self, window, frame=None, wait=1): """Set window frame. Value should be True or False for on and off, or None for toggle.""" # flags - set bit for every iteresting value # 0 functions => integer bits # 1 decorations => integer bits # 2 input_mode => enum string or integer # 3 status => integer bits # # functions: # bit actions offered # --- --------------- # 1 all functions # 2 resize window # 4 move window # 8 minimize, to iconify # 16 maximize, to full-screen (with a frame still) # 32 close window # # decorations: # bit decorations displayed # --- --------------------- # 1 all decorations # 2 border around the window # 4 resizeh, handles to resize by dragging # 8 title bar, showing WM_NAME # 16 menu, drop-down menu of the "functions" above # 32 minimize button, to iconify # 64 maximize button, to full-screen # # input mode: # string integer # "modeless" 0 not modal (the default) # "primary_application_modal" 1 modal to its "transient for" # "system_modal" 2 modal to the whole display # "full_application_modal" 3 modal to the current client # # status: # # bit # 1 tearoff menu window name = self.atom("_MOTIF_WM_HINTS", only_if_exists=True) # If does not exist, probably not supported, though should check # root for _NET_SUPPORTED list return assert prop != 0 pv = pv = self.get_prop(window, name) fe = self.get_frame_extents(window) if pv and len(pv) == 5: hints = array(pv.typecode, pv) if frame is None: hints[2] = 0 if hints[2] else 1 elif frame: hints[2] = 1 else: hints[2] = 0 else: # reasonable default hints = array('I', [2, 0, 0, 0, 0]) self.set_prop(window, name, name, hints) w = Waiter(wait) while w.wait(): pv = self.get_prop(window, name) if pv and array(pv.typecode, pv) == hints: new_fe = self.get_frame_extents(window) # make sure frame extents changed # this seems to take a while once the hints change if new_fe != fe: break def set_opacity(self, window, value): """value is a number between 0 and 1""" v = int(((1 << 32) - 1) * value) self.set_prop(window, "_NET_WM_WINDOW_OPACITY", Xatom.CARDINAL, array('I', [v])) def get_opacity(self, window): pv = self.get_prop(window, "_NET_WM_WINDOW_OPACITY") if pv: value = int(pv[0] / ((1 << 32) - 1)) return value return 1 @property def supported_properties(self): if self._supported_properties is None: self._supported_properties = self.get_prop(self.root, "_NET_SUPPORTED") or [] return self._supported_properties def get_window(self, win_id): if isinstance(win_id, int): return self.display.create_resource_object('window', win_id) else: return win_id def get_client_window(self, window): win_id = window.id for tlw in self.toplevel_windows(): for (_, parent, _) in self.window_tree( tlw, filter=lambda w, parent, level: w.id == win_id): return tlw return None def toplevel_windows(self, id_only=False): name = self.atom("_NET_CLIENT_LIST", only_if_exists=True) if name in self.supported_properties: lst = self.get_prop(self.root, name) if id_only: return lst else: return list(map(lambda win_id: self.get_window(win_id), lst)) else: print("BELGENGOC") if id_only: return list( map(lambda w: w.id, self.root.query_tree().children)) else: return list(self.root.query_tree().children) def window_tree(self, parent=None, level=1, filter=None): if parent is None: parent = self.root if filter is None or filter(parent, None, 0): yield (parent, None, 0) for w in parent.query_tree().children: if filter is None or filter(w, parent, level): yield (w, parent, level) yield from self.window_tree(parent=w, level=level + 1, filter=filter) def close_window(self, window): self.send_prop_change_event("_NET_CLOSE_WINDOW", (32, [0, 0, 0, 0, 0]), window=self.get_window(window)) # https://specifications.freedesktop.org/wm-spec/wm-spec-1.3.html # window = the respective client window # message_type = _NET_WM_STATE # format = 32 # data.l[0] = the action, as listed below # data.l[1] = first property to alter # data.l[2] = second property to alter # data.l[3] = source indication # other data.l[] elements = 0 # This message allows two prop # _NET_WM_STATE_REMOVE = 0 # remove/unset property _NET_WM_STATE_ADD = 1 #add/set property _NET_WM_STATE_TOGGLE = 2 # toggle property def set_wm_states(self, window, names, action=None): if action is None: action = self._NET_WM_STATE_TOGGLE elif action is True: action = self._NET_WM_STATE_ADD elif action is False: action = self._NET_WM_STATE_REMOVE window = self.get_window(window) values = list() for name in names: value = self.atom("_NET_WM_STATE_%s" % name.upper()) values.append(value) data = [action, *values] while len(data) < 5: data.append(0) self.send_prop_change_event("_NET_WM_STATE", (32, data), window=self.get_window(window)) def set_wm_state(self, window, name, action=None): if action is None: action = self._NET_WM_STATE_TOGGLE elif action is True: action = self._NET_WM_STATE_ADD elif action is False: action = self._NET_WM_STATE_REMOVE window = self.get_window(window) value = self.atom("_NET_WM_STATE_%s" % name.upper()) self.send_prop_change_event("_NET_WM_STATE", (32, [action, value, 0, 0, 0]), window=self.get_window(window)) def below_window(self, window, action=None): self.set_wm_state(window, name="below", action=action) def fullscreen_window(self, window, action=None): self.set_wm_state(window, name="fullscreen", action=action) def above_window(self, window, action=None): self.set_wm_state(window, name="above", action=action) def sticky_window(self, window, action=None): self.set_wm_state(window, name="sticky", action=action) def skip_pager(self, window, action=None): self.set_wm_state(window, name="skip_pager", action=action) def skip_taskbar(self, window, action=None): self.set_wm_state(window, name="skip_taskbar", action=action) def maximize_window(self, window, horizontal=True, vertical=True, action=None): if horizontal: self.set_wm_state(window, name="maximized_horz", action=action) if vertical: self.set_wm_state(window, name="maximized_vert", action=action) def minimize_window(self, window): if isinstance(window, int): window = self.get_window(window) self.send_prop_change_event("WM_CHANGE_STATE", (32, [Xutil.IconicState, 0, 0, 0, 0]), window=self.get_window(window)) def get_attributes(self, window): if isinstance(window, int): window = self.get_window(window) return window.get_attributes() def get_window_type(self, window): e = self.get_prop(window, "_NET_WM_WINDOW_TYPE") if e is None: return None type_details = set() prefix = "_NET_WM_WINDOW_TYPE_" for t in e: if not t: continue s = self.atom_name(t) if s.startswith(prefix): s = s[len(prefix):] type_details.add(s) return type_details def get_frame_extents(self, window): # x, y, width, height if isinstance(window, int): window = self.get_window(window) e = self.get_prop(window, "_NET_FRAME_EXTENTS") if e: return self.FrameExtents(*e) else: return self.FrameExtents(0, 0, 0, 0) def get_geometry(self, window): # x, y, width, height if isinstance(window, int): window = self.get_window(window) return window.get_geometry() def set_geometry(self, window, **data): # x, y, width, height if isinstance(window, int): window = self.get_window(window) if any(map(lambda v: v < 0, data.values())): gw = self.get_geometry(window) f = self.get_frame_extents(window) wa = self.usable_workarea() if 'x' in data and data['x'] < 0: data['x'] = wa.width - gw.width - (f.left + f.right) + data['x'] + 1 else: data['x'] += wa.x if 'y' in data and data['y'] < 0: data['y'] = wa.height - gw.height - (f.top + f.bottom) + data['y'] + 1 else: data['y'] += wa.y window.configure(**data) def usable_workarea(self): a = self.get_prop(self.root, "_NET_WORKAREA") if a: p = self.current_desktop() * 4 #return (x, y, width, height) return self.Geometry(*a[p:p + 4]) else: r = self.get_geometry(self.root) return self.Geometry(0, 0, r.width, r.height) def send_key(self, window, keysym, modifiers): if isinstance(window, int): window = self.get_window(window) keycode = self.display.keysym_to_keycode(keysym) event = protocol.event.KeyPress(time=X.CurrentTime, root=self.root, window=window, child=X.NONE, same_screen=True, root_x=0, root_y=0, event_x=0, event_y=0, state=modifiers.bitmap, detail=keycode) window.send_event(event, propagate=False) event = protocol.event.KeyRelease( time=X.CurrentTime, root=self.root, window=window, child=X.NONE, same_screen=True, # same screen as the root window root_x=0, root_y=0, event_x=0, event_y=0, state=modifiers.bitmap, detail=keycode) window.send_event(event, propagate=False) def show_desktop(self, action=None): prop_name = self.atom("_NET_SHOWING_DESKTOP") if action is True: self.send_prop_change_event(prop_name, (32, [1, X.CurrentTime, 0, 0, 0])) elif action is False: self.send_prop_change_event(prop_name, (32, [0, X.CurrentTime, 0, 0, 0])) else: pv = self.get_prop(self.root, prop_name) new_val = 0 if pv and pv[0] else 1 self.send_prop_change_event( prop_name, (32, [new_val, X.CurrentTime, 0, 0, 0])) def flush(self): # send all pending events self.display.flush() def sync(self): # flush and make sure everything is handled and processed or rejected by the server self.display.sync() @property def display_count(self): res = randr.get_screen_resources(self.root) n = 0 for i in res.outputs: o = randr.get_output_info(self.root, i, config_timestamp=0) if o.modes: # has modes, empty if there's no monitor connected here n += 1 return n
class Manager(): def __init__(self, inkscape_id): self.id = inkscape_id self.disp = Display() self.screen = self.disp.screen() self.root = self.screen.root self.inkscape = self.disp.create_resource_object('window', inkscape_id) self.mode = normal_mode def event(self, name, detail, state): return name(time=X.CurrentTime, root=self.root, window=self.inkscape, same_screen=0, child=Xlib.X.NONE, root_x=0, root_y=0, event_x=0, event_y=0, state=state, detail=detail) def string_to_keycode(self, key): keysym = XK.string_to_keysym(key) keycode = self.disp.keysym_to_keycode(keysym) return keycode def press(self, key, mask=X.NONE): keycode = self.string_to_keycode(key) self.inkscape.send_event(self.event(event.KeyPress, keycode, mask), propagate=True) self.inkscape.send_event(self.event(event.KeyRelease, keycode, mask), propagate=True) self.disp.flush() self.disp.sync() def grab(self): self.inkscape.grab_key(X.AnyKey, X.AnyModifier, True, X.GrabModeAsync, X.GrabModeAsync) # Ungrab window manager shortcuts (Super + ...) self.inkscape.ungrab_key(self.string_to_keycode('Super'), X.AnyModifier, True) self.inkscape.ungrab_key(self.string_to_keycode('Alt_L'), X.AnyModifier, True) self.inkscape.ungrab_key(self.string_to_keycode('Shift_R'), X.AnyModifier, True) self.inkscape.change_attributes(event_mask=X.KeyReleaseMask | X.KeyPressMask | X.StructureNotifyMask) def ungrab(self): self.inkscape.ungrab_key(X.AnyKey, X.AnyModifier, True) def listen(self): self.grab() while True: evt = self.disp.next_event() if evt.type in [X.KeyPress, X.KeyRelease]: keycode = evt.detail keysym = self.disp.keycode_to_keysym(keycode, 0) char = XK.keysym_to_string(keysym) self.disp.allow_events(X.ReplayKeyboard, X.CurrentTime) self.mode(self, evt, char) if evt.type == X.DestroyNotify: if evt.window.id == self.id: self.ungrab() return
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 PyKeyboardEvent(PyKeyboardEventMeta): """ The PyKeyboardEvent implementation for X11 systems (mostly linux). This allows one to listen for keyboard input. """ def __init__(self, display=None): self.display = Display(display) self.display2 = Display(display) self.ctx = self.display2.record_create_context( 0, [record.AllClients ], [{ 'core_requests': (0, 0), 'core_replies': (0, 0), 'ext_requests': (0, 0, 0, 0), 'ext_replies': (0, 0, 0, 0), 'delivered_events': (0, 0), 'device_events': (X.KeyPress, X.KeyRelease), 'errors': (0, 0), 'client_started': False, 'client_died': False, }]) self.lock_meaning = None #Get these dictionaries for converting keysyms and strings self.keysym_to_string, self.string_to_keysym = self.get_translation_dicts( ) #Identify and register special groups of keys self.modifier_keycodes = {} self.all_mod_keycodes = [] self.keypad_keycodes = [] #self.configure_keys() #Direct access to the display's keycode-to-keysym array #print('Keycode to Keysym map') #for i in range(len(self.display._keymap_codes)): # print('{0}: {1}'.format(i, self.display._keymap_codes[i])) PyKeyboardEventMeta.__init__(self) def run(self): """Begin listening for keyboard input events.""" self.state = True if self.capture: self.display2.screen().root.grab_keyboard( True, X.KeyPressMask | X.KeyReleaseMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime) self.display2.record_enable_context(self.ctx, self.handler) self.display2.record_free_context(self.ctx) def stop(self): """Stop listening for keyboard input events.""" self.state = False self.display.record_disable_context(self.ctx) self.display.ungrab_keyboard(X.CurrentTime) self.display.flush() self.display2.record_disable_context(self.ctx) self.display2.ungrab_keyboard(X.CurrentTime) self.display2.flush() def handler(self, reply): """Upper level handler of keyboard events.""" data = reply.data while len(data): event, data = rq.EventField(None).parse_binary_value( data, self.display.display, None, None) if self.escape(event): # Quit if this returns True self.stop() else: self._tap(event) def _tap(self, event): keycode = event.detail press_bool = (event.type == X.KeyPress) #Detect modifier states from event.state for mod, bit in self.modifier_bits.items(): self.modifiers[mod] = event.state & bit if keycode in self.all_mod_keycodes: keysym = self.display.keycode_to_keysym(keycode, 0) character = self.keysym_to_string[keysym] else: character = self.lookup_char_from_keycode(keycode) #All key events get passed to self.tap() self.tap(keycode, character, press=press_bool) def lookup_char_from_keycode(self, keycode): """ This will conduct a lookup of the character or string associated with a given keycode. """ #TODO: Logic should be strictly adapted from X11's src/KeyBind.c #Right now the logic is based off of #http://tronche.com/gui/x/xlib/input/keyboard-encoding.html #Which I suspect is not the whole story and may likely cause bugs keysym_index = 0 #TODO: Display's Keysyms per keycode count? Do I need this? #If the Num_Lock is on, and the keycode corresponds to the keypad if self.modifiers['Num_Lock'] and keycode in self.keypad_keycodes: if self.modifiers['Shift'] or self.modifiers['Shift_Lock']: keysym_index = 0 else: keysym_index = 1 elif not self.modifiers['Shift'] and self.modifiers['Caps_Lock']: #Use the first keysym if uppercase or uncased #Use the uppercase keysym if the first is lowercase (second) keysym_index = 0 keysym = self.display.keycode_to_keysym(keycode, keysym_index) #TODO: Support Unicode, Greek, and special latin characters if keysym & 0x7f == keysym and chr( keysym) in 'abcdefghijklmnopqrstuvwxyz': keysym_index = 1 elif self.modifiers['Shift'] and self.modifiers['Caps_Lock']: keysym_index = 1 keysym = self.display.keycode_to_keysym(keycode, keysym_index) #TODO: Support Unicode, Greek, and special latin characters if keysym & 0x7f == keysym and chr( keysym) in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ': keysym_index = 0 elif self.modifiers['Shift'] or self.modifiers['Shift_Lock']: keysym_index = 1 if self.modifiers['Mode_switch']: keysym_index += 2 #Finally! Get the keysym keysym = self.display.keycode_to_keysym(keycode, keysym_index) #If the character is ascii printable, return that character if keysym & 0x7f == keysym and self.ascii_printable(keysym): return chr(keysym) #If the character was not printable, look for its name try: char = self.keysym_to_string[keysym] except KeyError: print('Unable to determine character.') print('Keycode: {0} KeySym {1}'.format(keycode, keysym)) return None else: return char def escape(self, event): if event.detail == self.lookup_character_keycode('Escape'): return True return False def configure_keys(self): """ This function locates the keycodes corresponding to special groups of keys and creates data structures of them for use by the PyKeyboardEvent instance; including the keypad keys and the modifiers. The keycodes pertaining to the keyboard modifiers are assigned by the modifier name in a dictionary. This dictionary can be accessed in the following manner: self.modifier_keycodes['Shift'] # All keycodes for Shift Masking It also assigns certain named modifiers (Alt, Num_Lock, Super), which may be dynamically assigned to Mod1 - Mod5 on different platforms. This should generally allow the user to do the following lookups on any system: self.modifier_keycodes['Alt'] # All keycodes for Alt Masking self.modifiers['Alt'] # State of Alt mask, non-zero if "ON" """ modifier_mapping = self.display.get_modifier_mapping() all_mod_keycodes = [] mod_keycodes = {} mod_index = [('Shift', X.ShiftMapIndex), ('Lock', X.LockMapIndex), ('Control', X.ControlMapIndex), ('Mod1', X.Mod1MapIndex), ('Mod2', X.Mod2MapIndex), ('Mod3', X.Mod3MapIndex), ('Mod4', X.Mod4MapIndex), ('Mod5', X.Mod5MapIndex)] #This gets the list of all keycodes per Modifier, assigns to name for name, index in mod_index: codes = [v for v in list(modifier_mapping[index]) if v] mod_keycodes[name] = codes all_mod_keycodes += codes def lookup_keycode(string): keysym = self.string_to_keysym[string] return self.display.keysym_to_keycode(keysym) #Dynamically assign Lock to Caps_Lock, Shift_Lock, Alt, Num_Lock, Super, #and mode switch. Set in both mod_keycodes and self.modifier_bits #Try to assign Lock to Caps_Lock or Shift_Lock shift_lock_keycode = lookup_keycode('Shift_Lock') caps_lock_keycode = lookup_keycode('Caps_Lock') if shift_lock_keycode in mod_keycodes['Lock']: mod_keycodes['Shift_Lock'] = [shift_lock_keycode] self.modifier_bits['Shift_Lock'] = self.modifier_bits['Lock'] self.lock_meaning = 'Shift_Lock' elif caps_lock_keycode in mod_keycodes['Lock']: mod_keycodes['Caps_Lock'] = [caps_lock_keycode] self.modifier_bits['Caps_Lock'] = self.modifier_bits['Lock'] self.lock_meaning = 'Caps_Lock' else: self.lock_meaning = None #print('Lock is bound to {0}'.format(self.lock_meaning)) #Need to find out which Mod# to use for Alt, Num_Lock, Super, and #Mode_switch num_lock_keycodes = [lookup_keycode('Num_Lock')] alt_keycodes = [lookup_keycode(i) for i in ['Alt_L', 'Alt_R']] super_keycodes = [lookup_keycode(i) for i in ['Super_L', 'Super_R']] mode_switch_keycodes = [lookup_keycode('Mode_switch')] #Detect Mod number for Alt, Num_Lock, and Super for name, keycodes in list(mod_keycodes.items()): for alt_key in alt_keycodes: if alt_key in keycodes: mod_keycodes['Alt'] = keycodes self.modifier_bits['Alt'] = self.modifier_bits[name] for num_lock_key in num_lock_keycodes: if num_lock_key in keycodes: mod_keycodes['Num_Lock'] = keycodes self.modifier_bits['Num_Lock'] = self.modifier_bits[name] for super_key in super_keycodes: if super_key in keycodes: mod_keycodes['Super'] = keycodes self.modifier_bits['Super'] = self.modifier_bits[name] for mode_switch_key in mode_switch_keycodes: if mode_switch_key in keycodes: mod_keycodes['Mode_switch'] = keycodes self.modifier_bits['Mode_switch'] = self.modifier_bits[ name] #Assign the mod_keycodes to a local variable for access self.modifier_keycodes = mod_keycodes self.all_mod_keycodes = all_mod_keycodes #TODO: Determine if this might fail, perhaps iterate through the mapping #and identify all keycodes with registered keypad keysyms? #Acquire the full list of keypad keycodes self.keypad_keycodes = [] keypad = [ 'Space', 'Tab', 'Enter', 'F1', 'F2', 'F3', 'F4', 'Home', 'Left', 'Up', 'Right', 'Down', 'Prior', 'Page_Up', 'Next', 'Page_Down', 'End', 'Begin', 'Insert', 'Delete', 'Equal', 'Multiply', 'Add', 'Separator', 'Subtract', 'Decimal', 'Divide', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ] for keyname in keypad: keypad_keycode = self.lookup_character_keycode('KP_' + keyname) self.keypad_keycodes.append(keypad_keycode) def lookup_character_keycode(self, character): """ Looks up the keysym for the character then returns the keycode mapping for that keysym. """ keysym = self.string_to_keysym.get(character, 0) if keysym == 0: keysym = self.string_to_keysym.get(special_X_keysyms[character], 0) return self.display.keysym_to_keycode(keysym) def get_translation_dicts(self): """ Returns dictionaries for the translation of keysyms to strings and from strings to keysyms. """ keysym_to_string_dict = {} string_to_keysym_dict = {} #XK loads latin1 and miscellany on its own; load latin2-4 and greek Xlib.XK.load_keysym_group('latin2') Xlib.XK.load_keysym_group('latin3') Xlib.XK.load_keysym_group('latin4') Xlib.XK.load_keysym_group('greek') #Make a standard dict and the inverted dict for string, keysym in Xlib.XK.__dict__.items(): if string.startswith('XK_'): string_to_keysym_dict[string[3:]] = keysym keysym_to_string_dict[keysym] = string[3:] return keysym_to_string_dict, string_to_keysym_dict def ascii_printable(self, keysym): """ If the keysym corresponds to a non-printable ascii character this will return False. If it is printable, then True will be returned. ascii 11 (vertical tab) and ascii 12 are printable, chr(11) and chr(12) will return '\x0b' and '\x0c' respectively. """ if 0 <= keysym < 9: return False elif 13 < keysym < 32: return False elif keysym > 126: return False else: return True
class PyMouseEvent(PyMouseEventMeta): def __init__(self, capture=False, capture_move=False, display=None): PyMouseEventMeta.__init__(self, capture=capture, capture_move=capture_move) self.display = Display(display) self.display2 = Display(display) self.ctx = self.display2.record_create_context( 0, [record.AllClients], [{ 'core_requests': (0, 0), 'core_replies': (0, 0), 'ext_requests': (0, 0, 0, 0), 'ext_replies': (0, 0, 0, 0), 'delivered_events': (0, 0), 'device_events': (X.ButtonPressMask, X.ButtonReleaseMask), 'errors': (0, 0), 'client_started': False, 'client_died': False, }]) def run(self): try: if self.capture and self.capture_move: capturing = X.ButtonPressMask | X.ButtonReleaseMask | X.PointerMotionMask elif self.capture: capturing = X.ButtonPressMask | X.ButtonReleaseMask elif self.capture_move: capturing = X.PointerMotionMask else: capturing = False if capturing: self.display2.screen().root.grab_pointer(True, capturing, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime) self.display.screen().root.grab_pointer(True, capturing, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime) self.display2.record_enable_context(self.ctx, self.handler) self.display2.record_free_context(self.ctx) except KeyboardInterrupt: self.stop() def stop(self): self.display.flush() self.display.record_disable_context(self.ctx) self.display.ungrab_pointer(X.CurrentTime) self.display2.flush() self.display2.record_disable_context(self.ctx) self.display2.ungrab_pointer(X.CurrentTime) def handler(self, reply): data = reply.data while len(data): event, data = rq.EventField(None).parse_binary_value(data, self.display.display, None, None) #In X11, the button numbers are: leftclick=1, middleclick=2, # rightclick=3, scrollup=4, scrolldown=5 # For the purposes of the cross-platform interface of PyMouse, we # invert the button number values of the right and middle buttons if event.type == X.ButtonPress: self.click(event.root_x, event.root_y, (None, 1, 3, 2, 4, 5, 3)[event.detail], True) elif event.type == X.ButtonRelease: self.click(event.root_x, event.root_y, (None, 1, 3, 2, 4, 5, 3)[event.detail], False) else: self.move(event.root_x, event.root_y)
class KeyPointer: def __init__(self): self.display = Display() self.screen = self.display.screen() self.root = self.screen.root self.keymap = gtk.gdk.keymap_get_default() self.finish_keyval = gtk.keysyms.Return self.finish_keycode = self.keymap.get_entries_for_keyval(self.finish_keyval)[0][0] self.setup_movementkeys(DEFAULT_MVMT) self.setup_keymapping(DEFAULT_MAP) self.init_gconf(GCONF_DIR) def init_gconf(self, app_dir): self.gconf = gconf.client_get_default() self.gconf.add_dir(app_dir, gconf.CLIENT_PRELOAD_NONE) self.gconf.notify_add(LAYOUT_KEY, self.gconf_cb) self.gconf.notify_add(MOVEMENT_KEY, self.gconf_cb) self.read_gconf(app_dir) def read_gconf(self, app_dir): gconf_keymappings = self.gconf.get_list(LAYOUT_KEY, gconf.VALUE_STRING) keymappings = [] for line in gconf_keymappings: keymappings.append(line.strip()) self.setup_keymapping(keymappings) def gconf_cb(self, *args): # One of our settings changed, probably should re-read gconf data self.read_gconf(GCONF_DIR) def launch_cb(self, keybinding): print "Grabbing Keyboard Focus" self.root.grab_keyboard(True, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime) print "Placing pointer" self.screen_handler() try: self.display.ungrab_keyboard(X.CurrentTime) self.display.flush() print "Finished placing pointer" except: gtk.main_quit() def setup_movementkeys(self, mapping_dict): self.movement_dict = mapping_dict self.movement_keycodes = {} for key in mapping_dict: keyval = gtk.gdk.unicode_to_keyval(ord(key)) keycode = self.keymap.get_entries_for_keyval(keyval)[0][0] self.movement_keycodes[keycode] = mapping_dict[key] def setup_keymapping(self, mapping_array): # Map the keys to an x,y pair of where the key falls on the keyboard self.keyboard_keyvals = {} self.keymapping_array = mapping_array self.max_height = len(mapping_array) self.max_width = max([len(x) for x in mapping_array]) for y in xrange(len(mapping_array)): for x in xrange(len(mapping_array[y])): keyval = gtk.gdk.unicode_to_keyval(ord(mapping_array[y][x])) self.keyboard_keyvals[keyval] = (x, y) def keypress_cb(self, e): # Find the coordinates of the key pressed try: # Check if this is a keypress event (not a release or button, etc) if e.__class__ is not Xlib.protocol.event.KeyPress: return keycode = e.detail state = e.state w = self.screen.width_in_pixels h = self.screen.height_in_pixels # Find out if there are any modifiers being pressed # If ctrl + movement key is being pressed, move over by some amount if state & X.ControlMask and keycode in self.movement_keycodes: print "Control Hold Movement Keys" movement = self.movement_keycodes[keycode] # move the cursor a little # Not sure how to calculate the amount to move by? # Maybe take the smallest h_block, w_block we have and divide into thirds? h_block = float(h) / self.max_height / 3 w_block = float(w) / self.max_width / 3 cursor_position = self.root.query_pointer() x = cursor_position.root_x y = cursor_position.root_y to_x, to_y = x, y if movement == "left": to_x = x - w_block if movement == "down": to_y = y + h_block if movement == "up": to_y = y - w_block if movement == "right": to_x = x + w_block else: keyval_tuple = self.keymap.translate_keyboard_state(e.detail, e.state, e.type) keyval, group, level, modifiers = keyval_tuple x, y = self.keyboard_keyvals[keyval] h_block = float(h) / len(self.keymapping_array) w_block = float(w) / len(self.keymapping_array[y]) # divide the width by the number of rows we have and multiply by x to # figure out where the cursor goes to_x = w_block * x + (w_block / 2) to_y = h_block * y + (h_block / 2) print to_x, to_y self.root.warp_pointer(to_x, to_y) except KeyError, v: pass return e.detail == self.finish_keycode
class BreakScreen: """ The fullscreen window which prevents users from using the computer. This class reads the break_screen.glade and build the user interface. """ def __init__(self, context, on_skip, on_postpone, glade_file, style_sheet_path): self.context = context self.on_skip = on_skip self.on_postpone = on_postpone self.is_pretified = False self.windows = [] self.count_labels = [] self.glade_file = glade_file self.enable_shortcut = False self.display = Display() # Initialize the theme css_provider = Gtk.CssProvider() css_provider.load_from_path(style_sheet_path) Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) def initialize(self, config, language): """ Initialize the internal properties from configuration """ logging.info("Initialize the break screen") self.skip_button_text = language['ui_controls']['skip'] self.postpone_button_text = language['ui_controls']['postpone'] self.strict_break = config.get('strict_break', False) self.enable_postpone = config.get('allow_postpone', False) self.keycode_shortcut_skip = config.get('shortcut_skip', 9) self.keycode_shortcut_postpone = config.get('shortcut_postpone', 65) self.shortcut_disable_time = config.get('shortcut_disable_time', 2) def skip_break(self): """ Skip the break from the break screen """ logging.info("User skipped the break") # Must call on_skip before close to lock screen before closing the break screen self.on_skip() self.close() def postpone_break(self): """ Postpone the break from the break screen """ logging.info("User postponed the break") self.on_postpone() self.close() def on_window_delete(self, *args): """ Window close event handler. """ logging.info("Closing the break screen") self.__release_keyboard() self.close() def on_skip_clicked(self, button): """ Skip button press event handler. """ self.skip_break() def on_postpone_clicked(self, button): """ Postpone button press event handler. """ self.postpone_break() def show_count_down(self, count_down, seconds): """ Show/update the count down on all screens. """ self.enable_shortcut = not self.strict_break and self.shortcut_disable_time <= count_down mins, secs = divmod(seconds, 60) timeformat = '{:02d}:{:02d}'.format(mins, secs) GLib.idle_add(lambda: self.__update_count_down(timeformat)) def show_message(self, message, image_path, plugins_data): """ Show the break screen with the given message on all displays. """ self.enable_shortcut = not self.strict_break and self.shortcut_disable_time <= 0 GLib.idle_add(lambda: self.__show_break_screen(message, image_path, plugins_data)) def close(self): """ Hide the break screen from active window and destroy all other windows """ logging.info("Close the break screen(s)") self.__release_keyboard() # Destroy other windows if exists GLib.idle_add(lambda: self.__destroy_all_screens()) def __show_break_screen(self, message, image_path, plugins_data): """ Show an empty break screen on all screens. """ # Lock the keyboard thread = threading.Thread(target=self.__lock_keyboard) thread.start() logging.info("Show break screens in all displays") screen = Gtk.Window().get_screen() no_of_monitors = screen.get_n_monitors() for monitor in range(no_of_monitors): monitor_gemoetry = screen.get_monitor_geometry(monitor) x = monitor_gemoetry.x y = monitor_gemoetry.y builder = Gtk.Builder() builder.add_from_file(self.glade_file) builder.connect_signals(self) window = builder.get_object("window_main") lbl_message = builder.get_object("lbl_message") lbl_count = builder.get_object("lbl_count") lbl_left = builder.get_object("lbl_left") lbl_right = builder.get_object("lbl_right") img_break = builder.get_object("img_break") box_buttons = builder.get_object("box_buttons") # Add the buttons if not self.strict_break: # Add postpone button if self.enable_postpone: btn_postpone = Gtk.Button(self.postpone_button_text) btn_postpone.get_style_context().add_class('btn_postpone') btn_postpone.connect('clicked', self.on_postpone_clicked) btn_postpone.set_visible(True) box_buttons.pack_start(btn_postpone, True, True, 0) # Add the skip button btn_skip = Gtk.Button(self.skip_button_text) btn_skip.get_style_context().add_class('btn_skip') btn_skip.connect('clicked', self.on_skip_clicked) btn_skip.set_visible(True) box_buttons.pack_start(btn_skip, True, True, 0) # Set values if image_path: img_break.set_from_file(image_path) lbl_message.set_label(message) lbl_left.set_markup(plugins_data['left']) lbl_right.set_markup(plugins_data['right']) self.windows.append(window) self.count_labels.append(lbl_count) # Set visual to apply css theme. It should be called before show method. window.set_visual(window.get_screen().get_rgba_visual()) if self.context['desktop'] == 'kde': # Fix flickering screen in KDE by setting opacity to 1 window.set_opacity(0.9) window.move(x, y) window.stick() window.set_keep_above(True) window.present() window.fullscreen() def __update_count_down(self, count): """ Update the countdown on all break screens. """ for label in self.count_labels: label.set_text(count) def __lock_keyboard(self): """ Lock the keyboard to prevent the user from using keyboard shortcuts """ logging.info("Lock the keyboard") self.lock_keyboard = True # Grab the keyboard root = self.display.screen().root root.change_attributes(event_mask=X.KeyPressMask | X.KeyReleaseMask) root.grab_keyboard(True, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime) # Consume keyboard events while self.lock_keyboard: if self.display.pending_events() > 0: # Avoid waiting for next event by checking pending events event = self.display.next_event() if self.enable_shortcut and event.type == X.KeyPress: if event.detail == self.keycode_shortcut_skip: self.skip_break() break elif self.enable_postpone and event.detail == self.keycode_shortcut_postpone: self.postpone_break() break else: # Reduce the CPU usage by sleeping for a second time.sleep(1) def __release_keyboard(self): """ Release the locked keyboard. """ logging.info("Unlock the keyboard") self.lock_keyboard = False self.display.ungrab_keyboard(X.CurrentTime) self.display.flush() def __destroy_all_screens(self): """ Close all the break screens. """ for win in self.windows: win.destroy() del self.windows[:] del self.count_labels[:]
class GlobalKeyBinding(GObject.GObject, threading.Thread): __gsignals__ = { 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): GObject.GObject.__init__(self) threading.Thread.__init__(self) self.setDaemon(True) self.keymap = 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()