示例#1
0
def __patch_svg_data__(data):
    display = Display()
    screen = display.screen()
    atom = display.get_atom('_GNOME_BACKGROUND_REPRESENTATIVE_COLORS')
    result = screen.root.get_property(atom, Xatom.STRING, 0, 100)
    if result:
        color = result.value.strip('\x00')
        rgba = Gdk.RGBA()
        rgba.parse(color)
        color = rgba.to_color().to_string()
        data = data.replace('#000000', color)
    return data
示例#2
0
class ProgramIdentifier(object):
    def __init__(self):
        self.display = Display()
        self.pid_atom = self.display.get_atom("_NET_WM_PID")

    def get_active_program(self):
        pid = self._get_top_window_pid()
        realpath = self._get_pid_realpath(pid)
        return self._get_program_name_from_path(realpath)

    def _get_pid_realpath(self, pid):
        return os.path.realpath('/proc/' + str(pid) + '/exe')

    def _get_program_name_from_path(self, path):
        return path.split("/")[-1]

    def _get_top_window_pid(self):
        top_window = self._get_top_window()
        if top_window is not None:
            pid = self._get_window_pid(top_window)
            if pid is None:
                raise CantGetPIDOfWindowError("")
            return pid
        else:
            raise NoTopWindowFoundError("")

    def _get_top_window(self):
        focused_window = self.display.get_input_focus().focus
        if focused_window is not None and focused_window != X.NONE and focused_window != X.PointerRoot:
            parent = focused_window

            while (True):
                if self.pid_atom in parent.list_properties():
                    break
                query_result = parent.query_tree()
                root = query_result.root
                parent = query_result.parent

                if root.id == parent.id:
                    break

            return parent

        return None

    def _get_window_pid(self, window):
        if self.pid_atom in window.list_properties():
            value = window.get_full_property(self.pid_atom,
                                             X.AnyPropertyType).value
            if value is not None and len(value) == 1:
                return value[0]

        return None
示例#3
0
class XlibHelper:
    """Utility class for some X11-specific logic"""
    def __init__(self):
        self.display = Display()
        self.use_xres = self._try_init_xres()

    def _try_init_xres(self) -> bool:
        if XRes is None or self.display.query_extension(XRes.extname) is None:
            LOG.warning(
                "X-Resource extension is not supported. "
                "Process identification for X11 applications will be less reliable."
            )
            return False
        ver = self.display.res_query_version()
        LOG.info(
            "X-Resource version %d.%d",
            ver.server_major,
            ver.server_minor,
        )
        return (ver.server_major, ver.server_minor) >= (1, 2)

    def get_net_wm_pid(self, wid: int) -> int:
        """Get PID from _NET_WM_PID property of X11 window"""
        window = self.display.create_resource_object("window", wid)
        net_wm_pid = self.display.get_atom("_NET_WM_PID")
        pid = window.get_full_property(net_wm_pid, X.AnyPropertyType)

        if pid is None:
            raise Exception("Failed to get PID from _NET_WM_PID")
        return int(pid.value.tolist()[0])

    def get_xres_client_id(self, wid: int) -> int:
        """Get PID from X server via X-Resource extension"""
        res = self.display.res_query_client_ids([{
            "client":
            wid,
            "mask":
            XRes.LocalClientPIDMask
        }])
        for cid in res.ids:
            if cid.spec.client > 0 and cid.spec.mask == XRes.LocalClientPIDMask:
                for value in cid.value:
                    return value
        raise Exception("Failed to get PID via X-Resource extension")

    def get_window_pid(self, wid: int) -> Optional[int]:
        """Get PID of X11 window"""
        if self.use_xres:
            return self.get_xres_client_id(wid)

        return self.get_net_wm_pid(wid)
示例#4
0
def get_chameleonic_pixbuf_from_svg(filename):
    # Note: Experimental!!!!
    # TODO: Fix or Remove completely
    data = open(get_media_file(filename, '/'), 'r').read()
    display = Display()
    screen = display.screen()
    atom = display.get_atom('_GNOME_BACKGROUND_REPRESENTATIVE_COLORS')
    result = screen.root.get_property(atom, Xatom.STRING, 0, 100)
    if result:
        print result.value
        color = result.value.strip('\x00')
        data = data.replace('#000000', color)
    h = Rsvg.Handle.new_from_data(data)
    return h.get_pixbuf()
示例#5
0
def get_chameleonic_pixbuf_from_svg(filename):
    # Note: Experimental!!!!
    # TODO: Fix or Remove completely
    data = open(get_media_file(filename, '/'), 'r').read()
    display = Display()
    screen = display.screen()
    atom = display.get_atom('_GNOME_BACKGROUND_REPRESENTATIVE_COLORS')
    result = screen.root.get_property(atom, Xatom.STRING, 0, 100)
    if result:
        print result.value
        color = result.value.strip('\x00')
        data = data.replace('#000000', color)
    h = Rsvg.Handle.new_from_data(data)
    return h.get_pixbuf()
def main(argv):
    if len(sys.argv) != 2:
        sys.exit(
            'usage: {0} SELECTION\n\n'
            'SELECTION is typically PRIMARY, SECONDARY or CLIPBOARD.\n'.format(
                sys.argv[0]))

    display = Display()

    sel_name = sys.argv[1]
    sel_atom = display.get_atom(sel_name)

    if not display.has_extension('XFIXES'):
        if display.query_extension('XFIXES') is None:
            print('XFIXES extension not supported', file=sys.stderr)
            return 1

    xfixes_version = display.xfixes_query_version()
    print('Found XFIXES version %s.%s' % (
        xfixes_version.major_version,
        xfixes_version.minor_version,
    ),
          file=sys.stderr)

    screen = display.screen()

    mask = xfixes.XFixesSetSelectionOwnerNotifyMask | \
           xfixes.XFixesSelectionWindowDestroyNotifyMask | \
           xfixes.XFixesSelectionClientCloseNotifyMask

    display.xfixes_select_selection_input(screen.root, sel_atom, mask)

    while True:
        e = display.next_event()
        print(e)

        if (e.type,
                e.sub_code) == display.extension_event.SetSelectionOwnerNotify:
            print('SetSelectionOwner: owner=0x{0:08x}'.format(e.owner.id))
        elif (e.type, e.sub_code
              ) == display.extension_event.SelectionWindowDestroyNotify:
            print('SelectionWindowDestroy: owner=0x{0:08x}'.format(e.owner.id))
        elif (e.type, e.sub_code
              ) == display.extension_event.SelectionClientCloseNotify:
            print('SelectionClientClose: owner=0x{0:08x}'.format(e.owner.id))
示例#7
0
文件: __init__.py 项目: jage/cafesys
def _find_chrome_window():
    from Xlib.display import Display
    from Xlib import X, Xatom
    from Xlib.protocol import event
    display = Display()
    name = display.get_atom('WM_NAME', 1)
    root = display.screen().root

    class FoundItException(Exception):
        def __init__(self, msg, window):
            self.window = window

    def searcher(parent):
        children = parent.query_tree().children
        for child in children:
            prop = child.get_property(name, Xatom.STRING, 0, 1024)
            if prop and 0 < len(prop.value):
                if prop.value == 'cafesys terminal': # FIXME: make dynamic
                    raise FoundItException('found', child)
            searcher(child)
    try:
        searcher(root)
    except FoundItException, e:
        return e.window
示例#8
0
class Environment():
    def __init__(self):
        self.display = Display()
        self.screen = self.display.screen()
        self.root = self.screen.root
        self.region = Region(x=0,
                             y=0,
                             width=self.screen.width_in_pixels,
                             height=self.screen.height_in_pixels)
        self.desktops = []
        self.windows = self.get_window_set()
        self.window_desktop_map = {}
        self.hidden_windows = set()
        self.visible_windows = set()

        for i in range(self.number_of_desktops()):
            LOGGER.debug("\n\n\nCreating Desktop %d" % (i))
            d = Desktop(i, self)
            self.desktops.append(d)
            #d.print_windows()
            d.arrange()

        self.update_desktop_map()
        self.setup_listeners()

        #self.print_hierarchy(self.root, " - ")

    def setup_listeners(self):
        self.root.change_attributes(event_mask=X.SubstructureNotifyMask
                                    | X.PropertyChangeMask)

        anchor = self.root.create_window(0, 0, 1, 1, 1, self.screen.root_depth)

        anchor.xrandr_select_input(randr.RRScreenChangeNotifyMask
                                   | randr.RRCrtcChangeNotifyMask
                                   | randr.RROutputChangeNotifyMask
                                   | randr.RROutputPropertyNotifyMask)

    def update_all_the_things(self):
        self.display = Display()
        self.screen = self.display.screen()
        self.root = self.screen.root
        self.region = Region(x=0,
                             y=0,
                             width=self.screen.width_in_pixels,
                             height=self.screen.height_in_pixels - 32)

        LOGGER.debug("NEW REGION: %s" % (self.region))

        for d in self.desktops:
            d.resize()

        self.setup_listeners()

    def print_hierarchy(self, window, indent):
        children = window.query_tree().children
        for w in children:
            LOGGER.debug(indent, window.get_wm_class())
            self.print_hierarchy(w, indent + '-')

    def interesting_properties(self):
        #_NET_WM_DESKTOP
        # root: _NET_CURRENT_DESKTOP
        LOGGER.debug("Current desktop")
        LOGGER.debug(
            self.root.get_full_property(
                self.display.intern_atom('_NET_CURRENT_DESKTOP'),
                Xatom.CARDINAL).value[0])

    def current_desktop(self):
        return self.root.get_full_property(
            self.display.intern_atom('_NET_CURRENT_DESKTOP'),
            Xatom.CARDINAL).value[0]

    def number_of_desktops(self):
        return self.root.get_full_property(
            self.display.intern_atom('_NET_NUMBER_OF_DESKTOPS'),
            Xatom.CARDINAL).value[0]

    def update_window_states(self):
        window_ids = self.get_window_set(include_hidden=True)
        for window_id in window_ids:
            if self.is_window_hidden(window_id):
                self.hidden_windows.add(window_id)
            else:
                self.visible_windows.add(window_id)

    def update_desktop_map(self):
        self.window_desktop_map = {}
        for d in self.desktops:
            for window_id in d.get_window_set(include_hidden=True):
                self.window_desktop_map[window_id] = d

    def get_window_desktop(self, window):
        if type(window) is long:
            w = self.display.create_resource_object('window', window)
        else:
            w = window

        try:
            value = w.get_full_property(
                self.display.intern_atom('_NET_WM_DESKTOP'),
                Xatom.CARDINAL).value[0]
            if value > self.number_of_desktops():
                return None
            else:
                return value

        except AttributeError:
            return None
        except Xlib.error.BadWindow:
            return None

    def get_window_states(self, window_id):
        w = self.display.create_resource_object('window', window_id)
        #return w.get_full_property(self.display.intern_atom('_NET_WM_STATE'), Xatom.ATOM).value
        try:
            states = w.get_full_property(
                self.display.get_atom('_NET_WM_STATE'), Xatom.WINDOW)
        except Xlib.error.BadWindow:
            LOGGER.warn("Bad window fetching states...")
            states = None

        if states == None:
            return []
        else:
            res = w.get_full_property(self.display.get_atom('_NET_WM_STATE'),
                                      0).value.tolist()
            return res

    def is_window_hidden(self, window_id):
        hidden_state_atom = self.display.get_atom("_NET_WM_STATE_HIDDEN")
        states = self.get_window_states(window_id)
        return hidden_state_atom in states

    def is_window_visible(self, window_id):
        return not self.is_window_hidden(window_id)

    def listen_for_events(self):
        LOGGER.debug("Listening for change events!")
        while True:
            ev = self.display.next_event()
            self.handle_event(ev)

    def handle_event(self, event):
        old_hidden = set(self.hidden_windows)
        old_visible = set(self.visible_windows)
        self.update_window_states()
        changed_ids = set()
        changed_ids.update(old_hidden.symmetric_difference(
            self.hidden_windows))
        changed_ids.update(
            old_visible.symmetric_difference(self.visible_windows))

        if len(changed_ids) > 0:
            LOGGER.debug("Changed IDs: %s" % (changed_ids))

        needs_update = False

        wm_active_window = self.display.get_atom('_NET_ACTIVE_WINDOW')
        wm_move_window = self.display.get_atom('_NET_MOVERESIZE_WINDOW')
        wm_hidden_window = self.display.get_atom('_NET_WM_STATE_HIDDEN')
        wm_state = self.display.get_atom('_NET_WM_STATE')

        try:
            window_props = event.window.get_full_property(event.atom, 0)
            window_id = int(window_props.value.tolist()[0])
            LOGGER.debug("Window ID: %s" % (window_id))
        except AttributeError:
            pass
            #LOGGER.debug("Not a window-level event")

        for window_id in changed_ids:
            desktop = self.window_desktop_map.get(window_id, None)
            LOGGER.debug("Window changed: %s on desktop: %s" %
                         (window_id, desktop))
            if desktop is not None:
                desktop.arrange()
                needs_update = True

        if event.type == X.PropertyNotify:
            LOGGER.debug("Property changed...")
            if event.atom == wm_active_window:
                LOGGER.debug("Property changed on an active window....")

        elif event.type == X.CreateNotify or event.type == X.DestroyNotify:
            needs_update = True
            window_set = self.get_window_set()

            if event.type == X.CreateNotify:
                LOGGER.debug("Handling creation!")
                new_windows = window_set.difference(self.windows)
                for window in new_windows:
                    window_resource = self.display.create_resource_object(
                        'window', window)
                    window_desktop = self.get_window_desktop(window_resource)
                    if window_desktop is not None:
                        self.desktops[window_desktop].layout.add_window(
                            window_resource)

            if event.type == X.DestroyNotify:
                LOGGER.debug("Handling destruction!")
                missing_windows = self.windows.difference(window_set)
                for window in missing_windows:
                    window_resource = self.display.create_resource_object(
                        'window', window)
                    # TODO: optimize lookup by keeping old map?
                    for desktop in self.desktops:
                        LOGGER.debug("Trying to remove from desktop %s" %
                                     (desktop))
                        desktop.layout.remove_window(window_resource)

            self.windows = window_set

            for desktop in self.desktops:
                desktop.layout.redraw()

        elif event.__class__.__name__ == randr.ScreenChangeNotify.__name__:
            LOGGER.debug('Screen change')
            self.update_all_the_things()

        else:
            #LOGGER.debug("Unhandled event: %d" % (event.type))
            pass

        if needs_update:
            self.update_desktop_map()

    def get_window_set(self, include_hidden=False, desktop_number=None):
        windows = set([
            x for x in self.root.get_full_property(
                self.display.intern_atom('_NET_CLIENT_LIST'),
                Xatom.WINDOW).value
        ])

        if desktop_number is not None:
            #LOGGER.debug("Filtering windows not on: %d" % (desktop_number))
            windows = filter(
                lambda w: self.get_window_desktop(w) == desktop_number,
                windows)

        if include_hidden is False:
            #LOGGER.debug("Filtering hidden windows...")
            windows = filter(lambda w: self.is_window_visible(w) == True,
                             windows)

        return set(windows)
示例#9
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
示例#10
0
        xdiff = ev.root_x - start.root_x
        ydiff = ev.root_y - start.root_y
        start.child.configure(
            x=attr.x + (start.detail == 1 and xdiff or 0),
            y=attr.y + (start.detail == 1 and ydiff or 0),
            width=max(1, attr.width + (start.detail == 3 and xdiff or 0)),
            height=max(1, attr.height + (start.detail == 3 and ydiff or 0)))
    elif ev.type == X.ButtonRelease:
        start = None
wm_name = 'spwm'
dpy = Display()
wids = []
root = dpy.screen().root
fw = dpy.screen().width_in_pixels // 2
fh = dpy.screen().height_in_pixels // 2
net_active_window = dpy.get_atom('_NET_ACTIVE_WINDOW')
net_client_list = dpy.get_atom('_NET_CLIENT_LIST')
net_supported = dpy.get_atom('_NET_SUPPORTED')
net_supporting_wm = dpy.get_atom('_NET_SUPPORTING_WM_CHECK')
net_wm_name = dpy.get_atom('_NET_WM_NAME')
args = sys.argv[1:] if len(sys.argv) == 6 else ['4', 'u', 'i', 'j', 'k']
mods = [X.Mod1Mask, X.Mod2Mask, X.Mod3Mask, X.Mod4Mask, X.Mod5Mask]
mod = mods[int(args[0]) - 1]
btn_mask = X.ButtonPressMask | X.ButtonReleaseMask | X.PointerMotionMask
root.grab_button(1, mod, 1, btn_mask, X.GrabModeAsync, X.GrabModeAsync, X.NONE,
                 X.NONE)
root.grab_button(3, mod, 1, btn_mask, X.GrabModeAsync, X.GrabModeAsync, X.NONE,
                 X.NONE)
root.change_attributes(event_mask=X.SubstructureNotifyMask)
root.change_property(
    net_supported,
示例#11
0
class GlobalKeyBinding(GObject.GObject, threading.Thread):
    __gsignals__ = {
        'activate': (GObject.SignalFlags.RUN_LAST, None, ()),
    }

    def __init__(self):
        GObject.GObject.__init__(self)
        threading.Thread.__init__(self)
        self.setDaemon(True)

        self.keymap = Gdk.Keymap().get_default()
        self.display = Display()
        self.screen = self.display.screen()
        self.window = self.screen.root
        self.ignored_masks = self.get_mask_combinations(X.LockMask | X.Mod2Mask
                                                        | X.Mod5Mask)
        self.map_modifiers()
        self.raw_keyval = None
        self.keytext = ""

    def map_modifiers(self):
        gdk_modifiers = (Gdk.ModifierType.CONTROL_MASK,
                         Gdk.ModifierType.SHIFT_MASK,
                         Gdk.ModifierType.MOD1_MASK,
                         Gdk.ModifierType.MOD2_MASK,
                         Gdk.ModifierType.MOD3_MASK,
                         Gdk.ModifierType.MOD4_MASK,
                         Gdk.ModifierType.MOD5_MASK,
                         Gdk.ModifierType.SUPER_MASK,
                         Gdk.ModifierType.HYPER_MASK)
        self.known_modifiers_mask = 0
        for modifier in gdk_modifiers:
            if "Mod" not in Gtk.accelerator_name(
                    0, modifier) or "Mod4" in Gtk.accelerator_name(
                        0, modifier):
                self.known_modifiers_mask |= modifier

    def grab(self, key):
        accelerator = key
        accelerator = accelerator.replace("<Super>", "<Mod4>")
        keyval, modifiers = Gtk.accelerator_parse(accelerator)
        if not accelerator or (not keyval and not modifiers):
            self.keycode = None
            self.modifiers = None
            return False

        self.keytext = key
        try:
            self.keycode = self.keymap.get_entries_for_keyval(
                keyval).keys[0].keycode
        except AttributeError:
            # In older Gtk3 the get_entries_for_keyval() returns an unnamed tuple...
            self.keycode = self.keymap.get_entries_for_keyval(
                keyval)[1][0].keycode
        self.modifiers = int(modifiers)

        # Request to receive key press/release reports from other windows that may not be using modifiers
        catch = error.CatchError(error.BadWindow)
        if self.modifiers:
            self.window.change_attributes(onerror=catch,
                                          event_mask=X.KeyPressMask
                                          | X.KeyReleaseMask)
        else:
            self.window.change_attributes(onerror=catch,
                                          event_mask=X.NoEventMask)
        if catch.get_error():
            return False

        catch = error.CatchError(error.BadAccess)
        for ignored_mask in self.ignored_masks:
            mod = modifiers | ignored_mask
            result = self.window.grab_key(self.keycode,
                                          mod,
                                          True,
                                          X.GrabModeAsync,
                                          X.GrabModeAsync,
                                          onerror=catch)
        self.display.flush()
        if catch.get_error():
            return False

        catch = error.CatchError(error.BadCursor)
        if not self.modifiers:
            # We grab Super+click so that we can forward it to the window manager and allow Super+click bindings (window move, resize, etc.)
            self.window.grab_button(X.AnyButton, X.Mod4Mask, True,
                                    X.ButtonPressMask, X.GrabModeSync,
                                    X.GrabModeAsync, X.NONE, X.NONE)
        self.display.flush()
        if catch.get_error():
            return False

        return True

    def ungrab(self):
        if self.keycode:
            self.window.ungrab_key(self.keycode, X.AnyModifier, self.window)

    def rebind(self, key):
        self.ungrab()
        if key != "":
            self.grab(key)
        else:
            self.keytext = ""

    def set_focus_window(self, window=None):
        self.ungrab()
        if window is None:
            self.window = self.screen.root
        else:
            self.window = self.display.create_resource_object(
                "window", window.get_xid())
        self.grab(self.keytext)

    def get_mask_combinations(self, mask):
        return [x for x in range(mask + 1) if not (x & ~mask)]

    def idle(self):
        self.emit("activate")
        return False

    def activate(self):
        GLib.idle_add(self.run)

    # Get which window manager we're currently using (Marco, Compiz, Metacity, etc...)
    def get_wm(self):
        name = ''
        wm_check = self.display.get_atom('_NET_SUPPORTING_WM_CHECK')
        win_id = self.window.get_full_property(wm_check, X.AnyPropertyType)
        if win_id:
            w = self.display.create_resource_object("window", win_id.value[0])
            wm_name = self.display.get_atom('_NET_WM_NAME')
            prop = w.get_full_property(wm_name, X.AnyPropertyType)
            if prop:
                name = prop.value
        return name.lower()

    def run(self):
        self.running = True
        wait_for_release = False
        while self.running:
            event = self.display.next_event()

            if self.modifiers:
                # Use simpler logic when using traditional combined keybindings
                modifiers = event.state & self.known_modifiers_mask
                if event.type == X.KeyPress and event.detail == self.keycode and modifiers == self.modifiers:
                    GLib.idle_add(self.idle)

            else:
                try:
                    # KeyPress
                    if event.type == X.KeyPress and event.detail == self.keycode and not wait_for_release:
                        modifiers = event.state & self.known_modifiers_mask
                        if modifiers == self.modifiers:
                            wait_for_release = True

                    # KeyRelease
                    elif event.type == X.KeyRelease and event.detail == self.keycode and wait_for_release:
                        GLib.idle_add(self.idle)
                        wait_for_release = False

                    # Modifiers are often used with mouse events - don't let the system swallow those
                    elif event.type == X.ButtonPress:
                        self.display.allow_events(X.ReplayPointer,
                                                  X.CurrentTime)
                        # Compiz would rather not have the event sent to it and just read it from the replayed queue
                        wm = self.get_wm()
                        if wm != "compiz":
                            self.display.ungrab_keyboard(X.CurrentTime)
                            self.display.ungrab_pointer(X.CurrentTime)
                            query_pointer = self.window.query_pointer()
                            self.display.send_event(query_pointer.child, event,
                                                    X.ButtonPressMask, True)
                        wait_for_release = False

                    # If the user presses another key in between the KeyPress and the KeyRelease, they
                    # meant to use a different shortcut
                    else:
                        self.display.ungrab_keyboard(X.CurrentTime)
                        # Send the event up in case another window is listening to it
                        self.display.send_event(
                            event.window, event,
                            X.KeyPressMask | X.KeyReleaseMask, True)
                        wait_for_release = False
                except AttributeError:
                    continue

    def stop(self):
        self.running = False
        self.ungrab()
        self.display.close()