def record_context(dpy, ev_range, dev_range): range_spec = xlib.XRecordAllocRange() range_spec.contents.delivered_events.first = ev_range[0] range_spec.contents.delivered_events.last = ev_range[1] range_spec.contents.device_events.first = dev_range[0] range_spec.contents.device_events.last = dev_range[1] rec_ctx = xlib.XRecordCreateContext( dpy, 0, xlib.byref(xlib.c_ulong(xlib.XRecordAllClients)), 1, xlib.byref(range_spec), 1) xlib.XFree(range_spec) return rec_ctx
def create_replay_window(dpy): win_attr = xlib.XSetWindowAttributes() win_attr.override_redirect = True win = xlib.XCreateWindow(dpy, xlib.XDefaultRootWindow(dpy), 0, 0, 1, 1, 0, xlib.CopyFromParent, xlib.InputOnly, None, xlib.CWOverrideRedirect, xlib.byref(win_attr)) return win
def record_context(dpy, ev_ranges, dev_ranges): ev_ranges = coalesce_ranges(ev_ranges) dev_ranges = coalesce_ranges(dev_ranges) specs = max(len(ev_ranges), len(dev_ranges)) range_specs = (xlib.POINTER(xlib.XRecordRange) * specs)() for i in range(specs): range_specs[i] = xlib.XRecordAllocRange() if len(ev_ranges) > i: range_specs[i].contents.delivered_events.first = ev_ranges[i][0] range_specs[i].contents.delivered_events.last = ev_ranges[i][1] if len(dev_ranges) > i: range_specs[i].contents.device_events.first = dev_ranges[i][0] range_specs[i].contents.device_events.last = dev_ranges[i][1] rec_ctx = xlib.XRecordCreateContext( dpy, 0, xlib.byref(xlib.c_ulong(xlib.XRecordAllClients)), 1, range_specs, specs) for i in range(specs): xlib.XFree(range_specs[i]) return rec_ctx
def _event_keypress(self, kev, data): buf = xlib.create_string_buffer(16) keysym = xlib.KeySym() status = xlib.Status() ret = xlib.Xutf8LookupString(self._kbd_replay_xic, kev, buf, len(buf), xlib.byref(keysym), xlib.byref(status)) if ret != xlib.NoSymbol: if 32 <= keysym.value <= 126: # avoid ctrl sequences, just take the character value data.string = chr(keysym.value) else: try: data.string = buf.value.decode('utf-8') except UnicodeDecodeError: pass data.keysym = keysym.value data.status = status.value
def _event_keypress(self, kev, data): buf = xlib.create_string_buffer(16) keysym = xlib.KeySym() status = xlib.Status() ret = xlib.Xutf8LookupString(self.replay_xic, kev, buf, len(buf), xlib.byref(keysym), xlib.byref(status)) if ret != xlib.NoSymbol: if 32 <= keysym.value <= 126: # avoid ctrl sequences, just take the character value data.string = chr(keysym.value) else: try: data.string = buf.value.decode('utf-8') except UnicodeDecodeError: pass data.keysym = keysym.value data.status = status.value
def phantom_release(dpy, kev): if not xlib.XPending(dpy): return False ev = xlib.XEvent() xlib.XPeekEvent(dpy, xlib.byref(ev)) return (ev.type == xlib.KeyPress and \ ev.xkey.state == kev.state and \ ev.xkey.keycode == kev.keycode and \ ev.xkey.time == kev.time)
def run(self): # control connection self.control_dpy = xlib.XOpenDisplay(None) xlib.XSynchronize(self.control_dpy, True) # unmapped replay window self.replay_dpy = xlib.XOpenDisplay(None) self.custom_atom = xlib.XInternAtom(self.replay_dpy, b"SCREENKEY", False) replay_fd = xlib.XConnectionNumber(self.replay_dpy) self.replay_win = create_replay_window(self.replay_dpy) if self.input_types & InputType.keyboard: self._kbd_init() # initialize recording context ev_ranges = [] dev_ranges = [] if self.input_types & InputType.keyboard: ev_ranges.append([xlib.FocusIn, xlib.FocusOut]) dev_ranges.append([xlib.KeyPress, xlib.KeyRelease]) if self.input_types & InputType.button: dev_ranges.append([xlib.ButtonPress, xlib.ButtonRelease]) if self.input_types & InputType.movement: dev_ranges.append([xlib.MotionNotify, xlib.MotionNotify]) self.record_ctx = record_context(self.control_dpy, ev_ranges, dev_ranges) record_dpy = xlib.XOpenDisplay(None) record_fd = xlib.XConnectionNumber(record_dpy) # we need to keep the record_ref alive(!) record_ref = record_enable(record_dpy, self.record_ctx, self._event_received) # event loop self.lock.release() while True: with self.lock: if self.stopped: break r_fd = [] if xlib.XPending(record_dpy): r_fd.append(record_fd) if xlib.XPending(self.replay_dpy): r_fd.append(replay_fd) if not r_fd: r_fd, _, _ = select.select([record_fd, replay_fd], [], []) if not r_fd: break if record_fd in r_fd: xlib.XRecordProcessReplies(record_dpy) xlib.XFlush(self.replay_dpy) if replay_fd in r_fd: ev = xlib.XEvent() xlib.XNextEvent(self.replay_dpy, xlib.byref(ev)) if self.input_types & InputType.keyboard: self._kbd_process(ev) # finalize xlib.XRecordFreeContext(self.control_dpy, self.record_ctx) xlib.XCloseDisplay(self.control_dpy) xlib.XCloseDisplay(record_dpy) del record_ref if self.input_types & InputType.keyboard: self._kbd_del() xlib.XDestroyWindow(self.replay_dpy, self.replay_win) xlib.XCloseDisplay(self.replay_dpy)
def run(self): self.control_dpy = xlib.XOpenDisplay(None) xlib.XSynchronize(self.control_dpy, True) self.record_ctx = record_context(self.control_dpy, [xlib.FocusIn, xlib.FocusOut], [xlib.KeyPress, xlib.KeyRelease]) record_dpy = xlib.XOpenDisplay(None) record_fd = xlib.XConnectionNumber(record_dpy) # note that we never ever map the window self.replay_dpy = xlib.XOpenDisplay(None) self.custom_atom = xlib.XInternAtom(self.replay_dpy, "SCREENKEY", False) replay_fd = xlib.XConnectionNumber(self.replay_dpy) self.replay_win = create_replay_window(self.replay_dpy) if self.compose: style = xlib.XIMPreeditNothing | xlib.XIMStatusNothing else: style = xlib.XIMPreeditNone | xlib.XIMStatusNone # TODO: implement preedit callbacks for on-the-spot composition # (this would fix focus-stealing for the global IM state) replay_xim = xlib.XOpenIM(self.replay_dpy, None, None, None) if not replay_xim: raise Exception("Cannot initialize input method") self.replay_xic = xlib.XCreateIC(replay_xim, xlib.XNClientWindow, self.replay_win, xlib.XNInputStyle, style, None) xlib.XSetICFocus(self.replay_xic) # we need to keep the proc_ref alive proc_ref = record_enable(record_dpy, self.record_ctx, self._event_received) last_ev = xlib.XEvent() self.lock.release() while True: with self.lock: if self.stopped: break r_fd = [] if xlib.XPending(record_dpy): r_fd.append(record_fd) if xlib.XPending(self.replay_dpy): r_fd.append(replay_fd) if not r_fd: r_fd, _, _ = select.select([record_fd, replay_fd], [], []) if not r_fd: break if record_fd in r_fd: xlib.XRecordProcessReplies(record_dpy) xlib.XFlush(self.replay_dpy) if replay_fd in r_fd: ev = xlib.XEvent() xlib.XNextEvent(self.replay_dpy, xlib.byref(ev)) if ev.type == xlib.ClientMessage and \ ev.xclient.message_type == self.custom_atom: if ev.xclient.data[0] in [xlib.FocusIn, xlib.FocusOut]: # We do not keep track of multiple XICs, just reset xic = xlib.Xutf8ResetIC(self.replay_xic) if xic is not None: xlib.XFree(xic) continue elif ev.type in [xlib.KeyPress, xlib.KeyRelease]: ev.xkey.send_event = False ev.xkey.window = self.replay_win filtered = bool(xlib.XFilterEvent(ev, 0)) if ev.type not in [xlib.KeyPress, xlib.KeyRelease]: continue if ev.type == xlib.KeyRelease and \ phantom_release(self.replay_dpy, ev.xkey): continue data = KeyData() data.filtered = filtered data.pressed = (ev.type == xlib.KeyPress) data.repeated = (ev.type == last_ev.type and \ ev.xkey.state == last_ev.xkey.state and \ ev.xkey.keycode == last_ev.xkey.keycode) data.mods_mask = ev.xkey.state self._event_modifiers(ev.xkey, data) if not data.filtered and data.pressed and self.translate: self._event_keypress(ev.xkey, data) else: self._event_lookup(ev.xkey, data) self._event_processed(data) last_ev = ev xlib.XRecordFreeContext(self.control_dpy, self.record_ctx) xlib.XCloseDisplay(self.control_dpy) xlib.XCloseDisplay(record_dpy) del proc_ref xlib.XDestroyIC(self.replay_xic) xlib.XCloseIM(replay_xim) xlib.XDestroyWindow(self.replay_dpy, self.replay_win) xlib.XCloseDisplay(self.replay_dpy)
def run(self): self.control_dpy = xlib.XOpenDisplay(None) xlib.XSynchronize(self.control_dpy, True) self.record_ctx = record_context(self.control_dpy, [xlib.FocusIn, xlib.FocusOut], [xlib.KeyPress, xlib.KeyRelease]) record_dpy = xlib.XOpenDisplay(None) record_fd = xlib.XConnectionNumber(record_dpy) # note that we never ever map the window self.replay_dpy = xlib.XOpenDisplay(None) replay_fd = xlib.XConnectionNumber(self.replay_dpy) self.replay_win = create_replay_window(self.replay_dpy) if self.compose: style = xlib.XIMPreeditNothing | xlib.XIMStatusNothing else: style = xlib.XIMPreeditNone | xlib.XIMStatusNone # TODO: implement preedit callbacks for on-the-spot composition # (this would fix focus-stealing for the global IM state) replay_xim = xlib.XOpenIM(self.replay_dpy, None, None, None) self.replay_xic = xlib.XCreateIC(replay_xim, xlib.XNClientWindow, self.replay_win, xlib.XNInputStyle, style, None) xlib.XSetICFocus(self.replay_xic) # we need to keep the proc_ref alive proc_ref = record_enable(record_dpy, self.record_ctx, self._event_received) last_ev = xlib.XEvent() self.lock.release() while True: with self.lock: if self.stopped: break r_fd = [] if xlib.XPending(record_dpy): r_fd.append(record_fd) if xlib.XPending(self.replay_dpy): r_fd.append(replay_fd) if not r_fd: r_fd, _, _ = select.select([record_fd, replay_fd], [], []) if not r_fd: break if record_fd in r_fd: xlib.XRecordProcessReplies(record_dpy) xlib.XFlush(self.replay_dpy) if replay_fd in r_fd: ev = xlib.XEvent() xlib.XNextEvent(self.replay_dpy, xlib.byref(ev)) if ev.type in [xlib.KeyPress, xlib.KeyRelease]: ev.xkey.send_event = False ev.xkey.window = self.replay_win filtered = bool(xlib.XFilterEvent(ev, 0)) if ev.type not in [xlib.KeyPress, xlib.KeyRelease]: continue if ev.type == xlib.KeyRelease and \ phantom_release(self.replay_dpy, ev.xkey): continue data = KeyData() data.filtered = filtered data.pressed = (ev.type == xlib.KeyPress) data.repeated = (ev.type == last_ev.type and \ ev.xkey.state == last_ev.xkey.state and \ ev.xkey.keycode == last_ev.xkey.keycode) data.mods_mask = ev.xkey.state self._event_modifiers(ev.xkey, data) if not data.filtered and data.pressed and self.translate: self._event_keypress(ev.xkey, data) else: self._event_lookup(ev.xkey, data) self._event_processed(data) last_ev = ev xlib.XRecordFreeContext(self.control_dpy, self.record_ctx) xlib.XCloseDisplay(self.control_dpy) xlib.XCloseDisplay(record_dpy) del proc_ref xlib.XDestroyIC(self.replay_xic) xlib.XCloseIM(replay_xim) xlib.XDestroyWindow(self.replay_dpy, self.replay_win) xlib.XCloseDisplay(self.replay_dpy)
def run(self): # control connection self.control_dpy = xlib.XOpenDisplay(None) xlib.XSynchronize(self.control_dpy, True) # unmapped replay window self.replay_dpy = xlib.XOpenDisplay(None) self.custom_atom = xlib.XInternAtom(self.replay_dpy, b"SCREENKEY", False) replay_fd = xlib.XConnectionNumber(self.replay_dpy) self.replay_win = create_replay_window(self.replay_dpy) # bail during initialization errors try: if self.input_types & InputType.keyboard: self._kbd_init() except Exception as e: self.error = e xlib.XCloseDisplay(self.control_dpy) xlib.XDestroyWindow(self.replay_dpy, self.replay_win) xlib.XCloseDisplay(self.replay_dpy) # cheap wakeup() equivalent for compatibility glib.idle_add(self._event_callback, None) self._stop = True self.lock.release() return # initialize recording context ev_ranges = [] dev_ranges = [] if self.input_types & InputType.keyboard: ev_ranges.append([xlib.FocusIn, xlib.FocusOut]) dev_ranges.append([xlib.KeyPress, xlib.KeyRelease]) if self.input_types & InputType.button: dev_ranges.append([xlib.ButtonPress, xlib.ButtonRelease]) if self.input_types & InputType.movement: dev_ranges.append([xlib.MotionNotify, xlib.MotionNotify]) self.record_ctx = record_context(self.control_dpy, ev_ranges, dev_ranges); record_dpy = xlib.XOpenDisplay(None) record_fd = xlib.XConnectionNumber(record_dpy) # we need to keep the record_ref alive(!) record_ref = record_enable(record_dpy, self.record_ctx, self._event_received) # event loop self.lock.release() while True: with self.lock: if self._stop: break r_fd = [] if xlib.XPending(record_dpy): r_fd.append(record_fd) if xlib.XPending(self.replay_dpy): r_fd.append(replay_fd) if not r_fd: r_fd, _, _ = select.select([record_fd, replay_fd], [], []) if not r_fd: break if record_fd in r_fd: xlib.XRecordProcessReplies(record_dpy) xlib.XFlush(self.replay_dpy) if replay_fd in r_fd: ev = xlib.XEvent() xlib.XNextEvent(self.replay_dpy, xlib.byref(ev)) if self.input_types & InputType.keyboard: self._kbd_process(ev) # finalize self.lock.acquire() xlib.XRecordFreeContext(self.control_dpy, self.record_ctx) xlib.XCloseDisplay(self.control_dpy) xlib.XCloseDisplay(record_dpy) del record_ref if self.input_types & InputType.keyboard: self._kbd_del() xlib.XDestroyWindow(self.replay_dpy, self.replay_win) xlib.XCloseDisplay(self.replay_dpy) self._stop = True self.lock.release()