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(64, w)) h = max(1, min(64, h)) debug("dock_tray(%s) window=%s, geometry=%s, visual.depth=%s", hex(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()) debug("dock_tray(%s) setting tray properties", hex(xid)) set_tray_window(tray_window, window) tray_window.show() self.tray_windows[window] = tray_window self.window_trays[tray_window] = window debug("dock_tray(%s) resizing and reparenting", hex(xid)) window.resize(w, h) xwin = get_xwindow(window) xtray = get_xwindow(tray_window) X11Window.Withdraw(xwin) X11Window.Reparent(xwin, xtray, 0, 0) X11Window.MapRaised(xwin) debug("dock_tray(%s) new tray container window %s", hex(xid), hex(xtray)) tray_window.invalidate_rect(gtk.gdk.Rectangle(width=w, height=h), True) X11Window.send_xembed_message(xwin, XEMBED_EMBEDDED_NOTIFY, 0, xtray, XEMBED_VERSION)
def send(): root_window = get_xwindow(root) window = get_xwindow(self.gdk_window()) X11WindowBindings.sendClientMessage(root_window, window, False, event_mask, "_NET_WM_DESKTOP", workspace, const["CurrentTime"], 0, 0, 0)
def send_wm_take_focus(target, time): log("sending WM_TAKE_FOCUS: %r, %r", target, time) if time<0: time = 0 #should mean CurrentTime which is better than nothing elif time>0xFFFFFFFF: raise OverflowError("invalid time: %s" % hex(time)) X11Window.sendClientMessage(get_xwindow(target), get_xwindow(target), False, 0, #@UndefinedVariable" "WM_PROTOCOLS", "WM_TAKE_FOCUS", time, 0, 0, 0)
def send_wm_delete_window(target): log("sending WM_DELETE_WINDOW") X11Window.sendClientMessage( get_xwindow(target), get_xwindow(target), False, 0, #@UndefinedVariable" "WM_PROTOCOLS", "WM_DELETE_WINDOW", constants["CurrentTime"], 0, 0, 0) #@UndefinedVariable"
def send_wm_delete_window(target): log("sending WM_DELETE_WINDOW") X11Window.sendClientMessage( get_xwindow(target), get_xwindow(target), False, 0, # @UndefinedVariable" "WM_PROTOCOLS", "WM_DELETE_WINDOW", constants["CurrentTime"], 0, 0, 0, ) # @UndefinedVariable"
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(64, w)) h = max(1, min(64, h)) title = prop_get(window, "_NET_WM_NAME", "utf8", ignore_errors=True) if title is None: title = prop_get(window, "WM_NAME", "latin1", ignore_errors=True) if title is None: title = "" debug( "dock_tray(%s) window=%s, geometry=%s, title=%s, visual.depth=%s", hex(xid), window, window.get_geometry(), title, 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=title, x=-200, y=-200, override_redirect=True, visual=window.get_visual(), colormap=window.get_colormap()) debug("dock_tray(%s) setting tray properties", hex(xid)) set_tray_window(tray_window, window) tray_window.show() self.tray_windows[window] = tray_window self.window_trays[tray_window] = window debug("dock_tray(%s) resizing and reparenting", hex(xid)) window.resize(w, h) xwin = get_xwindow(window) xtray = get_xwindow(tray_window) X11Window.Withdraw(xwin) X11Window.Reparent(xwin, xtray, 0, 0) X11Window.MapRaised(xwin) debug("dock_tray(%s) new tray container window %s", hex(xid), hex(xtray)) tray_window.invalidate_rect(gtk.gdk.Rectangle(width=w, height=h), True) X11Window.send_xembed_message(xwin, XEMBED_EMBEDDED_NOTIFY, 0, xtray, XEMBED_VERSION)
def do_get_property_shm_handle(self, name): if not self._use_shm or not CompositeHelper.XShmEnabled: return None if self._shm_handle and self._shm_handle.get_size( ) != self._window.get_size(): #size has changed! #make sure the current wrapper gets garbage collected: self._shm_handle.cleanup() self._shm_handle = None if self._shm_handle is None: #make a new one: self._shm_handle = XImage.get_XShmWrapper(get_xwindow( self._window)) if self._shm_handle is None: #failed (may retry) return None init_ok, retry_window, xshm_failed = self._shm_handle.setup() if not init_ok: #this handle is not valid, clear it: self._shm_handle = None if not retry_window: #and it looks like it is not worth re-trying this window: self._use_shm = False if xshm_failed: log.warn( "disabling XShm support following irrecoverable error") CompositeHelper.XShmEnabled = False return self._shm_handle
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. X11Window.addXSelectInput( get_xwindow(win), constants["StructureNotifyMask"]) add_event_receiver(win, self, max_receivers=-1) listening.append(win) win = get_parent(win) handle = XImage.get_xcomposite_pixmap( get_xwindow(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 do_get_property_shm_handle(self, name): if not self._use_shm or not CompositeHelper.XShmEnabled: return None if self._shm_handle and self._shm_handle.get_size()!=self._window.get_size(): #size has changed! #make sure the current wrapper gets garbage collected: self._shm_handle.cleanup() self._shm_handle = None if self._shm_handle is None: #make a new one: self._shm_handle = XImage.get_XShmWrapper(get_xwindow(self._window)) if self._shm_handle is None: #failed (may retry) return None init_ok, retry_window, xshm_failed = self._shm_handle.setup() if not init_ok: #this handle is not valid, clear it: self._shm_handle = None if not retry_window: #and it looks like it is not worth re-trying this window: self._use_shm = False if xshm_failed: log.warn("disabling XShm support following irrecoverable error") CompositeHelper.XShmEnabled = False return self._shm_handle
def prop_get(target, key, etype, ignore_errors=False, raise_xerrors=False): if isinstance(etype, list): scalar_type = etype[0] else: scalar_type = etype (_, atom, _, _, _, _) = _prop_types[scalar_type] try: data = trap.call_synced(X11Window.XGetWindowProperty, get_xwindow(target), key, atom) except NoSuchProperty: log.debug("Missing property %s (%s)", key, etype) return None except XError: if raise_xerrors: raise log.info("Missing window %s or wrong property type %s (%s)", target, key, etype, exc_info=True) return None except PropertyError: if not ignore_errors: log.info("Missing property or wrong property type %s (%s)", key, etype, exc_info=True) return None try: return _prop_decode(target, etype, data) except: if not ignore_errors: log.warn("Error parsing property %s (type %s); this may be a" + " misbehaving application, or bug in Xpra\n" + " Data: %r[...?]", key, etype, data[:160], exc_info=True) raise
def prop_get(target, key, etype, ignore_errors=False, raise_xerrors=False): if isinstance(etype, list): scalar_type = etype[0] else: scalar_type = etype (_, atom, _, _, _, _) = _prop_types[scalar_type] try: data = trap.call_synced(X11Window.XGetWindowProperty, get_xwindow(target), key, atom, etype) if data is None: if not ignore_errors: log("Missing property %s (%s)", key, etype) return None except XError: if raise_xerrors: raise log.info("Missing window %s or wrong property type %s (%s)", target, key, etype, exc_info=True) return None except PropertyError: if not ignore_errors: log.info("Missing property or wrong property type %s (%s)", key, etype, exc_info=True) return None try: return _prop_decode(target, etype, data) except: if not ignore_errors: log.warn("Error parsing property %s (type %s); this may be a" + " misbehaving application, or bug in Xpra\n" + " Data: %r[...?]", key, etype, data[:160], exc_info=True) raise
def send_wm_take_focus(target, time): log("sending WM_TAKE_FOCUS: %r, %r", target, time) if time < 0: time = 0 #should mean CurrentTime which is better than nothing elif time > 0xFFFFFFFF: raise OverflowError("invalid time: %s" % hex(time)) X11Window.sendClientMessage( get_xwindow(target), get_xwindow(target), False, 0, #@UndefinedVariable" "WM_PROTOCOLS", "WM_TAKE_FOCUS", time, 0, 0, 0)
def setup(self): xwin = get_xwindow(self._window) if not self._already_composited: X11Window.XCompositeRedirectWindow(xwin) _, _, _, _, self._border_width = X11Window.geometry_with_border(xwin) self.invalidate_pixmap() self._damage_handle = X11Window.XDamageCreate(xwin) log("CompositeHelper.setup() damage handle(%s)=%s", hex(xwin), hex(self._damage_handle)) add_event_receiver(self._window, self)
def cause_badwindow(self): root = self.display.get_default_screen().get_root_window() win = gtk.gdk.Window(root, width=10, height=10, window_type=gtk.gdk.WINDOW_TOPLEVEL, wclass=gtk.gdk.INPUT_OUTPUT, event_mask=0) win.destroy() X11Window.XAddToSaveSet(get_xwindow(win)) return 3
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. X11Window.addXSelectInput(get_xwindow(win), constants["StructureNotifyMask"]) add_event_receiver(win, self, max_receivers=-1) listening.append(win) win = get_parent(win) handle = XImage.get_xcomposite_pixmap(get_xwindow(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 = X11Window.XGetSelectionOwner(SELECTION) debug("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) xtray = get_xwindow(self.tray_window) set_tray_visual(self.tray_window, visual) set_tray_orientation(self.tray_window, TRAY_ORIENTATION_HORZ) debug("setup tray: tray window %s", hex(xtray)) display.request_selection_notification(SELECTION) setsel = X11Window.XSetSelectionOwner(xtray, SELECTION) debug("setup tray: set selection owner returned %s", setsel) event_mask = const["StructureNotifyMask"] xroot = get_xwindow(root) X11Window.sendClientMessage(xroot, xroot, False, event_mask, "MANAGER", const["CurrentTime"], SELECTION, xtray, 0, 0) owner = X11Window.XGetSelectionOwner(SELECTION) #FIXME: cleanup if we fail! assert owner == xtray, "we failed to get ownership of the tray selection" add_event_receiver(self.tray_window, self) debug("setup tray: done")
def cleanup(self): debug("Tray.cleanup()") root = gtk.gdk.get_default_root_window() owner = X11Window.XGetSelectionOwner(SELECTION) if owner==get_xwindow(self.tray_window): X11Window.XSetSelectionOwner(get_xwindow(root), SELECTION) else: log.warn("Tray.cleanup() we were no longer the selection owner") remove_event_receiver(self.tray_window, self) def undock(window): debug("undocking %s", window) X11Window.Withdraw(get_xwindow(window)) X11Window.Reparent(get_xwindow(window), get_xwindow(root), 0, 0) X11Window.MapRaised(get_xwindow(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 system_bell(window, device, percent, pitch, duration, bell_class, bell_id, bell_name): global device_bell if device_bell is None: return False try: xwindow = get_xwindow(window) trap.call_synced(device_bell, xwindow, device, bell_class, bell_id, percent, bell_name) return True except XError, e: log.error("error using device_bell: %s, switching native X11 bell support off", e) device_bell = None return False
def cleanup(self): debug("Tray.cleanup()") root = gtk.gdk.get_default_root_window() owner = X11Window.XGetSelectionOwner(SELECTION) if owner == get_xwindow(self.tray_window): X11Window.XSetSelectionOwner(get_xwindow(root), SELECTION) else: log.warn("Tray.cleanup() we were no longer the selection owner") remove_event_receiver(self.tray_window, self) def undock(window): debug("undocking %s", window) X11Window.Withdraw(get_xwindow(window)) X11Window.Reparent(get_xwindow(window), get_xwindow(root), 0, 0) X11Window.MapRaised(get_xwindow(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 _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 = X11Window.XGetSelectionOwner(SELECTION) debug("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) xtray = get_xwindow(self.tray_window) set_tray_visual(self.tray_window, visual) set_tray_orientation(self.tray_window, TRAY_ORIENTATION_HORZ) debug("setup tray: tray window %s", hex(xtray)) display.request_selection_notification(SELECTION) setsel = X11Window.XSetSelectionOwner(xtray, SELECTION) debug("setup tray: set selection owner returned %s", setsel) event_mask = const["StructureNotifyMask"] xroot = get_xwindow(root) X11Window.sendClientMessage(xroot, xroot, False, event_mask, "MANAGER", const["CurrentTime"], SELECTION, xtray, 0, 0) owner = X11Window.XGetSelectionOwner(SELECTION) #FIXME: cleanup if we fail! assert owner==xtray, "we failed to get ownership of the tray selection" add_event_receiver(self.tray_window, self) debug("setup tray: done")
def do_child_configure_request_event(self, event): # The point of this method is to handle configure requests on # withdrawn windows. We simply allow them to move/resize any way they # want. This is harmless because the window isn't visible anyway (and # apps can create unmapped windows with whatever coordinates they want # anyway, no harm in letting them move existing ones around), and it # means that when the window actually gets mapped, we have more # accurate info on what the app is actually requesting. log("do_child_configure_request_event(%s)", event) if event.window in self._windows: return log("Reconfigure on withdrawn window") trap.swallow_synced(X11Window.configureAndNotify, get_xwindow(event.window), event.x, event.y, event.width, event.height, event.value_mask)
def system_bell(window, device, percent, pitch, duration, bell_class, bell_id, bell_name): global device_bell if device_bell is None: return False try: xwindow = get_xwindow(window) trap.call_synced(device_bell, xwindow, device, bell_class, bell_id, percent, bell_name) return True except XError, e: log.error( "error using device_bell: %s, switching native X11 bell support off", e) device_bell = None return False
def wm_check(display, upgrading=False): #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 = X11Window.XGetSelectionOwner(wm_prop) cwm_so = X11Window.XGetSelectionOwner(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 if upgrading and name and name == WM_WINDOW_NAME: log.info("found previous Xpra instance") else: 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 _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)) #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) 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 do_setup_listening(self): assert self._listening is None add_event_receiver(self._window, self, max_receivers=-1) self._listening = [self._window] #recurse parents: 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. X11Window.addXSelectInput(get_xwindow(win), constants["StructureNotifyMask"]) add_event_receiver(win, self, max_receivers=-1) self._listening.append(win) win = get_parent(win) debug("grab: listening for: %s", self._listening)
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. X11Window.addXSelectInput(get_xwindow(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
def destroy(self): if self._window is None: log.warn("composite window %s already destroyed!", self) return #clear the reference to the window early: win = self._window xwin = get_xwindow(self._window) #Note: invalidate_pixmap()/_cleanup_listening() use self._window, but won't care if it's None self._window = None remove_event_receiver(win, self) self.invalidate_pixmap() if not self._already_composited: trap.swallow_synced(X11Window.XCompositeUnredirectWindow, xwin) if self._damage_handle: trap.swallow_synced(X11Window.XDamageDestroy, self._damage_handle) self._damage_handle = None if self._shm_handle: self._shm_handle.cleanup() self._shm_handle = None #note: this should be redundant since we cleared the #reference to self._window and shortcut out in do_get_property_contents_handle #but it's cheap anyway self.invalidate_pixmap()
def __init__(self, replace_other_wm, display=None): gobject.GObject.__init__(self) 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 = xpra.x11.gtk_x11.selection.ManagerSelection(self._display, "WM_S0") self._cm_wm_selection = xpra.x11.gtk_x11.selection.ManagerSelection(self._display, "_NET_WM_CM_S0") self._wm_selection.connect("selection-lost", self._lost_wm_selection) self._cm_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) self._cm_wm_selection.acquire(mode) # Set up the necessary EWMH properties on the root window. self._setup_ewmh_window() # Start with just one desktop: self.do_desktop_list_changed([u"Main"]) self.set_current_desktop(0) # Start with the full display as workarea: root_w, root_h = gtk.gdk.get_default_root_window().get_size() self.root_set("_NET_SUPPORTED", ["atom"], self._NET_SUPPORTED) self.set_workarea(0, 0, root_w, root_h) self.root_set("_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) X11Window.substructureRedirect(get_xwindow(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 X11Window.is_override_redirect(get_xwindow(w)) and X11Window.is_mapped(get_xwindow(w))): log("Wm managing pre-existing child") self._manage_client(w) # Also watch for focus change events on the root window X11Window.selectFocusChange(get_xwindow(self._root)) X11Keyboard.selectBellNotification(True)
_get_atom, ""), "state": ((int, long), "WM_STATE", 32, lambda disp, c: struct.pack("=I", c), lambda disp, d: struct.unpack("=I", d)[0], ""), "u32": ((int, long), "CARDINAL", 32, lambda disp, c: struct.pack("=I", c), lambda disp, d: struct.unpack("=I", d)[0], ""), "visual": (gtk.gdk.Visual, "VISUALID", 32, lambda disp, c: struct.pack("=I", get_xvisual(c)), unsupported, ""), "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), "motif-hints": (MotifWMHints, "_MOTIF_WM_HINTS", 32,
def acquire(self, when): old_owner = self._owner() if when is self.IF_UNOWNED and old_owner != constants["XNone"]: raise AlreadyOwned self.clipboard.set_with_data([("VERSION", 0, 0)], self._get, self._clear, None) # Having acquired the selection, we have to announce our existence # (ICCCM 2.8, still). The details here probably don't matter too # much; I've never heard of an app that cares about these messages, # and metacity actually gets the format wrong in several ways (no # MANAGER or owner_window atoms). But might as well get it as right # as possible. # To announce our existence, we need: # -- the timestamp we arrived at # -- the manager selection atom # -- the window that registered the selection # Of course, because Gtk is doing so much magic for us, we have to do # some weird tricks to get at these. # Ask ourselves when we acquired the selection: ts_data = self.clipboard.wait_for_contents("TIMESTAMP").data #data is a timestamp, X11 datatype is Time which is CARD32, #(which is 64 bits on 64-bit systems!) Lsize = calcsize("@L") if len(ts_data) == Lsize: ts_num = unpack("@L", ts_data[:Lsize])[0] else: ts_num = 0 #CurrentTime log.warn("invalid data for 'TIMESTAMP': %s", ([hex(ord(x)) for x in ts_data])) # Calculate the X atom for this selection: selection_xatom = get_xatom(self.atom) # Ask X what window we used: self._xwindow = X11Window.XGetSelectionOwner(self.atom) root = self.clipboard.get_display().get_default_screen( ).get_root_window() xroot = get_xwindow(root) X11Window.sendClientMessage(xroot, xroot, False, constants["StructureNotifyMask"], "MANAGER", ts_num, selection_xatom, self._xwindow, 0, 0) if old_owner != constants["XNone"] and when is self.FORCE: # Block in a recursive mainloop until the previous owner has # cleared out. def getwin(): window = get_pywindow(self.clipboard, old_owner) window.set_events(window.get_events() | gtk.gdk.STRUCTURE_MASK) return window try: window = trap.call_synced(getwin) log("got window") except XError: log("Previous owner is already gone, not blocking") else: log("Waiting for previous owner to exit...") add_event_receiver(window, self) gtk.main() log("...they did.") window = get_pywindow(self.clipboard, self._xwindow) window.set_title("Xpra-ManagerSelection")
def prop_set(target, key, etype, value): with xsync: X11Window.XChangeProperty(get_xwindow(target), key, _prop_encode(target, etype, value))
def set_tray_window(tray_window, window): tray_window.set_data(XPRA_TRAY_WINDOW_PROPERTY, get_xwindow(window))
def undock(window): debug("undocking %s", window) X11Window.Withdraw(get_xwindow(window)) X11Window.Reparent(get_xwindow(window), get_xwindow(root), 0, 0) X11Window.MapRaised(get_xwindow(window))
lambda disp, d: d.decode("latin1"), "\0"), "atom": (str, "ATOM", 32, lambda disp, a: struct.pack("@I", get_xatom(a)), _get_atom, ""), "state": ((int, long), "WM_STATE", 32, lambda disp, c: struct.pack("=I", c), lambda disp, d: struct.unpack("=I", d)[0], ""), "u32": ((int, long), "CARDINAL", 32, lambda disp, c: struct.pack("=I", c), lambda disp, d: struct.unpack("=I", d)[0], ""), "integer": ((int, long), "INTEGER", 32, lambda disp, c: struct.pack("=I", c), lambda disp, d: struct.unpack("=I", d)[0], ""), "visual": (gtk.gdk.Visual, "VISUALID", 32, lambda disp, c: struct.pack("=I", get_xvisual(c)), unsupported, ""), "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]), ""), "strut": (NetWMStrut, "CARDINAL", 32, unsupported, NetWMStrut, None), "strut-partial": (NetWMStrut, "CARDINAL", 32, unsupported, NetWMStrut, None), "motif-hints": (MotifWMHints, "_MOTIF_WM_HINTS", 32, unsupported, MotifWMHints, None), "icon": (cairo.ImageSurface, "CARDINAL", 32, unsupported, NetWMIcons, None), "xsettings-settings": (tuple, "_XSETTINGS_SETTINGS", 8, set_settings, get_settings, None), # For uploading ad-hoc instances of the above complex structures to the # server, so we can test reading them out again: "debug-CARDINAL": (str, "CARDINAL", 32, lambda disp, c: c, lambda disp, d: d, None),
def prop_set(target, key, etype, value): trap.call_synced(X11Window.XChangeProperty, get_xwindow(target), key, _prop_encode(target, etype, value))
def acquire(self, when): old_owner = self._owner() if when is self.IF_UNOWNED and old_owner != const["XNone"]: raise AlreadyOwned self.clipboard.set_with_data([("VERSION", 0, 0)], self._get, self._clear, None) # Having acquired the selection, we have to announce our existence # (ICCCM 2.8, still). The details here probably don't matter too # much; I've never heard of an app that cares about these messages, # and metacity actually gets the format wrong in several ways (no # MANAGER or owner_window atoms). But might as well get it as right # as possible. # To announce our existence, we need: # -- the timestamp we arrived at # -- the manager selection atom # -- the window that registered the selection # Of course, because Gtk is doing so much magic for us, we have to do # some weird tricks to get at these. # Ask ourselves when we acquired the selection: ts_data = self.clipboard.wait_for_contents("TIMESTAMP").data #data is a timestamp, X11 datatype is Time which is CARD32, #(which is 64 bits on 64-bit systems!) Lsize = calcsize("@L") if len(ts_data)==Lsize: ts_num = unpack("@L", ts_data[:Lsize])[0] else: ts_num = 0 #CurrentTime log.warn("invalid data for 'TIMESTAMP': %s", ([hex(ord(x)) for x in ts_data])) # Calculate the X atom for this selection: selection_xatom = get_xatom(self.atom) # Ask X what window we used: self._xwindow = X11Window.XGetSelectionOwner(self.atom) root = self.clipboard.get_display().get_default_screen().get_root_window() xroot = get_xwindow(root) X11Window.sendClientMessage(xroot, xroot, False, const["StructureNotifyMask"], "MANAGER", ts_num, selection_xatom, self._xwindow, 0, 0) if old_owner != const["XNone"] and when is self.FORCE: # Block in a recursive mainloop until the previous owner has # cleared out. def getwin(): window = get_pywindow(self.clipboard, old_owner) window.set_events(window.get_events() | gtk.gdk.STRUCTURE_MASK) return window try: window = trap.call_synced(getwin) log("got window") except XError: log("Previous owner is already gone, not blocking") else: log("Waiting for previous owner to exit...") add_event_receiver(window, self) gtk.main() log("...they did.") window = get_pywindow(self.clipboard, self._xwindow) window.set_title("Xpra-ManagerSelection")
def send(): root_window = get_xwindow(root) window = get_xwindow(self.gdk_window()) X11WindowBindings.sendClientMessage( root_window, window, False, event_mask, "_NET_WM_DESKTOP", workspace, constants["CurrentTime"], 0, 0, 0 )
class XpraServer(gobject.GObject, X11ServerBase): __gsignals__ = { "xpra-child-map-event": one_arg_signal, "xpra-cursor-event": one_arg_signal, } def __init__(self): gobject.GObject.__init__(self) X11ServerBase.__init__(self) def init(self, clobber, opts): X11ServerBase.init(self, clobber, opts) def x11_init(self): X11ServerBase.x11_init(self) init_x11_filter() self._has_focus = 0 # Do this before creating the Wm object, to avoid clobbering its # selecting SubstructureRedirect. root = gtk.gdk.get_default_root_window() root.set_events(root.get_events() | gtk.gdk.SUBSTRUCTURE_MASK) root.property_change(gtk.gdk.atom_intern("XPRA_SERVER", False), gtk.gdk.atom_intern("STRING", False), 8, gtk.gdk.PROP_MODE_REPLACE, xpra.__version__) add_event_receiver(root, self) ### Create the WM object self._wm = Wm(self.clobber) self._wm.connect("new-window", self._new_window_signaled) self._wm.connect("bell", self._bell_signaled) self._wm.connect("quit", lambda _: self.quit(True)) self.default_cursor_data = None self.last_cursor_serial = None self.send_cursor_pending = False self.cursor_data = None self.cursor_sizes = None def get_default_cursor(): self.default_cursor_data = X11Keyboard.get_cursor_image() log("get_default_cursor=%s", self.default_cursor_data) trap.swallow_synced(get_default_cursor) self._wm.enableCursors(True) def make_hello(self): capabilities = X11ServerBase.make_hello(self) capabilities["window.raise"] = True capabilities["pointer.grabs"] = True return capabilities def do_get_info(self, proto, server_sources, window_ids): info = X11ServerBase.do_get_info(self, proto, server_sources, window_ids) log("do_get_info: adding cursor=%s", self.cursor_data) #copy to prevent race: cd = self.cursor_data if cd is None: info["cursor"] = "None" else: info["cursor.is_default"] = bool(self.default_cursor_data and len(self.default_cursor_data)>=8 and len(cd)>=8 and cd[7]==cd[7]) #all but pixels: i = 0 for x in ("x", "y", "width", "height", "xhot", "yhot", "serial", None, "name"): if x: v = cd[i] or "" info["cursor." + x] = v i += 1 return info def get_ui_info(self, proto, wids, *args): info = X11ServerBase.get_ui_info(self, proto, wids, *args) #now cursor size info: display = gtk.gdk.display_get_default() for prop, size in {"default" : display.get_default_cursor_size(), "max" : display.get_maximal_cursor_size()}.items(): if size is None: continue info["cursor.%s_size" % prop] = size return info def get_window_info(self, window): info = X11ServerBase.get_window_info(self, window) info["focused"] = self._window_to_id.get(window, -1)==self._has_focus return info def set_workarea(self, workarea): self._wm.set_workarea(workarea.x, workarea.y, workarea.width, workarea.height) def get_transient_for(self, window): return self._desktop_manager.get_transient_for(window, self._window_to_id) def is_shown(self, window): return self._desktop_manager.is_shown(window) def cleanup(self, *args): if self._tray: self._tray.cleanup() self._tray = None X11ServerBase.cleanup(self) def load_existing_windows(self, system_tray): # Tray handler: self._tray = None if system_tray: try: self._tray = SystemTray() except Exception, e: log.error("cannot setup tray forwarding: %s", e, exc_info=True) ### Create our window managing data structures: self._desktop_manager = DesktopManager() self._wm.get_property("toplevel").add(self._desktop_manager) self._desktop_manager.show_all() ### Load in existing windows: for window in self._wm.get_property("windows"): self._add_new_window(window) root = gtk.gdk.get_default_root_window() for window in get_children(root): if X11Window.is_override_redirect(get_xwindow(window)) and X11Window.is_mapped(get_xwindow(window)): self._add_new_or_window(window)
def __init__(self, replace_other_wm, display=None): gobject.GObject.__init__(self) 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 = xpra.x11.gtk_x11.selection.ManagerSelection( self._display, "WM_S0") self._cm_wm_selection = xpra.x11.gtk_x11.selection.ManagerSelection( self._display, "_NET_WM_CM_S0") self._wm_selection.connect("selection-lost", self._lost_wm_selection) self._cm_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) self._cm_wm_selection.acquire(mode) # Set up the necessary EWMH properties on the root window. self._setup_ewmh_window() # Start with just one desktop: self.do_desktop_list_changed([u"Main"]) self.set_current_desktop(0) # Start with the full display as workarea: root_w, root_h = gtk.gdk.get_default_root_window().get_size() self.root_set("_NET_SUPPORTED", ["atom"], self._NET_SUPPORTED) self.set_workarea(0, 0, root_w, root_h) self.root_set("_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) X11Window.substructureRedirect(get_xwindow(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 X11Window.is_override_redirect(get_xwindow(w)) and X11Window.is_mapped(get_xwindow(w))): log("Wm managing pre-existing child") self._manage_client(w) # Also watch for focus change events on the root window X11Window.selectFocusChange(get_xwindow(self._root)) X11Keyboard.selectBellNotification(True)
def wm_check(display, upgrading=False): #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 = X11Window.XGetSelectionOwner(wm_prop) cwm_so = X11Window.XGetSelectionOwner(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 if upgrading and name and name==WM_WINDOW_NAME: log.info("found previous Xpra instance") else: 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