def wm_check(display): #there should only be one screen... but let's check all of them for i in range(display.get_n_screens()): screen = display.get_screen(i) root = screen.get_root_window() wm_prop = "WM_S%s" % i cwm_prop = "_NEW_WM_CM_S%s" % i wm_so = myGetSelectionOwner(display, wm_prop) cwm_so = myGetSelectionOwner(display, cwm_prop) log("ewmh selection owner for %s: %s", wm_prop, wm_so) log("compositing window manager %s: %s", cwm_prop, cwm_so) try: ewmh_wm = prop_get(root, "_NET_SUPPORTING_WM_CHECK", "window", ignore_errors=True, raise_xerrors=False) except: #errors here generally indicate that the window is gone #which is fine: it means the previous window manager is no longer active continue log("_NET_SUPPORTING_WM_CHECK for screen %s: %s", i, ewmh_wm) if ewmh_wm: try: name = prop_get(ewmh_wm, "_NET_WM_NAME", "utf8", ignore_errors=False, raise_xerrors=False) except: name = None log.warn("Warning: found an existing window manager on screen %s using window id %s: %s", i, hex(get_xwindow(ewmh_wm)), name or "unknown") if (wm_so is None or wm_so==0) and (cwm_so is None or cwm_so==0): log.error("it does not own the selection '%s' or '%s' so we cannot take over and make it exit", wm_prop, cwm_prop) log.error("please stop %s so you can run xpra on this display", name or "the existing window manager") return False return True
def test_icon(self): LARGE_W = 49 LARGE_H = 47 SMALL_W = 25 SMALL_H = 23 large = cairo.ImageSurface(cairo.FORMAT_ARGB32, LARGE_W, LARGE_H) # Scribble something on our "icon" large_cr = cairo.Context(large) pat = cairo.LinearGradient(0, 0, LARGE_W, LARGE_H) pat.add_color_stop_rgb(0, 1, 0, 0) pat.add_color_stop_rgb(1, 0, 1, 0) large_cr.set_source(pat) large_cr.paint() # Make a "small version" small = cairo.ImageSurface(cairo.FORMAT_ARGB32, SMALL_W, SMALL_H) small_cr = cairo.Context(small) small_cr.set_source(pat) small_cr.paint() small_dat = struct.pack("@II", SMALL_W, SMALL_H) + str( small.get_data()) large_dat = struct.pack("@II", LARGE_W, LARGE_H) + str( large.get_data()) icon_bytes = small_dat + large_dat + small_dat p.prop_set(self.win, "_NET_WM_ICON", "debug-CARDINAL", icon_bytes) self._assert_icon_matches("_NET_WM_ICON", large) # Corrupted icons: # Width, but not height: p.prop_set(self.win, "corrupted1", "debug-CARDINAL", "\xff\xff\xff\xff") corrupted1 = p.prop_get(self.win, "corrupted1", "icon") assert corrupted1 is None # Width and height, but not enough data for them: p.prop_set(self.win, "corrupted2", "debug-CARDINAL", struct.pack("@" + "i" * 4, 10, 10, 0, 0)) corrupted2 = p.prop_get(self.win, "corrupted2", "icon") assert corrupted2 is None # A small, then a large, then a small, then a corrupted, should # successfully extract largest: p.prop_set( self.win, "corrupted3", "debug-CARDINAL", small_dat + large_dat + small_dat # Width and height -- large enough to overflow to negative # if we treat sizes as signed + "\xff\xff\xff\xff" + "\xff\xff\xff\xff" # Inadequate body + "\xff\xff\xff\xff") self._assert_icon_matches("corrupted3", large)
def test_icon(self): LARGE_W = 49 LARGE_H = 47 SMALL_W = 25 SMALL_H = 23 large = cairo.ImageSurface(cairo.FORMAT_ARGB32, LARGE_W, LARGE_H) # Scribble something on our "icon" large_cr = cairo.Context(large) pat = cairo.LinearGradient(0, 0, LARGE_W, LARGE_H) pat.add_color_stop_rgb(0, 1, 0, 0) pat.add_color_stop_rgb(1, 0, 1, 0) large_cr.set_source(pat) large_cr.paint() # Make a "small version" small = cairo.ImageSurface(cairo.FORMAT_ARGB32, SMALL_W, SMALL_H) small_cr = cairo.Context(small) small_cr.set_source(pat) small_cr.paint() small_dat = struct.pack("@II", SMALL_W, SMALL_H) + str(small.get_data()) large_dat = struct.pack("@II", LARGE_W, LARGE_H) + str(large.get_data()) icon_bytes = small_dat + large_dat + small_dat p.prop_set(self.win, "_NET_WM_ICON", "debug-CARDINAL", small_dat + large_dat + small_dat) self._assert_icon_matches("_NET_WM_ICON", large) # Corrupted icons: # Width, but not height: p.prop_set(self.win, "corrupted1", "debug-CARDINAL", "\xff\xff\xff\xff") corrupted1 = p.prop_get(self.win, "corrupted1", "icon") assert corrupted1 is None # Width and height, but not enough data for them: p.prop_set(self.win, "corrupted2", "debug-CARDINAL", struct.pack("@" + "i" * 4, 10, 10, 0, 0)) corrupted2 = p.prop_get(self.win, "corrupted2", "icon") assert corrupted2 is None # A small, then a large, then a small, then a corrupted, should # successfully extract largest: p.prop_set(self.win, "corrupted3", "debug-CARDINAL", small_dat + large_dat + small_dat # Width and height -- large enough to overflow to negative # if we treat sizes as signed + "\xff\xff\xff\xff" + "\xff\xff\xff\xff" # Inadequate body + "\xff\xff\xff\xff") self._assert_icon_matches("corrupted3", large)
def test_prop_get_set_errors(self): assert p.prop_get(self.win, "SADFSAFDSADFASDF", "utf8") is None self.win2.destroy() gtk.gdk.flush() assert_raises(wimpiggy.error.XError, wimpiggy.error.trap.call, p.prop_set, self.win2, "ASDF", "utf8", u("")) assert p.prop_get(self.win2, "ASDF", "utf8") is None p.prop_set(self.win, "ASDF", "utf8", u("")) assert p.prop_get(self.win, "ASDF", "latin1") is None
def test_strut(self): p.prop_set(self.win, "_NET_WM_STRUT_PARTIAL", "debug-CARDINAL", struct.pack("@" + "i" * 12, *range(12))) partial = p.prop_get(self.win, "_NET_WM_STRUT_PARTIAL", "strut-partial") assert partial.left == 0 assert partial.right == 1 assert partial.top == 2 assert partial.bottom == 3 assert partial.left_start_y == 4 assert partial.left_end_y == 5 assert partial.right_start_y == 6 assert partial.right_end_y == 7 assert partial.top_start_x == 8 assert partial.top_end_x == 9 assert partial.bottom_start_x == 10 assert partial.bottom_stop_x == 11 p.prop_set(self.win, "_NET_WM_STRUT", "debug-CARDINAL", struct.pack("@" + "i" * 4, *range(4))) full = p.prop_get(self.win, "_NET_WM_STRUT", "strut") assert full.left == 0 assert full.right == 1 assert full.top == 2 assert full.bottom == 3 assert full.left_start_y == 0 assert full.left_end_y == 0 assert full.right_start_y == 0 assert full.right_end_y == 0 assert full.top_start_x == 0 assert full.top_end_x == 0 assert full.bottom_start_x == 0 assert full.bottom_stop_x == 0 p.prop_set(self.win, "corrupted1", "debug-CARDINAL", "\xff\xff\xff\xff") corrupted = p.prop_get(self.win, "corrupted1", "strut") assert corrupted.left == 0xffffffff assert corrupted.right == 0 assert corrupted.top == 0 assert corrupted.bottom == 0 assert corrupted.left_start_y == 0 assert corrupted.left_end_y == 0 assert corrupted.right_start_y == 0 assert corrupted.right_end_y == 0 assert corrupted.top_start_x == 0 assert corrupted.top_end_x == 0 assert corrupted.bottom_start_x == 0 assert corrupted.bottom_stop_x == 0
def do_selection_request_event(self, event): debug("do_selection_request_event(%s)", event) # Black magic: the superclass default handler for this signal # implements all the hards parts of selection handling, occasionally # calling back to the do_selection_get handler (below) to actually get # the data to be sent. However, it only does this for targets that # have been registered ahead of time; other targets fall through to a # default implementation that cannot be overridden. So, we swoop in # ahead of time and add whatever target was requested to the list of # targets we want to handle! # # Special cases (magic targets defined by ICCCM): # TIMESTAMP: the remote side has a different timeline than us, so # sending TIMESTAMPS across the wire doesn't make any sense. We # ignore TIMESTAMP requests, and let them fall through to GTK+'s # default handler. # TARGET: GTK+ has default handling for this, but we don't want to # use it. Fortunately, if we tell GTK+ that we can handle TARGET # requests, then it will pass them on to us rather than fall # through to the default handler. # MULTIPLE: Ugh. To handle this properly, we need to go out # ourselves and fetch the magic property off the requesting window # (with proper error trapping and all), and interpret its # contents. Probably doable (FIXME), just a pain. # # Another special case is that if an app requests the contents of a # clipboard that it currently owns, then GTK+ will short-circuit the # normal logic and request the contents directly (i.e. it calls # gtk_selection_invoke_handler) -- without giving us a chance to # assert that we can handle the requested sort of target. Fortunately, # Xpra never needs to request the clipboard when it owns it, so that's # okay. assert str(event.selection) == self._selection target = str(event.target) if target == "TIMESTAMP": pass elif target == "MULTIPLE": try: from wimpiggy.prop import prop_get except ImportError: debug( "MULTIPLE for property '%s' not handled due to missing wimpiggy bindings", event.property) gtk.Invisible.do_selection_request_event(self, event) return atoms = prop_get(event.window, event.property, ["multiple-conversion"]) debug("MULTIPLE clipboard atoms: %r", atoms) if atoms: targets = atoms[::2] for t in targets: self.selection_add_target(self._selection, t, 0) else: debug("target for %s: %r", self._selection, target) self.selection_add_target(self._selection, target, 0) debug("do_selection_request_event(%s) target=%s, selection=%s", event, target, self._selection) gtk.Invisible.do_selection_request_event(self, event)
def wm_check(display): #there should only be one screen... but let's check all of them for i in range(display.get_n_screens()): screen = display.get_screen(i) root = screen.get_root_window() wm_prop = "WM_S%s" % i cwm_prop = "_NEW_WM_CM_S%s" % i wm_so = myGetSelectionOwner(display, wm_prop) cwm_so = myGetSelectionOwner(display, cwm_prop) log("ewmh selection owner for %s: %s", wm_prop, wm_so) log("compositing window manager %s: %s", cwm_prop, cwm_so) try: ewmh_wm = prop_get(root, "_NET_SUPPORTING_WM_CHECK", "window", ignore_errors=True, raise_xerrors=False) except: #errors here generally indicate that the window is gone #which is fine: it means the previous window manager is no longer active continue log("_NET_SUPPORTING_WM_CHECK for screen %s: %s", i, ewmh_wm) if ewmh_wm: try: name = prop_get(ewmh_wm, "_NET_WM_NAME", "utf8", ignore_errors=False, raise_xerrors=False) except: name = None log.warn( "Warning: found an existing window manager on screen %s using window id %s: %s", i, hex(get_xwindow(ewmh_wm)), name or "unknown") if (wm_so is None or wm_so == 0) and (cwm_so is None or cwm_so == 0): log.error( "it does not own the selection '%s' or '%s' so we cannot take over and make it exit", wm_prop, cwm_prop) log.error("please stop %s so you can run xpra on this display", name or "the existing window manager") return False return True
def test_multiple_conversion(self): x1 = wimpiggy.lowlevel.get_xatom("X1") x2 = wimpiggy.lowlevel.get_xatom("X2") x3 = wimpiggy.lowlevel.get_xatom("X3") x4 = wimpiggy.lowlevel.get_xatom("X4") p.prop_set(self.win, "_MY_MULTIPLE_TEST", "debug-CARDINAL", struct.pack("@IIII", x1, x2, x3, x4)) out = p.prop_get(self.win, "_MY_MULTIPLE_TEST", ["multiple-conversion"]) assert len(out) == 4 assert out == ["X1", "X2", "X3", "X4"]
def do_selection_request_event(self, event): debug("do_selection_request_event(%s)", event) # Black magic: the superclass default handler for this signal # implements all the hards parts of selection handling, occasionally # calling back to the do_selection_get handler (below) to actually get # the data to be sent. However, it only does this for targets that # have been registered ahead of time; other targets fall through to a # default implementation that cannot be overridden. So, we swoop in # ahead of time and add whatever target was requested to the list of # targets we want to handle! # # Special cases (magic targets defined by ICCCM): # TIMESTAMP: the remote side has a different timeline than us, so # sending TIMESTAMPS across the wire doesn't make any sense. We # ignore TIMESTAMP requests, and let them fall through to GTK+'s # default handler. # TARGET: GTK+ has default handling for this, but we don't want to # use it. Fortunately, if we tell GTK+ that we can handle TARGET # requests, then it will pass them on to us rather than fall # through to the default handler. # MULTIPLE: Ugh. To handle this properly, we need to go out # ourselves and fetch the magic property off the requesting window # (with proper error trapping and all), and interpret its # contents. Probably doable (FIXME), just a pain. # # Another special case is that if an app requests the contents of a # clipboard that it currently owns, then GTK+ will short-circuit the # normal logic and request the contents directly (i.e. it calls # gtk_selection_invoke_handler) -- without giving us a chance to # assert that we can handle the requested sort of target. Fortunately, # Xpra never needs to request the clipboard when it owns it, so that's # okay. assert str(event.selection) == self._selection target = str(event.target) if target == "TIMESTAMP": pass elif target == "MULTIPLE": try: from wimpiggy.prop import prop_get except ImportError: debug("MULTIPLE for property '%s' not handled due to missing wimpiggy bindings", event.property) gtk.Invisible.do_selection_request_event(self, event) return atoms = prop_get(event.window, event.property, ["multiple-conversion"]) debug("MULTIPLE clipboard atoms: %r", atoms) if atoms: targets = atoms[::2] for t in targets: self.selection_add_target(self._selection, t, 0) else: debug("target for %s: %r", self._selection, target) self.selection_add_target(self._selection, target, 0) debug("do_selection_request_event(%s) target=%s, selection=%s", event, target, self._selection) gtk.Invisible.do_selection_request_event(self, event)
def get_workspace(self): try: if not has_wimpiggy_prop: prop = self.window.get_screen().get_root_window().property_get("_NET_CURRENT_DESKTOP") if not prop or len(prop)!=3 or len(prop[2])!=1: return -1 return prop[2][0] v = prop_get(self.get_window(), "_NET_WM_DESKTOP", "u32", ignore_errors=True) if type(v)==int: return v except Exception, e: log.error("failed to detect workspace: %s", e)
def get_workspace(self): try: if not has_wimpiggy_prop: prop = self.window.get_screen().get_root_window().property_get( "_NET_CURRENT_DESKTOP") if not prop or len(prop) != 3 or len(prop[2]) != 1: return -1 return prop[2][0] v = prop_get(self.get_window(), "_NET_WM_DESKTOP", "u32", ignore_errors=True) if type(v) == int: return v except Exception, e: log.error("failed to detect workspace: %s", e)
def xget_u32_property(target, name): try: if not has_wimpiggy_prop: if is_gtk3(): name_atom = gdk.Atom.intern(name, False) type_atom = gdk.Atom.intern("CARDINAL", False) gdk.property_get(target, name_atom, type_atom, 0, 9999, False) else: prop = target.property_get(name) if not prop or len(prop) != 3 or len(prop[2]) != 1: return None log("xget_u32_property(%s, %s)=%s", target, name, prop[2][0]) return prop[2][0] v = prop_get(target, name, "u32", ignore_errors=True) log("xget_u32_property(%s, %s)=%s", target, name, v) if type(v) == int: return v except Exception, e: log.error("xget_u32_property error on %s / %s: %s", target, name, e)
def enc(self, t, value, exp): enc = p._prop_encode(self.display, t, value) assert enc[-1] == exp assert p._prop_decode(self.display, t, enc[-1]) == value p.prop_set(self.win, "__TEST__", t, value) assert p.prop_get(self.win, "__TEST__", t) == value
def _get_settings_blob(self): owner = self._owner() if owner is None: return None return prop_get(owner, XSETTINGS, XSETTINGS_TYPE)
v = prop_get(target, name, "u32", ignore_errors=True) log("xget_u32_property(%s, %s)=%s", target, name, v) if type(v) == int: return v except Exception, e: log.error("xget_u32_property error on %s / %s: %s", target, name, e) return None CAN_SET_WORKSPACE = False if not sys.platform.startswith("win") and has_wimpiggy_prop: try: #TODO: in theory this is not a proper check, meh - that will do root = gtk.gdk.get_default_root_window() supported = prop_get(root, "_NET_SUPPORTED", ["atom"], ignore_errors=True) CAN_SET_WORKSPACE = bool(supported) and "_NET_WM_DESKTOP" in supported except: pass if sys.version < '3': import codecs def u(x): return codecs.unicode_escape_decode(x)[0] else: def u(x): return x
def _assert_icon_matches(self, prop, expected): surf = p.prop_get(self.win, prop, "icon") assert surf.get_width() == expected.get_width() assert surf.get_height() == expected.get_height() assert str(surf.get_data()) == str(expected.get_data())
def get_targets(targets): atoms = prop_get(event.window, event.property, ["multiple-conversion"]) debug("MULTIPLE clipboard atoms: %r", atoms) targets += atoms[::2]
def get_targets(targets): atoms = prop_get(event.window, event.property, ["multiple-conversion"]) log("MULTIPLE clipboard atoms: %r", atoms) targets += atoms[::2]
def _notify(self, prop): v = prop_get(gtk.gdk.get_default_root_window(), prop, "latin1", ignore_errors=True) self.emit("root-prop-changed", prop, v)
return prop[2][0] v = prop_get(target, name, "u32", ignore_errors=True) log("xget_u32_property(%s, %s)=%s", target, name, v) if type(v) == int: return v except Exception, e: log.error("xget_u32_property error on %s / %s: %s", target, name, e) return None CAN_SET_WORKSPACE = False if not sys.platform.startswith("win") and has_wimpiggy_prop: try: # TODO: in theory this is not a proper check, meh - that will do root = gtk.gdk.get_default_root_window() supported = prop_get(root, "_NET_SUPPORTED", ["atom"], ignore_errors=True) CAN_SET_WORKSPACE = bool(supported) and "_NET_WM_DESKTOP" in supported except: pass if sys.version < "3": import codecs def u(x): return codecs.unicode_escape_decode(x)[0] else: def u(x): return x
def _get_settings_blob(self): owner = self._owner() if owner is None: return None blob = prop_get(owner, "_XSETTINGS_SETTINGS", "xsettings-settings") return blob
def get_targets(): win = get_pywindow(event.requestor) atoms = prop_get(win, event.property, ["multiple-conversion"]) log("MULTIPLE clipboard atoms: %r", atoms) targets += atoms[::2]
def get_uuid(): return prop_get(gtk.gdk.get_default_root_window(), "_XPRA_SERVER_UUID", "latin1", ignore_errors=True)