def __init__(self, name, replace_other_wm, display=None): gobject.GObject.__init__(self) self._name = name if display is None: display = gtk.gdk.display_manager_get().get_default_display() self._display = display self._alt_display = gtk.gdk.Display(self._display.get_name()) self._root = self._display.get_default_screen().get_root_window() self._ewmh_window = None self._windows = {} # EWMH says we have to know the order of our windows oldest to # youngest... self._windows_in_order = [] # Become the Official Window Manager of this year's display: self._wm_selection = wimpiggy.selection.ManagerSelection(self._display, "WM_S0") self._wm_selection.connect("selection-lost", self._lost_wm_selection) # May throw AlreadyOwned: if replace_other_wm: mode = self._wm_selection.FORCE else: mode = self._wm_selection.IF_UNOWNED self._wm_selection.acquire(mode) # (If we become a compositing manager, then we will want to do the # same thing with the _NET_WM_CM_S0 selection (says EWMH). AFAICT # this basically will just be used by clients to know that they can # use RGBA visuals.) # Set up the necessary EWMH properties on the root window. self._setup_ewmh_window() prop_set(self._root, "_NET_SUPPORTED", ["atom"], self._NET_SUPPORTED) prop_set(self._root, "_NET_DESKTOP_VIEWPORT", ["u32"], [0, 0]) # Load up our full-screen widget self._world_window = WorldWindow() self._world_window.set_screen(self._display.get_default_screen()) self.notify("toplevel") self._world_window.show_all() # Okay, ready to select for SubstructureRedirect and then load in all # the existing clients. wimpiggy.lowlevel.add_event_receiver(self._root, self) wimpiggy.lowlevel.substructureRedirect(self._root) for w in wimpiggy.lowlevel.get_children(self._root): # Checking for FOREIGN here filters out anything that we've # created ourselves (like, say, the world window), and checking # for mapped filters out any withdrawn windows. if (w.get_window_type() == gtk.gdk.WINDOW_FOREIGN and not wimpiggy.lowlevel.is_override_redirect(w) and wimpiggy.lowlevel.is_mapped(w)): log("Wm managing pre-existing child") self._manage_client(w) # Also watch for focus change events on the root window wimpiggy.lowlevel.selectFocusChange(self._root)
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 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_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 _setup_ewmh_window(self): # Set up a 1x1 invisible unmapped window, with which to participate in # EWMH's _NET_SUPPORTING_WM_CHECK protocol. The only important things # about this window are the _NET_SUPPORTING_WM_CHECK property, and # its title (which is supposed to be the name of the window manager). # NB, GDK will do strange things to this window. We don't want to use # it for anything. (In particular, it will call XSelectInput on it, # which is fine normally when GDK is running in a client, but since it # happens to be using the same connection as we the WM, it will # clobber any XSelectInput calls that *we* might have wanted to make # on this window.) Also, GDK might silently swallow all events that # are detected on it, anyway. self._ewmh_window = gtk.gdk.Window(self._root, width=1, height=1, window_type=gtk.gdk.WINDOW_TOPLEVEL, event_mask=0, # event mask wclass=gtk.gdk.INPUT_ONLY, title=self._name) prop_set(self._ewmh_window, "_NET_SUPPORTING_WM_CHECK", "window", self._ewmh_window) prop_set(self._root, "_NET_SUPPORTING_WM_CHECK", "window", self._ewmh_window)
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_desktop_list_changed(self, desktops): prop_set(self._root, "_NET_NUMBER_OF_DESKTOPS", "u32", len(desktops)) prop_set(self._root, "_NET_DESKTOP_NAMES", ["utf8"], desktops)
def _set_blob_in_place(self, settings_blob): if type(settings_blob)!=tuple: log.warn("discarding xsettings because of incompatible format: %s", type(settings_blob)) return prop_set(self._window, XSETTINGS, XSETTINGS_TYPE, settings_blob)
def set_tray_orientation(tray_window, orientation): prop_set(tray_window, TRAY_ORIENTATION, "u32", orientation)
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 root_set(self, *args): prop_set(self._root, *args)
def set_tray_orientation(tray_window, orientation): prop_set(tray_window, TRAY_VISUAL, "u32", orientation)
def set_tray_visual(tray_window, gdk_visual): prop_set(tray_window, TRAY_VISUAL, "visual", gdk_visual)
def _set_blob_in_place(self, settings_blob): if type(settings_blob) != tuple: log.warn("discarding xsettings because of incompatible format: %s", type(settings_blob)) return prop_set(self._window, XSETTINGS, XSETTINGS_TYPE, settings_blob)
def save_uuid(uuid): prop_set(gtk.gdk.get_default_root_window(), "_XPRA_SERVER_UUID", "latin1", uuid)
def __init__(self, name, replace_other_wm, display=None): gobject.GObject.__init__(self) self._name = name if display is None: display = gtk.gdk.display_manager_get().get_default_display() self._display = display self._alt_display = gtk.gdk.Display(self._display.get_name()) self._root = self._display.get_default_screen().get_root_window() self._ewmh_window = None self._windows = {} # EWMH says we have to know the order of our windows oldest to # youngest... self._windows_in_order = [] # Become the Official Window Manager of this year's display: self._wm_selection = wimpiggy.selection.ManagerSelection(self._display, "WM_S0") self._wm_selection.connect("selection-lost", self._lost_wm_selection) # May throw AlreadyOwned: if replace_other_wm: mode = self._wm_selection.FORCE else: mode = self._wm_selection.IF_UNOWNED self._wm_selection.acquire(mode) # (If we become a compositing manager, then we will want to do the # same thing with the _NET_WM_CM_S0 selection (says EWMH). AFAICT # this basically will just be used by clients to know that they can # use RGBA visuals.) # Set up the necessary EWMH properties on the root window. self._setup_ewmh_window() prop_set(self._root, "_NET_SUPPORTED", ["atom"], self._NET_SUPPORTED) prop_set(self._root, "_NET_DESKTOP_VIEWPORT", ["u32"], [0, 0]) # Load up our full-screen widget self._world_window = WorldWindow() self._world_window.set_screen(self._display.get_default_screen()) self.notify("toplevel") self._world_window.show_all() # Okay, ready to select for SubstructureRedirect and then load in all # the existing clients. add_event_receiver(self._root, self) substructureRedirect(self._root) for w in get_children(self._root): # Checking for FOREIGN here filters out anything that we've # created ourselves (like, say, the world window), and checking # for mapped filters out any withdrawn windows. if (w.get_window_type() == gtk.gdk.WINDOW_FOREIGN and not is_override_redirect(w) and is_mapped(w)): log("Wm managing pre-existing child") self._manage_client(w) # Also watch for focus change events on the root window selectFocusChange(self._root) selectBellNotification(self._root, True)
def _set_blob_in_place(self, settings_blob): prop_set(self._window, "_XSETTINGS_SETTINGS", "xsettings-settings", settings_blob)
def root_set(*args): prop_set(gtk.gdk.get_default_root_window(), *args)
class ClientWindow(gtk.Window): def __init__(self, client, wid, x, y, w, h, metadata, override_redirect, client_properties): if override_redirect: init_window(self, WINDOW_POPUP) else: init_window(self, WINDOW_TOPLEVEL) self._client = client self._id = wid self._pos = (-1, -1) self._size = (1, 1) self._backing = None self.new_backing(w, h) self._metadata = {} self._override_redirect = override_redirect self._client_properties = client_properties self._refresh_timer = None self._refresh_min_pixels = -1 self._refresh_ignore_sequence = -1 # used for only sending focus events *after* the window is mapped: self._been_mapped = False self._override_redirect_windows = [] # tell KDE/oxygen not to intercept clicks # see: https://bugs.kde.org/show_bug.cgi?id=274485 self.set_data("_kde_no_window_grab", 1) self.update_metadata(metadata) self.set_app_paintable(True) self.add_events(WINDOW_EVENT_MASK) self.move(x, y) self.set_default_size(w, h) if override_redirect: transient_for = self.get_transient_for() type_hint = self.get_type_hint() if transient_for is not None and transient_for.window is not None and type_hint in OR_TYPE_HINTS: transient_for._override_redirect_windows.append(self) self.connect("notify::has-toplevel-focus", self._focus_change) def do_realize(self): if not PRESERVE_WORSPACE: gtk.Window.do_realize(self) return ndesktops = 0 try: root = gtk.gdk.screen_get_default().get_root_window() prop = root.property_get("_NET_NUMBER_OF_DESKTOPS") if prop is not None and len(prop) == 3 and len(prop[2]) == 1: ndesktops = prop[2][0] except Exception, e: log.error("failed to get workspace count: %s", e) workspace = self._client_properties.get("workspace", -1) log("do_realize() workspace=%s (ndesktops=%s)", workspace, ndesktops) #below we duplicate gtk.window.realize() code #just so we can insert the property code at the right place: #after the gdk.Window is created, but before it gets positionned. allocation = self.get_allocation() if allocation.x == -1 and allocation.y == -1 and allocation.width == 1 and allocation.height == 1: w, h = self.size_request() if w > 0 or h > 0: allocation.width = w allocation.height = h self.size_allocate(w, h) self.queue_resize() if self.flags() & gtk.REALIZED: log.error("window is already realized!") return self.set_flags(gtk.REALIZED) is_toplevel = self.get_parent() is None if hasattr(self, "is_toplevel"): is_toplevel = self.is_toplevel() if is_toplevel: window_type = gtk.gdk.WINDOW_TOPLEVEL else: window_type = gtk.gdk.WINDOW_TEMP if self.get_has_frame(): #TODO: duplicate gtk code here too.. pass events = self.get_events() | gdk.EXPOSURE_MASK | gdk.STRUCTURE_MASK | \ gdk.KEY_PRESS_MASK | gdk.KEY_RELEASE_MASK self.window = gdk.Window( self.get_root_window(), x=allocation.x, y=allocation.y, width=allocation.width, height=allocation.height, wmclass_name=self.wmclass_name, wmclass_class=self.wmclass_class, window_type=window_type, wclass=gdk.INPUT_OUTPUT, title=self.get_title(), event_mask=events, ) if has_wimpiggy_prop and not self._override_redirect and ndesktops > workspace and workspace >= 0: try: prop_set(self.window, "_NET_WM_DESKTOP", "u32", workspace) except Exception, e: log.error("failed to set workspace: %s", e)