def dock_tray(self, xid): root = gtk.gdk.get_default_root_window() window = gtk.gdk.window_foreign_new(xid) w, h = window.get_geometry()[2:4] event_mask = gtk.gdk.STRUCTURE_MASK | gtk.gdk.EXPOSURE_MASK | gtk.gdk.PROPERTY_CHANGE_MASK window.set_events(event_mask=event_mask) add_event_receiver(window, self) w = max(1, min(200, w)) h = max(1, min(200, h)) log("dock_tray(%s) window=%s, geometry=%s, visual.depth=%s", xid, window, window.get_geometry(), window.get_visual().depth) event_mask = gtk.gdk.STRUCTURE_MASK | gtk.gdk.EXPOSURE_MASK | gtk.gdk.PROPERTY_CHANGE_MASK tray_window = gtk.gdk.Window(root, width=w, height=h, window_type=gtk.gdk.WINDOW_TOPLEVEL, event_mask = event_mask, wclass=gtk.gdk.INPUT_OUTPUT, title="TrayWindow", x=-200, y=-200, override_redirect=True, visual=window.get_visual(), colormap=window.get_colormap()) log("dock_tray(%s) setting tray properties", xid) set_tray_window(tray_window, window) tray_window.show() self.tray_windows[window] = tray_window self.window_trays[tray_window] = window log("dock_tray(%s) resizing and reparenting", xid) window.resize(w, h) withdraw(window) reparent(window, tray_window, 0, 0) map_raised(window) log("dock_tray(%s) new tray container window %s", xid, get_xwindow(tray_window)) tray_window.invalidate_rect(gtk.gdk.Rectangle(width=w, height=h), True) embedder = get_xwindow(tray_window) send_xembed_message(window, XEMBED_EMBEDDED_NOTIFY, 0, embedder, XEMBED_VERSION)
def test_get_xwindow_pywindow(self): d2 = self.clone_display() r1 = self.root() r2 = self.root(d2) assert r1 is not r2 assert l.get_xwindow(r1) == l.get_xwindow(r2) win = self.window() assert l.get_xwindow(r1) != l.get_xwindow(win) assert l.get_pywindow(r2, l.get_xwindow(r1)) is r2 assert_raises(l.XError, l.get_pywindow, self.display, 0) # This is necessary to stop some mysterious failure (perhaps d2 being # garbage collected before r2): del r2
def do_get_property_contents_handle(self, name): if self._window is None: #shortcut out return None if self._contents_handle is None: log("refreshing named pixmap") assert self._listening_to is None def set_pixmap(): # The tricky part here is that the pixmap returned by # NameWindowPixmap gets invalidated every time the window's # viewable state changes. ("viewable" here is the X term that # means "mapped, and all ancestors are also mapped".) But # there is no X event that will tell you when a window's # viewability changes! Instead we have to find all ancestors, # and watch all of them for unmap and reparent events. But # what about races? I hear you cry. By doing things in the # exact order: # 1) select for StructureNotify # 2) QueryTree to get parent # 3) repeat 1 & 2 up to the root # 4) call NameWindowPixmap # we are safe. (I think.) listening = [] e = None try: win = get_parent(self._window) while win is not None and win.get_parent() is not None: # We have to use a lowlevel function to manipulate the # event selection here, because SubstructureRedirectMask # does not roundtrip through the GDK event mask # functions. So if we used them, here, we would clobber # corral window selection masks, and those don't deserve # clobbering. They are our friends! X is driving me # slowly mad. addXSelectInput(win, const["StructureNotifyMask"]) add_event_receiver(win, self, max_receivers=-1) listening.append(win) win = get_parent(win) handle = xcomposite_name_window_pixmap(self._window) except Exception, e: try: self._cleanup_listening(listening) except: pass raise if handle is None: log("failed to name a window pixmap for %s: %s", get_xwindow(self._window), e) self._cleanup_listening(listening) else: self._contents_handle = handle # Don't save the listening set until after # NameWindowPixmap has succeeded, to maintain our # invariant: self._listening_to = listening trap.swallow_synced(set_pixmap)
def setup_tray_window(self): display = gtk.gdk.display_get_default() root = gtk.gdk.get_default_root_window() screen = root.get_screen() if TRANSPARENCY: colormap, visual = screen.get_rgba_colormap( ), screen.get_rgba_visual() if colormap is None or visual is None: log.warn("setup tray: using rgb visual fallback") colormap, visual = screen.get_rgb_colormap( ), screen.get_rgb_visual() assert colormap is not None and visual is not None, "failed to obtain visual or colormap" owner = myGetSelectionOwner(root, SELECTION) log("setup tray: current selection owner=%s", owner) if owner != const["XNone"]: raise Exception("%s already owned by %s" % (SELECTION, owner)) self.tray_window = gtk.gdk.Window(root, width=1, height=1, window_type=gtk.gdk.WINDOW_TOPLEVEL, event_mask=0, wclass=gtk.gdk.INPUT_OUTPUT, title="Xpra-SystemTray", visual=visual, colormap=colormap) set_tray_visual(self.tray_window, visual) set_tray_orientation(self.tray_window, TRAY_ORIENTATION_HORZ) log("setup tray: tray window %s", get_xwindow(self.tray_window)) display.request_selection_notification(SELECTION) setsel = mySetSelectionOwner(root, self.tray_window, SELECTION) log("setup tray: set selection owner returned %s", setsel) event_mask = const["StructureNotifyMask"] sendClientMessage(root, root, False, event_mask, "MANAGER", const["CurrentTime"], SELECTION, get_xwindow(self.tray_window), 0, 0) owner = myGetSelectionOwner(root, SELECTION) #FIXME: cleanup if we fail! assert owner == get_xwindow( self.tray_window ), "we failed to get ownership of the tray selection" add_event_receiver(self.tray_window, self) log("setup tray: done")
def test_get_children_and_get_parent_and_reparent(self): d2 = self.clone_display() w1 = self.window(self.display) w2 = self.window(d2) gtk.gdk.flush() assert not l.get_children(w1) children = l.get_children(self.root()) xchildren = map(l.get_xwindow, children) xwins = map(l.get_xwindow, [w1, w2]) # GDK creates an invisible child of the root window on each # connection, so there are some windows we don't know about: for known in xwins: assert known in xchildren assert l.get_parent(w1) == w1.get_parent() w1.reparent(l.get_pywindow(w1, l.get_xwindow(w2)), 0, 0) gtk.gdk.flush() assert map(l.get_xwindow, l.get_children(w2)) == [l.get_xwindow(w1)] assert l.get_parent(w1).xid == w2.xid
def _add_new_or_window(self, raw_window): xid = get_xwindow(raw_window) if raw_window.get_window_type() == gtk.gdk.WINDOW_TEMP: #ignoring one of gtk's temporary windows #all the windows we manage should be gtk.gdk.WINDOW_FOREIGN log("ignoring TEMP window %s", hex(xid)) return WINDOW_MODEL_KEY = "_xpra_window_model_" wid = raw_window.get_data(WINDOW_MODEL_KEY) window = self._id_to_window.get(wid) if window: if window.is_managed(): log("found existing window model %s for %s, will refresh it", type(window), hex(xid)) geometry = window.get_property("geometry") _, _, w, h = geometry self._damage(window, 0, 0, w, h, options={"min_delay": 50}) return log("found existing model %s (but no longer managed!) for %s", type(window), hex(xid)) #TODO: we could try to re-use the existing model and window ID, #but for now it is just easier to create a new one: self._lost_window(window) tray_window = get_tray_window(raw_window) log("Discovered new override-redirect window: %s (tray=%s)", hex(xid), tray_window) try: if tray_window is not None: assert self._tray window = SystemTrayWindowModel(raw_window) wid = self._add_new_window_common(window) raw_window.set_data(WINDOW_MODEL_KEY, wid) window.call_setup() self._send_new_tray_window_packet(wid, window) else: window = OverrideRedirectWindowModel(raw_window) wid = self._add_new_window_common(window) raw_window.set_data(WINDOW_MODEL_KEY, wid) window.call_setup() window.connect("notify::geometry", self._or_window_geometry_changed) window.connect("geometry", self._or_window_geometry_changed) self._send_new_or_window_packet(window) except Unmanageable, e: if window: #if window is set, we failed after instantiating it, #so we need to fail it manually: window.setup_failed(e) if window in self._window_to_id: self._lost_window(window, False) else: log.warn("cannot add %s: %s", hex(xid), e)
def _bell_signaled(self, wm, event): log("bell signaled on window %s", get_xwindow(event.window)) if not self.bell: return wid = 0 if event.window!=gtk.gdk.get_default_root_window() and event.window_model is not None: try: wid = self._window_to_id[event.window_model] except: pass log("_bell_signaled(%s,%r) wid=%s", wm, event, wid) for ss in self._server_sources.values(): ss.bell(wid, event.device, event.percent, event.pitch, event.duration, event.bell_class, event.bell_id, event.bell_name or "")
def setup_tray_window(self): display = gtk.gdk.display_get_default() root = gtk.gdk.get_default_root_window() screen = root.get_screen() if TRANSPARENCY: colormap, visual = screen.get_rgba_colormap(), screen.get_rgba_visual() if colormap is None or visual is None: log.warn("setup tray: using rgb visual fallback") colormap, visual = screen.get_rgb_colormap(), screen.get_rgb_visual() assert colormap is not None and visual is not None, "failed to obtain visual or colormap" owner = myGetSelectionOwner(root, SELECTION) log("setup tray: current selection owner=%s", owner) if owner!=const["XNone"]: raise Exception("%s already owned by %s" % (SELECTION, owner)) self.tray_window = gtk.gdk.Window(root, width=1, height=1, window_type=gtk.gdk.WINDOW_TOPLEVEL, event_mask = 0, wclass=gtk.gdk.INPUT_OUTPUT, title="Xpra-SystemTray", visual=visual, colormap=colormap) set_tray_visual(self.tray_window, visual) set_tray_orientation(self.tray_window, TRAY_ORIENTATION_HORZ) log("setup tray: tray window %s", get_xwindow(self.tray_window)) display.request_selection_notification(SELECTION) setsel = mySetSelectionOwner(root, self.tray_window, SELECTION) log("setup tray: set selection owner returned %s", setsel) event_mask = const["StructureNotifyMask"] sendClientMessage(root, root, False, event_mask, "MANAGER", const["CurrentTime"], SELECTION, get_xwindow(self.tray_window), 0, 0) owner = myGetSelectionOwner(root, SELECTION) #FIXME: cleanup if we fail! assert owner==get_xwindow(self.tray_window), "we failed to get ownership of the tray selection" add_event_receiver(self.tray_window, self) log("setup tray: done")
def _add_new_or_window(self, raw_window): xid = get_xwindow(raw_window) if raw_window.get_window_type()==gtk.gdk.WINDOW_TEMP: #ignoring one of gtk's temporary windows #all the windows we manage should be gtk.gdk.WINDOW_FOREIGN log("ignoring TEMP window %s", hex(xid)) return WINDOW_MODEL_KEY = "_xpra_window_model_" wid = raw_window.get_data(WINDOW_MODEL_KEY) window = self._id_to_window.get(wid) if window: if window.is_managed(): log("found existing window model %s for %s, will refresh it", type(window), hex(xid)) geometry = window.get_property("geometry") _, _, w, h = geometry self._damage(window, 0, 0, w, h, options={"min_delay" : 50}) return log("found existing model %s (but no longer managed!) for %s", type(window), hex(xid)) #TODO: we could try to re-use the existing model and window ID, #but for now it is just easier to create a new one: self._lost_window(window) tray_window = get_tray_window(raw_window) log("Discovered new override-redirect window: %s (tray=%s)", hex(xid), tray_window) try: if tray_window is not None: assert self._tray window = SystemTrayWindowModel(raw_window) wid = self._add_new_window_common(window) raw_window.set_data(WINDOW_MODEL_KEY, wid) window.call_setup() self._send_new_tray_window_packet(wid, window) else: window = OverrideRedirectWindowModel(raw_window) wid = self._add_new_window_common(window) raw_window.set_data(WINDOW_MODEL_KEY, wid) window.call_setup() window.connect("notify::geometry", self._or_window_geometry_changed) window.connect("geometry", self._or_window_geometry_changed) self._send_new_or_window_packet(window) except Unmanageable, e: if window: #if window is set, we failed after instantiating it, #so we need to fail it manually: window.setup_failed(e) if window in self._window_to_id: self._lost_window(window, False) else: log.warn("cannot add %s: %s", hex(xid), e)
def _bell_signaled(self, wm, event): log("bell signaled on window %s", get_xwindow(event.window)) if not self.bell: return wid = 0 if event.window != gtk.gdk.get_default_root_window( ) and event.window_model is not None: try: wid = self._window_to_id[event.window_model] except: pass log("_bell_signaled(%s,%r) wid=%s", wm, event, wid) for ss in self._server_sources.values(): ss.bell(wid, event.device, event.percent, event.pitch, event.duration, event.bell_class, event.bell_id, event.bell_name or "")
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 do_child(disp_name, xwindow1, xwindow2, xwindow3): print("child: in do_child") d2 = gtk.gdk.Display(disp_name) w1on2 = l.get_pywindow(d2, xwindow1) w2on2 = l.get_pywindow(d2, xwindow2) w3on2 = l.get_pywindow(d2, xwindow3) mywin = self.window(d2) print("child: mywin == %s" % l.get_xwindow(mywin)) w1on2.reparent(mywin, 0, 0) w2on2.reparent(mywin, 0, 0) w3on2.reparent(mywin, 0, 0) gtk.gdk.flush() # w1 gets saved: l.XAddToSaveSet(w1on2) # w2 does not # w3 is saved, but then unsaved (to test RemoveFromSaveSet): l.XAddToSaveSet(w3on2) l.XRemoveFromSaveSet(w3on2) gtk.gdk.flush() print("child: finished")
def cleanup(self): log("Tray.cleanup()") root = gtk.gdk.get_default_root_window() owner = myGetSelectionOwner(root, SELECTION) if owner==get_xwindow(self.tray_window): mySetSelectionOwner(root, const["XNone"], SELECTION) else: log.warn("Tray.cleanup() we were no longer the selection owner") remove_event_receiver(self.tray_window, self) def undock(window): log("undocking %s", window) withdraw(window) reparent(window, root, 0, 0) map_raised(window) for window, tray_window in self.tray_windows.items(): trap.swallow_synced(undock, window) tray_window.destroy() self.tray_window.destroy() self.tray_window = None log("Tray.cleanup() done")
def test_configureAndNotify(self): self.conf_ev = None l.substructureRedirect(self.root()) l.add_event_receiver(self.root(), self) # Need to hold onto a handle to this, so connection doesn't get # dropped: client = self.clone_display() w1_client = self.window(client) gtk.gdk.flush() w1_wm = l.get_pywindow(self.display, l.get_xwindow(w1_client)) l.configureAndNotify(w1_client, 11, 12, 13, 14) gtk.main() assert self.conf_ev is not None assert self.conf_ev.delivered_to is self.root() assert self.conf_ev.window is w1_wm assert self.conf_ev.x == 11 assert self.conf_ev.y == 12 assert self.conf_ev.width == 13 assert self.conf_ev.height == 14 assert self.conf_ev.border_width == 0 assert self.conf_ev.value_mask == (l.const["CWX"] | l.const["CWY"] | l.const["CWWidth"] | l.const["CWHeight"] | l.const["CWBorderWidth"]) partial_mask = l.const["CWWidth"] | l.const["CWStackMode"] l.configureAndNotify(w1_client, 11, 12, 13, 14, partial_mask) gtk.main() assert self.conf_ev is not None assert self.conf_ev.delivered_to is self.root() assert self.conf_ev.window is w1_wm assert self.conf_ev.width == 13 assert self.conf_ev.border_width == 0 assert self.conf_ev.value_mask == (l.const["CWWidth"] | l.const["CWBorderWidth"])
def test_substructure_redirect(self): self.map_ev = None self.conf_ev = None root = self.root() d2 = self.clone_display() w2 = self.window(d2) gtk.gdk.flush() w1 = l.get_pywindow(self.display, l.get_xwindow(w2)) l.add_event_receiver(root, self) l.substructureRedirect(root) gtk.gdk.flush() # gdk_window_show does both a map and a configure (to raise the # window) print("showing w2") w2.show() # Can't just call gtk.main() twice, the two events may be delivered # together and processed in a single mainloop iteration. while None in (self.map_ev, self.conf_ev): gtk.main() assert self.map_ev.delivered_to is root assert self.map_ev.window is w1 assert self.conf_ev.delivered_to is root assert self.conf_ev.window is w1 for field in ("x", "y", "width", "height", "border_width", "above", "detail", "value_mask"): print(field) assert hasattr(self.conf_ev, field) self.map_ev = None self.conf_ev = None w2.move_resize(1, 2, 3, 4) gtk.main() assert self.map_ev is None assert self.conf_ev is not None assert self.conf_ev.delivered_to is root assert self.conf_ev.window is w1 assert self.conf_ev.x == 1 assert self.conf_ev.y == 2 assert self.conf_ev.width == 3 assert self.conf_ev.height == 4 assert self.conf_ev.value_mask == (l.const["CWX"] | l.const["CWY"] | l.const["CWWidth"] | l.const["CWHeight"]) self.map_ev = None self.conf_ev = None w2.move(5, 6) gtk.main() assert self.map_ev is None assert self.conf_ev.x == 5 assert self.conf_ev.y == 6 assert self.conf_ev.value_mask == (l.const["CWX"] | l.const["CWY"]) self.map_ev = None self.conf_ev = None w2.raise_() gtk.main() assert self.map_ev is None assert self.conf_ev.detail == l.const["Above"] assert self.conf_ev.value_mask == l.const["CWStackMode"]
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_sendConfigureNotify(self): # GDK discards ConfigureNotify's sent to child windows, so we can't # use self.window(): w1 = gtk.gdk.Window(self.root(), width=10, height=10, window_type=gtk.gdk.WINDOW_TOPLEVEL, wclass=gtk.gdk.INPUT_OUTPUT, event_mask=gtk.gdk.ALL_EVENTS_MASK) self.ev = None def myfilter(ev, data=None): print("ev %s" % (ev.type,)) if ev.type == gtk.gdk.CONFIGURE: self.ev = ev gtk.main_quit() gtk.main_do_event(ev) gtk.gdk.event_handler_set(myfilter) w1.show() gtk.gdk.flush() l.sendConfigureNotify(w1) gtk.main() assert self.ev is not None assert self.ev.type == gtk.gdk.CONFIGURE assert self.ev.window == w1 assert self.ev.send_event assert self.ev.x == 0 assert self.ev.y == 0 assert self.ev.width == 10 assert self.ev.height == 10 # We have to create w2 on a separate connection, because if we just # did w1.reparent(w2, ...), then GDK would magically convert w1 from a # TOPLEVEL window into a CHILD window. # Have to hold onto a reference to d2, so it doesn't get garbage # collected and kill the connection: d2 = self.clone_display() w2 = self.window(d2) gtk.gdk.flush() w2on1 = l.get_pywindow(w1, l.get_xwindow(w2)) # Doesn't generate an event, because event mask is zeroed out. w2.move(11, 12) # Reparenting doesn't trigger a ConfigureNotify. w1.reparent(w2on1, 13, 14) # To double-check that it's still a TOPLEVEL: print(w1.get_window_type()) w1.resize(15, 16) gtk.main() # w1 in root coordinates is now at (24, 26) self.ev = None l.sendConfigureNotify(w1) gtk.main() assert self.ev is not None assert self.ev.type == gtk.gdk.CONFIGURE assert self.ev.window == w1 assert self.ev.send_event assert self.ev.x == 24 assert self.ev.y == 26 assert self.ev.width == 15 assert self.ev.height == 16
def set_tray_window(tray_window, window): tray_window.set_data(XPRA_TRAY_WINDOW_PROPERTY, get_xwindow(window))
_prop_types = { # Python type, X type Atom, format, serializer, deserializer, list # terminator "utf8": (unicode, "UTF8_STRING", 8, lambda disp, u: u.encode("UTF-8"), lambda disp, d: d.decode("UTF-8"), "\0"), # In theory, there should be something clever about COMPOUND_TEXT here. I # am not sufficiently clever to deal with COMPOUNT_TEXT. Even knowing # that Xutf8TextPropertyToTextList exists. "latin1": (unicode, "STRING", 8, lambda disp, u: u.encode("latin1"), lambda disp, d: d.decode("latin1"), "\0"), "atom": (str, "ATOM", 32, lambda disp, a: struct.pack("@I", get_xatom(a)), _get_atom, ""), "u32": ((int, long), "CARDINAL", 32, lambda disp, c: struct.pack("@I", c), lambda disp, d: struct.unpack("@I", d)[0], ""), "window": (gtk.gdk.Window, "WINDOW", 32, lambda disp, c: struct.pack("@I", get_xwindow(c)), lambda disp, d: get_pywindow(disp, struct.unpack("@I", d)[0]), ""), "wm-size-hints": (WMSizeHints, "WM_SIZE_HINTS", 32, unsupported, WMSizeHints, None), "wm-hints": (WMHints, "WM_HINTS", 32, unsupported, WMHints, None), "strut": (NetWMStrut, "CARDINAL", 32, unsupported, NetWMStrut, None), "strut-partial": (NetWMStrut, "CARDINAL", 32, unsupported, NetWMStrut, None), "icon": (cairo.ImageSurface, "CARDINAL", 32, unsupported, NetWMIcons, None), "xsettings-settings": (str, "_XSETTINGS_SETTINGS", 8, lambda disp, d: d, lambda disp, d: d, None), # For uploading ad-hoc instances of the above complex structures to the # server, so we can test reading them out again: "debug-CARDINAL":
def test_save_set(self): w1 = self.window(self.display) w2 = self.window(self.display) w3 = self.window(self.display) gtk.gdk.flush() def do_child(disp_name, xwindow1, xwindow2, xwindow3): print("child: in do_child") d2 = gtk.gdk.Display(disp_name) w1on2 = l.get_pywindow(d2, xwindow1) w2on2 = l.get_pywindow(d2, xwindow2) w3on2 = l.get_pywindow(d2, xwindow3) mywin = self.window(d2) print("child: mywin == %s" % l.get_xwindow(mywin)) w1on2.reparent(mywin, 0, 0) w2on2.reparent(mywin, 0, 0) w3on2.reparent(mywin, 0, 0) gtk.gdk.flush() # w1 gets saved: l.XAddToSaveSet(w1on2) # w2 does not # w3 is saved, but then unsaved (to test RemoveFromSaveSet): l.XAddToSaveSet(w3on2) l.XRemoveFromSaveSet(w3on2) gtk.gdk.flush() print("child: finished") import os print("prefork: ", os.getpid()) pid = os.fork() if not pid: # Child try: print("child: pid ", os.getpid()) name = self.display.get_name() # This is very important, though I don't know why. If we # don't close this display then something inside # xcb_wait_for_reply gets Very Confused and the *parent* # process gets a spurious IO error with nonsense errno # (because errno is not actually being set, because there is # no IO error, just something going weird inside xcb). I'm # not even sure that this actually fixes the underlying # problem, but it makes the test pass, so... self.display.close() do_child(name, l.get_xwindow(w1), l.get_xwindow(w2), l.get_xwindow(w3)) finally: os._exit(0) # Parent print("parent: ", os.getpid()) print("parent: child is ", pid) print("parent: waiting for child") os.waitpid(pid, 0) print("parent: child exited") # Is there a race condition here, where the child exits but the X # server doesn't notice until after we send our commands? print(map(l.get_xwindow, [w1, w2, w3])) print(map(l.get_xwindow, l.get_children(self.root()))) assert w1 in l.get_children(self.root()) assert w2 not in l.get_children(self.root()) assert w3 not in l.get_children(self.root())
# am not sufficiently clever to deal with COMPOUNT_TEXT. Even knowing # that Xutf8TextPropertyToTextList exists. "latin1": (unicode, "STRING", 8, lambda disp, u: u.encode("latin1"), lambda disp, d: d.decode("latin1"), "\0"), "atom": (str, "ATOM", 32, lambda disp, a: struct.pack("=I", get_xatom(a)), _get_atom, ""), "u32": ((int, long), "CARDINAL", 32, lambda disp, c: struct.pack("=I", c), lambda disp, d: struct.unpack("=I", d)[0], ""), "window": (gtk.gdk.Window, "WINDOW", 32, lambda disp, c: struct.pack("=I", get_xwindow(c)), lambda disp, d: get_pywindow(disp, struct.unpack("=I", d)[0]), ""), "wm-size-hints": (WMSizeHints, "WM_SIZE_HINTS", 32, unsupported, WMSizeHints, None), "wm-hints": (WMHints, "WM_HINTS", 32, unsupported, WMHints, None), "strut": (NetWMStrut, "CARDINAL", 32, unsupported, NetWMStrut, None), "strut-partial": (NetWMStrut, "CARDINAL", 32, unsupported, NetWMStrut, None), "icon": (cairo.ImageSurface, "CARDINAL", 32,