def setup(self): super().setup() ox, oy, ow, oh = self.client_window.get_geometry()[:4] # We enable PROPERTY_CHANGE_MASK so that we can call # x11_get_server_time on this window. # clamp this window to the desktop size: x, y = self._clamp_to_desktop(ox, oy, ow, oh) self.corral_window = GDKX11Window( self.parking_window, x=x, y=y, width=ow, height=oh, window_type=Gdk.WindowType.CHILD, event_mask=Gdk.EventMask.PROPERTY_CHANGE_MASK, title="CorralWindow-%#x" % self.xid) cxid = self.corral_window.get_xid() log("setup() corral_window=%#x", cxid) prop_set(self.corral_window, "_NET_WM_NAME", "utf8", "Xpra-CorralWindow-%#x" % self.xid) X11Window.substructureRedirect(cxid) add_event_receiver(self.corral_window, self) # The child might already be mapped, in case we inherited it from # a previous window manager. If so, we unmap it now, and save the # serial number of the request -- this way, when we get an # UnmapNotify later, we'll know that it's just from us unmapping # the window, not from the client withdrawing the window. if X11Window.is_mapped(self.xid): log("hiding inherited window") self.last_unmap_serial = X11Window.Unmap(self.xid) log("setup() adding to save set") X11Window.XAddToSaveSet(self.xid) self.in_save_set = True log("setup() reparenting") X11Window.Reparent(self.xid, cxid, 0, 0) self.client_reparented = True geomlog("setup() geometry") geom = X11Window.geometry_with_border(self.xid) if geom is None: raise Unmanageable("window %#x disappeared already" % self.xid) w, h = geom[2:4] hints = self.get_property("size-hints") geomlog("setup() hints=%s size=%ix%i", hints, w, h) nw, nh = self.calc_constrained_size(w, h, hints) self._updateprop("geometry", (x, y, nw, nh)) geomlog("setup() resizing windows to %sx%s", nw, nh) #don't trigger a resize unless we have to: if ow != nw or oh != nh: self.corral_window.resize(nw, nh) if w != nw or h != nh: self.client_window.resize(nw, nh) self.client_window.show_unraised() #this is here to trigger X11 errors if any are pending #or if the window is deleted already: self.client_window.get_geometry()
def set_icc_profile(self): if not SYNC_ICC: return ui_clients = [s for s in self._server_sources.values() if s.ui_client] if len(ui_clients) != 1: screenlog("%i UI clients, not setting ICC profile") self.reset_icc_profile() return icc = ui_clients[0].icc data = None for x in ("data", "icc-data", "icc-profile"): if x in icc: data = icc.get(x) break if not data: screenlog("no icc data found in %s", icc) self.reset_icc_profile() return import binascii screenlog("set_icc_profile() icc data for %s: %s (%i bytes)", ui_clients[0], binascii.hexlify(data or ""), len(data or "")) from xpra.x11.gtk_x11.prop import prop_set #each CARD32 contains just one 8-bit value - don't ask me why prop_set(self.root_window, "_ICC_PROFILE", ["u32"], [ord(x) for x in data]) prop_set(self.root_window, "_ICC_PROFILE_IN_X_VERSION", "u32", 0 * 100 + 4) #0.4 -> 0*100+4*1
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._wm_name, ) prop_set(self._ewmh_window, "_NET_SUPPORTING_WM_CHECK", "window", self._ewmh_window) self.root_set("_NET_SUPPORTING_WM_CHECK", "window", self._ewmh_window) self.root_set("_NET_WM_NAME", "utf8", self._wm_name.decode("utf8"))
def set_icc_profile(self): if not SYNC_ICC: return ui_clients = [s for s in self._server_sources.values() if s.ui_client] if len(ui_clients) != 1: screenlog("%i UI clients, resetting ICC profile to default", len(ui_clients)) self.reset_icc_profile() return icc = typedict(ui_clients[0].icc) data = None for x in ("data", "icc-data", "icc-profile"): data = icc.strget(x) if data: break if not data: screenlog("no icc data found in %s", icc) self.reset_icc_profile() return screenlog("set_icc_profile() icc data for %s: %s (%i bytes)", ui_clients[0], hexstr(data or ""), len(data or "")) self.icc_profile = data from xpra.x11.gtk_x11.prop import prop_set prop_set(self.root_window, "_ICC_PROFILE", ["u32"], [ord(x) for x in data]) prop_set(self.root_window, "_ICC_PROFILE_IN_X_VERSION", "u32", 0 * 100 + 4) #0.4 -> 0*100+4*1
def do_xpra_client_message_event(self, event): # FIXME # Need to listen for: # _NET_ACTIVE_WINDOW # _NET_CURRENT_DESKTOP # _NET_WM_PING responses # and maybe: # _NET_RESTACK_WINDOW # _NET_WM_STATE log("do_xpra_client_message_event(%s)", event) if event.message_type=="_NET_SHOWING_DESKTOP": show = bool(event.data[0]) self.emit("show-desktop", show) elif event.message_type=="_NET_REQUEST_FRAME_EXTENTS": #if we're here, that means the window model does not exist #(or it would have processed the event) #so this must be a an unmapped window frame = (0, 0, 0, 0) if not X11Window.is_override_redirect(event.window.xid): #use the global default: frame = prop_get(self._root, "DEFAULT_NET_FRAME_EXTENTS", ["u32"], ignore_errors=True) if not frame: #fallback: frame = (0, 0, 0, 0) log("_NET_REQUEST_FRAME_EXTENTS: setting _NET_FRAME_EXTENTS=%s on %#x", frame, event.window.xid) with xswallow: prop_set(event.window, "_NET_FRAME_EXTENTS", ["u32"], frame)
def set_settings(self, settings): if type(settings)==list: settings = tuple(settings) elif type(settings)!=tuple: log.warn("discarding xsettings because of incompatible format: %s", type(settings)) return prop_set(self._window, XSETTINGS, XSETTINGS_TYPE, settings)
def _set_gtk_x11_window_menu(add, wid, window, menus, application_action_callback=None, window_action_callback=None): from xpra.x11.dbus.menu import setup_dbus_window_menu from xpra.x11.gtk_x11.prop import prop_set, prop_del window_props = setup_dbus_window_menu(add, wid, menus, application_action_callback, window_action_callback) #window_props may contains X11 window properties we have to clear or set if not window_props: return if not window: #window has already been closed #(but we still want to call setup_dbus_window_menu above to ensure we clear things up!) return menulog("will set/remove the following window properties for wid=%i: %s", wid, window_props) try: from xpra.gtk_common.error import xsync with xsync: for k, v in window_props.items(): if v is None: prop_del(window, k) else: vtype, value = v prop_set(window, k, vtype, value) except Exception as e: menulog.error("Error setting menu window properties:") menulog.error(" %s", e)
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._wm_name) prop_set(self._ewmh_window, "_NET_SUPPORTING_WM_CHECK", "window", self._ewmh_window) self.root_set("_NET_SUPPORTING_WM_CHECK", "window", self._ewmh_window) self.root_set("_NET_WM_NAME", "utf8", self._wm_name.decode("utf8"))
def do_xpra_client_message_event(self, event): # FIXME # Need to listen for: # _NET_ACTIVE_WINDOW # _NET_CURRENT_DESKTOP # _NET_WM_PING responses # and maybe: # _NET_RESTACK_WINDOW # _NET_WM_STATE log("do_xpra_client_message_event(%s)", event) if event.message_type == "_NET_SHOWING_DESKTOP": show = bool(event.data[0]) self.emit("show-desktop", show) elif event.message_type == "_NET_REQUEST_FRAME_EXTENTS" and FRAME_EXTENTS: #if we're here, that means the window model does not exist #(or it would have processed the event) #so this must be a an unmapped window frame = (0, 0, 0, 0) with xswallow: if not X11Window.is_override_redirect(get_xwindow( event.window)): #use the global default: frame = prop_get(self._root, "DEFAULT_NET_FRAME_EXTENTS", ["u32"], ignore_errors=True) if not frame: #fallback: frame = (0, 0, 0, 0) framelog( "_NET_REQUEST_FRAME_EXTENTS: setting _NET_FRAME_EXTENTS=%s on %#x", frame, get_xwindow(event.window)) prop_set(event.window, "_NET_FRAME_EXTENTS", ["u32"], frame)
def init_randr(self): self.randr = RandR.has_randr() screenlog("randr=%s", self.randr) #check the property first, #because we may be inheriting this display, #in which case the screen sizes list may be longer than 1 eprop = prop_get(self.root_window, "_XPRA_RANDR_EXACT_SIZE", "u32", ignore_errors=True, raise_xerrors=False) screenlog("_XPRA_RANDR_EXACT_SIZE=%s", eprop) self.randr_exact_size = eprop == 1 if not self.randr_exact_size: #ugly hackish way of detecting Xvfb with randr, #assume that it has only one resolution pre-defined: sizes = RandR.get_xrr_screen_sizes() if len(sizes) == 1: self.randr_exact_size = True prop_set(self.root_window, "_XPRA_RANDR_EXACT_SIZE", "u32", 1) elif not sizes: #xwayland? self.randr = False self.randr_exact_size = False screenlog("randr=%s, exact size=%s", self.randr, self.randr_exact_size) display = display_get_default() i = 0 while i < display.get_n_screens(): screen = display.get_screen(i) screen.connect("size-changed", self._screen_size_changed) i += 1 screenlog("randr enabled: %s", self.randr) if not self.randr: screenlog.warn("Warning: no X11 RandR support on %s", os.environ.get("DISPLAY"))
def _sync_allowed_actions(self, *args): actions = self.get_property("allowed-actions") or [] metalog( "sync_allowed_actions: setting _NET_WM_ALLOWED_ACTIONS=%s on %#x", actions, self.xid) with xswallow: prop_set(self.client_window, "_NET_WM_ALLOWED_ACTIONS", ["atom"], actions)
def set_bypass_compositor(self, v): if not HAS_X11_BINDINGS: return if v not in (0, 1, 2): v = 0 if not self.is_realized(): self.realize() prop_set(self.get_window(), "_NET_WM_BYPASS_COMPOSITOR", "u32", v)
def set_settings(self, settings): if type(settings) == list: settings = tuple(settings) elif type(settings) != tuple: log.warn("discarding xsettings because of incompatible format: %s", type(settings)) return prop_set(self._window, XSETTINGS, XSETTINGS_TYPE, settings)
def setup(self): super(WindowModel, self).setup() x, y, w, h, _ = self.client_window.get_geometry() # We enable PROPERTY_CHANGE_MASK so that we can call # x11_get_server_time on this window. self.corral_window = gtk.gdk.Window( self.parking_window, x=x, y=y, width=w, height=h, window_type=gtk.gdk.WINDOW_CHILD, wclass=gtk.gdk.INPUT_OUTPUT, event_mask=gtk.gdk.PROPERTY_CHANGE_MASK, title="CorralWindow-%#x" % self.xid) log("setup() corral_window=%#x", self.corral_window.xid) prop_set(self.corral_window, "_NET_WM_NAME", "utf8", u"Xpra-CorralWindow-%#x" % self.xid) X11Window.substructureRedirect(self.corral_window.xid) add_event_receiver(self.corral_window, self) # The child might already be mapped, in case we inherited it from # a previous window manager. If so, we unmap it now, and save the # serial number of the request -- this way, when we get an # UnmapNotify later, we'll know that it's just from us unmapping # the window, not from the client withdrawing the window. if X11Window.is_mapped(self.xid): log("hiding inherited window") self.last_unmap_serial = X11Window.Unmap(self.xid) log("setup() adding to save set") X11Window.XAddToSaveSet(self.xid) self.in_save_set = True log("setup() reparenting") X11Window.Reparent(self.xid, self.corral_window.xid, 0, 0) self.client_reparented = True log("setup() geometry") w, h = X11Window.geometry_with_border(self.xid)[2:4] hints = self.get_property("size-hints") log("setup() hints=%s size=%ix%i", hints, w, h) nw, nh = calc_constrained_size(w, h, hints) if nw >= MAX_WINDOW_SIZE or nh >= MAX_WINDOW_SIZE: #we can't handle windows that big! raise Unmanageable( "window constrained size is too large: %sx%s (from client geometry: %s,%s with size hints=%s)" % (nw, nh, w, h, hints)) self._updateprop("geometry", (x, y, nw, nh)) log("setup() resizing windows to %sx%s", nw, nh) self.corral_window.resize(nw, nh) self.client_window.resize(nw, nh) self.client_window.show_unraised() #this is here to trigger X11 errors if any are pending #or if the window is deleted already: self.client_window.get_geometry()
def show(self): if self.group_leader: if not self.is_realized(): self.realize() self.window.set_group(self.group_leader) gtk.Window.show(self) #now it is realized, we can set WM_COMMAND (for X11 clients only) command = self._metadata.strget("command") if command and HAS_X11_BINDINGS: prop_set(self.get_window(), "WM_COMMAND", "latin1", command.decode("latin1"))
def set_settings(self, settings): if isinstance(settings, list): settings = tuple(settings) elif not isinstance(settings, tuple): log.warn("Warning: discarding xsettings because of incompatible format: %s", type(settings)) return try: prop_set(self._window, XSETTINGS, XSETTINGS_TYPE, settings) except XError as e: log.error("Error: XSettings not applied") log.error(" %s", e)
def _sync_frame(self, *args): v = self.get_property("frame") framelog("sync_frame: frame(%#x)=%s", self.xid, v) if not v and (not self.is_OR() and not self.is_tray()): root = self.client_window.get_screen().get_root_window() v = prop_get(root, "DEFAULT_NET_FRAME_EXTENTS", ["u32"], ignore_errors=True) if not v: #default for OR, or if we don't have any other value: v = (0, 0, 0, 0) framelog("sync_frame: setting _NET_FRAME_EXTENTS=%s on %#x", v, self.xid) with xswallow: prop_set(self.client_window, "_NET_FRAME_EXTENTS", ["u32"], v)
def setup(self): super(WindowModel, self).setup() x, y, w, h, _ = self.client_window.get_geometry() # We enable PROPERTY_CHANGE_MASK so that we can call # x11_get_server_time on this window. self.corral_window = gtk.gdk.Window(self.parking_window, x = x, y = y, width =w, height= h, window_type=gtk.gdk.WINDOW_CHILD, wclass=gtk.gdk.INPUT_OUTPUT, event_mask=gtk.gdk.PROPERTY_CHANGE_MASK, title = "CorralWindow-%#x" % self.xid) log("setup() corral_window=%#x", self.corral_window.xid) prop_set(self.corral_window, "_NET_WM_NAME", "utf8", u"Xpra-CorralWindow-%#x" % self.xid) X11Window.substructureRedirect(self.corral_window.xid) add_event_receiver(self.corral_window, self) # The child might already be mapped, in case we inherited it from # a previous window manager. If so, we unmap it now, and save the # serial number of the request -- this way, when we get an # UnmapNotify later, we'll know that it's just from us unmapping # the window, not from the client withdrawing the window. if X11Window.is_mapped(self.xid): log("hiding inherited window") self.last_unmap_serial = X11Window.Unmap(self.xid) log("setup() adding to save set") X11Window.XAddToSaveSet(self.xid) self.in_save_set = True log("setup() reparenting") X11Window.Reparent(self.xid, self.corral_window.xid, 0, 0) self.client_reparented = True log("setup() geometry") w, h = X11Window.geometry_with_border(self.xid)[2:4] hints = self.get_property("size-hints") log("setup() hints=%s size=%ix%i", hints, w, h) nw, nh = calc_constrained_size(w, h, hints) if nw>=MAX_WINDOW_SIZE or nh>=MAX_WINDOW_SIZE: #we can't handle windows that big! raise Unmanageable("window constrained size is too large: %sx%s (from client geometry: %s,%s with size hints=%s)" % (nw, nh, w, h, hints)) self._updateprop("geometry", (x, y, nw, nh)) log("setup() resizing windows to %sx%s", nw, nh) self.corral_window.resize(nw, nh) self.client_window.resize(nw, nh) self.client_window.show_unraised() #this is here to trigger X11 errors if any are pending #or if the window is deleted already: self.client_window.get_geometry()
def move_to_workspace(self, workspace : int): #we send a message to ourselves, we could also just update the property current = self.get_property("workspace") if current==workspace: workspacelog("move_to_workspace(%s) unchanged", workspacestr(workspace)) return workspacelog("move_to_workspace(%s) current=%s", workspacestr(workspace), workspacestr(current)) with xswallow: if workspace==WORKSPACE_UNSET: workspacelog("removing _NET_WM_DESKTOP property from window %#x", self.xid) prop_del(self.client_window, "_NET_WM_DESKTOP") else: workspacelog("setting _NET_WM_DESKTOP=%s on window %#x", workspacestr(workspace), self.xid) prop_set(self.client_window, "_NET_WM_DESKTOP", "u32", workspace)
def move_to_workspace(self, workspace): #we send a message to ourselves, we could also just update the property current = self.get_property("workspace") if current==workspace: workspacelog("move_to_workspace(%s) unchanged", workspacestr(workspace)) return workspacelog("move_to_workspace(%s) current=%s", workspacestr(workspace), workspacestr(current)) with xswallow: if workspace==WORKSPACE_UNSET: workspacelog("removing _NET_WM_DESKTOP property from window %#x", self.xid) X11Window.XDeleteProperty(self.xid, "_NET_WM_DESKTOP") else: workspacelog("setting _NET_WM_DESKTOP=%s on window %#x", workspacestr(workspace), self.xid) prop_set(self.client_window, "_NET_WM_DESKTOP", "u32", workspace)
def set_workspace(self, workspace): if not self._can_set_workspace: return None desktop = self.get_desktop_workspace() if self._been_mapped and (workspace == desktop or desktop is None): #window is back in view self._client.control_refresh(self._id, False, False) ndesktops = self.get_workspace_count() if ndesktops is None or ndesktops <= 1: workspacelog( "number of desktops not defined, cannot set workspace") return None if workspace < 0: #this should not happen, workspace is unsigned! (CARDINAL) workspace = WORKSPACE_UNSET workspacelog.warn("invalid workspace number: %s", self._window_workspace) workspacelog("%s.set_workspace() workspace=%s ndesktops=%s", self, workspace, ndesktops) #we will need the gdk window: if not self.is_realized(): self.realize() gdkwin = self.get_window() if workspace == WORKSPACE_UNSET: #we want to remove the setting, so access the window property directly: self._window_workspace = workspace with xswallow: X11Window.XDeleteProperty(gdkwin.xid, "_NET_WM_DESKTOP") return self._window_workspace #clamp to number of workspaces just in case we have a mismatch: if workspace == WORKSPACE_ALL: self._window_workspace = WORKSPACE_ALL else: self._window_workspace = max(0, min(ndesktops - 1, workspace)) workspacelog("%s.set_workspace() clamped workspace=%s", self, self._window_workspace) if not gdkwin.is_visible(): #window is unmapped so we can set the window property directly: prop_set(self.get_window(), "_NET_WM_DESKTOP", "u32", self._window_workspace) return self._window_workspace #the window is visible, so we have to ask the window manager politely with xsync: send_wm_workspace(root, self.get_window(), self._window_workspace) return self._window_workspace
def set_workspace(self, workspace): if not self._can_set_workspace: return None desktop = self.get_desktop_workspace() if self._been_mapped and (workspace==desktop or desktop is None): #window is back in view self._client.control_refresh(self._id, False, False) ndesktops = self.get_workspace_count() if ndesktops is None or ndesktops<=1: workspacelog("number of desktops not defined, cannot set workspace") return None if workspace<0: #this should not happen, workspace is unsigned! (CARDINAL) workspace = WORKSPACE_UNSET workspacelog.warn("invalid workspace number: %s", self._window_workspace) workspacelog("%s.set_workspace() workspace=%s ndesktops=%s", self, workspace, ndesktops) #we will need the gdk window: if not self.is_realized(): self.realize() gdkwin = self.get_window() if workspace==WORKSPACE_UNSET: #we want to remove the setting, so access the window property directly: self._window_workspace = workspace with xswallow: X11Window.XDeleteProperty(gdkwin.xid, "_NET_WM_DESKTOP") return self._window_workspace #clamp to number of workspaces just in case we have a mismatch: if workspace==WORKSPACE_ALL: self._window_workspace = WORKSPACE_ALL else: self._window_workspace = max(0, min(ndesktops-1, workspace)) workspacelog("%s.set_workspace() clamped workspace=%s", self, self._window_workspace) if not gdkwin.is_visible(): #window is unmapped so we can set the window property directly: prop_set(self.get_window(), "_NET_WM_DESKTOP", "u32", self._window_workspace) return self._window_workspace #the window is visible, so we have to ask the window manager politely with xsync: send_wm_workspace(root, self.get_window(), self._window_workspace) return self._window_workspace
def set_strut(self, strut): if not HAS_X11_BINDINGS: return log("strut=%s", strut) d = typedict(strut) values = [] for x in ("left", "right", "top", "bottom"): values.append(d.intget(x, 0)) has_partial = False for x in ("left_start_y", "left_end_y", "right_start_y", "right_end_y", "top_start_x", "top_end_x", "bottom_start_x", "bottom_end_x"): if x in d: has_partial = True values.append(d.intget(x, 0)) log("setting strut=%s, has partial=%s", values, has_partial) if not self.is_realized(): self.realize() if has_partial: prop_set(self.get_window(), "_NET_WM_STRUT_PARTIAL", ["u32"], values) prop_set(self.get_window(), "_NET_WM_STRUT", ["u32"], values[:4])
def set_icc_profile(self): ui_clients = [s for s in self._server_sources.values() if s.ui_client] if len(ui_clients)!=1: screenlog("%i UI clients, not setting ICC profile") self.reset_icc_profile() return icc = ui_clients[0].icc data = None for x in ("data", "icc-data", "icc-profile"): if x in icc: data = icc.get(x) break if not data: screenlog("no icc data found in %s", icc) self.reset_icc_profile() return import binascii screenlog("set_icc_profile() icc data for %s: %s (%i bytes)", ui_clients[0], binascii.hexlify(data or ""), len(data or "")) from xpra.x11.gtk_x11.prop import prop_set #each CARD32 contains just one 8-bit value - don't ask me why prop_set(self.root_window, "_ICC_PROFILE", ["u32"], [ord(x) for x in data]) prop_set(self.root_window, "_ICC_PROFILE_IN_X_VERSION", "u32", 0*100+4) #0.4 -> 0*100+4*1
def _set_gtk_x11_window_menu(add, wid, window, menus, application_action_callback=None, window_action_callback=None): from xpra.x11.dbus.menu import setup_dbus_window_menu from xpra.x11.gtk_x11.prop import prop_set, prop_del window_props = setup_dbus_window_menu(add, wid, menus, application_action_callback, window_action_callback) #window_props may contains X11 window properties we have to clear or set if not window_props: return if not window: #window has already been closed #(but we still want to call setup_dbus_window_menu above to ensure we clear things up!) return menulog("will set/remove the following window properties for wid=%i: %s", wid, window_props) try: from xpra.gtk_common.error import xsync with xsync: for k,v in window_props.items(): if v is None: prop_del(window, k) else: vtype, value = v prop_set(window, k, vtype, value) except Exception as e: menulog.error("Error setting menu window properties:") menulog.error(" %s", e)
def root_set(*args): prop_set(gtk.gdk.get_default_root_window(), *args)
def save_xvfb_pid(pid): import gtk from xpra.x11.gtk_x11.prop import prop_set prop_set(gtk.gdk.get_default_root_window(), "_XPRA_SERVER_PID", "u32", pid)
def pset(key, value): return prop_set(w, key, "utf8", value)
def set_tray_visual(tray_window, gdk_visual): prop_set(tray_window, TRAY_VISUAL, "visual", gdk_visual)
def _save_int(prop_name, pid): import gtk from xpra.x11.gtk_x11.prop import prop_set prop_set(gtk.gdk.get_default_root_window(), prop_name, "u32", pid)
def _sync_allowed_actions(self, *args): actions = self.get_property("allowed-actions") or [] metalog("sync_allowed_actions: setting _NET_WM_ALLOWED_ACTIONS=%s on %#x", actions, self.xid) with xswallow: prop_set(self.client_window, "_NET_WM_ALLOWED_ACTIONS", ["atom"], actions)
def process_client_message_event(self, event): # FIXME # Need to listen for: # _NET_CURRENT_DESKTOP # _NET_WM_PING responses # and maybe: # _NET_RESTACK_WINDOW # _NET_WM_STATE (more fully) if event.message_type == "_NET_WM_STATE": def update_wm_state(prop): current = self.get_property(prop) mode = event.data[0] if mode == _NET_WM_STATE_ADD: v = True elif mode == _NET_WM_STATE_REMOVE: v = False elif mode == _NET_WM_STATE_TOGGLE: v = not bool(current) else: log.warn("Warning: invalid mode for _NET_WM_STATE: %s", mode) return log( "process_client_message_event(%s) window %s=%s after %s (current state=%s)", event, prop, v, STATE_STRING.get(mode, mode), current) if v != current: self.update_wm_state(prop, v) atom1 = get_pyatom(event.window, event.data[1]) log("_NET_WM_STATE: %s", atom1) if atom1 == "_NET_WM_STATE_FULLSCREEN": update_wm_state("fullscreen") elif atom1 == "_NET_WM_STATE_ABOVE": update_wm_state("above") elif atom1 == "_NET_WM_STATE_BELOW": update_wm_state("below") elif atom1 == "_NET_WM_STATE_SHADED": update_wm_state("shaded") elif atom1 == "_NET_WM_STATE_STICKY": update_wm_state("sticky") elif atom1 == "_NET_WM_STATE_SKIP_TASKBAR": update_wm_state("skip-taskbar") elif atom1 == "_NET_WM_STATE_SKIP_PAGER": update_wm_state("skip-pager") get_pyatom(event.window, event.data[2]) elif atom1 in ("_NET_WM_STATE_MAXIMIZED_VERT", "_NET_WM_STATE_MAXIMIZED_HORZ"): atom2 = get_pyatom(event.window, event.data[2]) #we only have one state for both, so we require both to be set: if atom1 != atom2 and atom2 in ( "_NET_WM_STATE_MAXIMIZED_VERT", "_NET_WM_STATE_MAXIMIZED_HORZ"): update_wm_state("maximized") elif atom1 == "_NET_WM_STATE_HIDDEN": log("ignoring 'HIDDEN' _NET_WM_STATE: %s", event) #we don't honour those because they make little sense, see: #https://mail.gnome.org/archives/wm-spec-list/2005-May/msg00004.html elif atom1 == "_NET_WM_STATE_MODAL": update_wm_state("modal") elif atom1 == "_NET_WM_STATE_DEMANDS_ATTENTION": update_wm_state("attention-requested") else: log.info("Unhandled _NET_WM_STATE request: '%s'", event, atom1) log.info(" event%s", event) return True if event.message_type == "WM_CHANGE_STATE": iconic = event.data[0] log("WM_CHANGE_STATE: %s, serial=%s, last unmap serial=%#x", ICONIC_STATE_STRING.get(iconic, iconic), event.serial, self.last_unmap_serial) if (iconic in (IconicState, NormalState) and self.serial_after_last_unmap(event.serial) and not self.is_OR() and not self.is_tray()): self._updateprop("iconic", iconic == IconicState) return True if event.message_type == "_NET_WM_MOVERESIZE": log("_NET_WM_MOVERESIZE: %s", event) self.emit("initiate-moveresize", event) return True if event.message_type == "_NET_ACTIVE_WINDOW" and event.data[0] in (0, 1): log("_NET_ACTIVE_WINDOW: %s", event) self.set_active() self.emit("raised", event) return True if event.message_type == "_NET_WM_DESKTOP": workspace = int(event.data[0]) #query the workspace count on the root window #since we cannot access Wm from here.. root = self.client_window.get_screen().get_root_window() ndesktops = prop_get(root, "_NET_NUMBER_OF_DESKTOPS", "u32", ignore_errors=True) workspacelog( "received _NET_WM_DESKTOP: workspace=%s, number of desktops=%s", workspacestr(workspace), ndesktops) if ndesktops > 0 and (workspace in (WORKSPACE_UNSET, WORKSPACE_ALL) or 0 <= workspace < ndesktops): self.move_to_workspace(workspace) else: workspacelog.warn( "invalid _NET_WM_DESKTOP request: workspace=%s, number of desktops=%s", workspacestr(workspace), ndesktops) return True if event.message_type == "_NET_WM_FULLSCREEN_MONITORS": log("_NET_WM_FULLSCREEN_MONITORS: %s", event) #TODO: we should validate the indexes instead of copying them blindly! #TODO: keep track of source indication so we can forward that to the client m1, m2, m3, m4 = event.data[0], event.data[1], event.data[ 2], event.data[3] N = 16 #FIXME: arbitrary limit if m1 < 0 or m1 >= N or m2 < 0 or m2 >= N or m3 < 0 or m3 >= N or m4 < 0 or m4 >= N: log.warn( "invalid list of _NET_WM_FULLSCREEN_MONITORS - ignored") return False monitors = [m1, m2, m3, m4] log("_NET_WM_FULLSCREEN_MONITORS: monitors=%s", monitors) prop_set(self.client_window, "_NET_WM_FULLSCREEN_MONITORS", ["u32"], monitors) return True #TODO: maybe we should process _NET_MOVERESIZE_WINDOW here? # it may make sense to apply it to the client_window # whereas the code in WindowModel assumes there is a corral window #not handled: return CoreX11WindowModel.process_client_message_event(self, event)
def set_state(state): log("_handle_iconic_update: set_state(%s)", state) with xswallow: prop_set(self.client_window, "WM_STATE", "state", state)
def _sync_state(self, *_args): state = self.get_property("state") metalog("sync_state: setting _NET_WM_STATE=%s on %#x", state, self.xid) with xswallow: prop_set(self.client_window, "_NET_WM_STATE", ["atom"], state)
def set_active(self): root = self.client_window.get_screen().get_root_window() prop_set(root, "_NET_ACTIVE_WINDOW", "u32", self.xid)
def xset_u32_property(self, target, name, value): prop_set(target, name, "u32", value)
def change_wmcommand(): with xsync: prop_set(win.get_window(), "WM_COMMAND", "latin1", u"HELLO WORLD") print("WM_COMMAND changed!")
def do_update_server_settings(self, settings, reset=False, dpi=0, double_click_time=0, double_click_distance=(-1, -1), antialias={}, cursor_size=-1): if not self._xsettings_enabled: log("ignoring xsettings update: %s", settings) return if reset: #FIXME: preserve serial? (what happens when we change values which had the same serial?) self.reset_settings() self._settings = {} if self._default_xsettings: #try to parse default xsettings into a dict: try: for _, prop_name, value, _ in self._default_xsettings[1]: self._settings[prop_name] = value except Exception as e: log("failed to parse %s", self._default_xsettings) log.warn("Warning: failed to parse default XSettings:") log.warn(" %s", e) old_settings = dict(self._settings) log("server_settings: old=%s, updating with=%s", nonl(old_settings), nonl(settings)) log( "overrides: dpi=%s, double click time=%s, double click distance=%s", dpi, double_click_time, double_click_distance) log("overrides: antialias=%s", antialias) self._settings.update(settings) for k, v in settings.items(): #cook the "resource-manager" value to add the DPI and/or antialias values: if k == b"resource-manager" and (dpi > 0 or antialias or cursor_size > 0): value = bytestostr(v) #parse the resources into a dict: values = {} options = value.split("\n") for option in options: if not option: continue parts = option.split(":\t", 1) if len(parts) != 2: log("skipped invalid option: '%s'", option) continue if parts[0] in BLACKLISTED_XSETTINGS: log("skipped blacklisted option: '%s'", option) continue values[parts[0]] = parts[1] if cursor_size > 0: values["Xcursor.size"] = cursor_size if dpi > 0: values["Xft.dpi"] = dpi values["Xft/DPI"] = dpi * 1024 values["gnome.Xft/DPI"] = dpi * 1024 if antialias: ad = typedict(antialias) subpixel_order = "none" sss = tuple(self._server_sources.values()) if len(sss) == 1: #only honour sub-pixel hinting if a single client is connected #and only when it is not using any scaling (or overriden with SCALED_FONT_ANTIALIAS): ss = sss[0] ds_unscaled = getattr(ss, "desktop_size_unscaled", None) ds_scaled = getattr(ss, "desktop_size", None) if SCALED_FONT_ANTIALIAS or (not ds_unscaled or ds_unscaled == ds_scaled): subpixel_order = ad.strget("orientation", "none").lower() values.update({ "Xft.antialias": ad.intget("enabled", -1), "Xft.hinting": ad.intget("hinting", -1), "Xft.rgba": subpixel_order, "Xft.hintstyle": _get_antialias_hintstyle(ad) }) log("server_settings: resource-manager values=%s", nonl(values)) #convert the dict back into a resource string: value = '' for vk, vv in values.items(): value += "%s:\t%s\n" % (vk, vv) #record the actual value used self._settings[b"resource-manager"] = value v = value.encode("utf-8") #cook xsettings to add various settings: #(as those may not be present in xsettings on some platforms.. like win32 and osx) if k==b"xsettings-blob" and \ (self.double_click_time>0 or self.double_click_distance!=(-1, -1) or antialias or dpi>0): #start by removing blacklisted options: def filter_blacklisted(): serial, values = v new_values = [] for _t, _n, _v, _s in values: if bytestostr(_n) in BLACKLISTED_XSETTINGS: log("skipped blacklisted option %s", (_t, _n, _v, _s)) else: new_values.append((_t, _n, _v, _s)) return serial, new_values v = filter_blacklisted() def set_xsettings_value(name, value_type, value): #remove existing one, if any: serial, values = v new_values = [(_t, _n, _v, _s) for (_t, _n, _v, _s) in values if _n != name] new_values.append((value_type, name, value, 0)) return serial, new_values def set_xsettings_int(name, value): if value < 0: #not set, return v unchanged return v return set_xsettings_value(name, XSettingsTypeInteger, value) if dpi > 0: v = set_xsettings_int(b"Xft/DPI", dpi * 1024) if double_click_time > 0: v = set_xsettings_int(b"Net/DoubleClickTime", self.double_click_time) if antialias: ad = typedict(antialias) v = set_xsettings_int(b"Xft/Antialias", ad.intget("enabled", -1)) v = set_xsettings_int(b"Xft/Hinting", ad.intget("hinting", -1)) v = set_xsettings_value( b"Xft/RGBA", XSettingsTypeString, ad.strget("orientation", "none").lower()) v = set_xsettings_value(b"Xft/HintStyle", XSettingsTypeString, _get_antialias_hintstyle(ad)) if double_click_distance != (-1, -1): #some platforms give us a value for each axis, #but X11 only has one, so take the average try: x, y = double_click_distance if x > 0 and y > 0: d = iround((x + y) / 2.0) d = max(1, min(128, d)) #sanitize it a bit v = set_xsettings_int(b"Net/DoubleClickDistance", d) except Exception as e: log.warn( "error setting double click distance from %s: %s", double_click_distance, e) if k not in old_settings or v != old_settings[k]: if k == b"xsettings-blob": self.set_xsettings(v) elif k == b"resource-manager": from xpra.x11.gtk_x11.prop import prop_set p = "RESOURCE_MANAGER" log("server_settings: setting %s to %s", nonl(p), nonl(v)) prop_set(self.root_window, p, "latin1", strtobytes(v).decode("latin1")) else: log.warn("Warning: unexpected setting '%s'", bytestostr(k))
def set_int_prop(prop_name, value): with xsync: prop_set(win.get_window(), prop_name, "u32", value) print("%s=%s" % (prop_name, value))
def prop_set(self, key, ptype, value): prop_set(self.client_window, key, ptype, value)
def _sync_state(self, *args): state = self.get_property("state") metalog("sync_state: setting _NET_WM_STATE=%s on %#x", state, self.xid) with xswallow: prop_set(self.client_window, "_NET_WM_STATE", ["atom"], state)
def _save_str(prop_name, s): import gtk from xpra.x11.gtk_x11.prop import prop_set prop_set(gtk.gdk.get_default_root_window(), prop_name, "latin1", s.decode("latin1"))
def process_client_message_event(self, event): # FIXME # Need to listen for: # _NET_CURRENT_DESKTOP # _NET_WM_PING responses # and maybe: # _NET_RESTACK_WINDOW # _NET_WM_STATE (more fully) if event.message_type=="_NET_WM_STATE": def update_wm_state(prop): current = self.get_property(prop) mode = event.data[0] if mode==_NET_WM_STATE_ADD: v = True elif mode==_NET_WM_STATE_REMOVE: v = False elif mode==_NET_WM_STATE_TOGGLE: v = not bool(current) else: log.warn("invalid mode for _NET_WM_STATE: %s", mode) return log("process_client_message_event(%s) window %s=%s after %s (current state=%s)", event, prop, v, STATE_STRING.get(mode, mode), current) if v!=current: self.update_wm_state(prop, v) atom1 = get_pyatom(event.window, event.data[1]) log("_NET_WM_STATE: %s", atom1) if atom1=="_NET_WM_STATE_FULLSCREEN": update_wm_state("fullscreen") elif atom1=="_NET_WM_STATE_ABOVE": update_wm_state("above") elif atom1=="_NET_WM_STATE_BELOW": update_wm_state("below") elif atom1=="_NET_WM_STATE_SHADED": update_wm_state("shaded") elif atom1=="_NET_WM_STATE_STICKY": update_wm_state("sticky") elif atom1=="_NET_WM_STATE_SKIP_TASKBAR": update_wm_state("skip-taskbar") elif atom1=="_NET_WM_STATE_SKIP_PAGER": update_wm_state("skip-pager") get_pyatom(event.window, event.data[2]) elif atom1 in ("_NET_WM_STATE_MAXIMIZED_VERT", "_NET_WM_STATE_MAXIMIZED_HORZ"): atom2 = get_pyatom(event.window, event.data[2]) #we only have one state for both, so we require both to be set: if atom1!=atom2 and atom2 in ("_NET_WM_STATE_MAXIMIZED_VERT", "_NET_WM_STATE_MAXIMIZED_HORZ"): update_wm_state("maximized") elif atom1=="_NET_WM_STATE_HIDDEN": log("ignoring 'HIDDEN' _NET_WM_STATE: %s", event) #we don't honour those because they make little sense, see: #https://mail.gnome.org/archives/wm-spec-list/2005-May/msg00004.html pass elif atom1=="_NET_WM_STATE_MODAL": update_wm_state("modal") else: log.info("process_client_message_event(%s) unhandled atom=%s", event, atom1) return True elif event.message_type=="WM_CHANGE_STATE": log("WM_CHANGE_STATE: %s", event.data[0]) if event.data[0]==IconicState and event.serial>self.last_unmap_serial and not self.is_OR() and not self.is_tray(): self._updateprop("iconic", True) return True elif event.message_type=="_NET_WM_MOVERESIZE": log("_NET_WM_MOVERESIZE: %s", event) self.emit("initiate-moveresize", event) return True elif event.message_type=="_NET_ACTIVE_WINDOW" and event.data[0] in (0, 1): log("_NET_ACTIVE_WINDOW: %s", event) self.set_active() self.emit("raised", event) return True elif event.message_type=="_NET_WM_DESKTOP": workspace = int(event.data[0]) #query the workspace count on the root window #since we cannot access Wm from here.. root = self.client_window.get_screen().get_root_window() ndesktops = prop_get(root, "_NET_NUMBER_OF_DESKTOPS", "u32", ignore_errors=True) workspacelog("received _NET_WM_DESKTOP: workspace=%s, number of desktops=%s", workspacestr(workspace), ndesktops) if ndesktops>0 and (workspace in (WORKSPACE_UNSET, WORKSPACE_ALL) or (workspace>=0 and workspace<ndesktops)): self.move_to_workspace(workspace) else: workspacelog.warn("invalid _NET_WM_DESKTOP request: workspace=%s, number of desktops=%s", workspacestr(workspace), ndesktops) return True elif event.message_type=="_NET_WM_FULLSCREEN_MONITORS": log("_NET_WM_FULLSCREEN_MONITORS: %s", event) #TODO: we should validate the indexes instead of copying them blindly! #TODO: keep track of source indication so we can forward that to the client m1, m2, m3, m4 = event.data[0], event.data[1], event.data[2], event.data[3] N = 16 #FIXME: arbitrary limit if m1<0 or m1>=N or m2<0 or m2>=N or m3<0 or m3>=N or m4<0 or m4>=N: log.warn("invalid list of _NET_WM_FULLSCREEN_MONITORS - ignored") return monitors = [m1, m2, m3, m4] log("_NET_WM_FULLSCREEN_MONITORS: monitors=%s", monitors) prop_set(self.client_window, "_NET_WM_FULLSCREEN_MONITORS", ["u32"], monitors) return True #TODO: maybe we should process _NET_MOVERESIZE_WINDOW here? # it may make sense to apply it to the client_window # whereas the code in WindowModel assumes there is a corral window #not handled: return CoreX11WindowModel.process_client_message_event(self, event)
def set_tray_orientation(tray_window, orientation): prop_set(tray_window, TRAY_ORIENTATION, "u32", orientation)
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 root_set(p): settingslog("server_settings: setting %s to %s", nonl(p), nonl(v)) prop_set(root, p, "latin1", v.decode("utf-8"))
def do_set_command(): metalog("do_set_command() str(%s)=%s (type=%s)", command, v, type(command)) prop_set(self.get_window(), "WM_COMMAND", "latin1", v)
def root_set(self, *args): prop_set(self._root, *args)
def do_set_bypass_compositor(): prop_set(self.get_window(), "_NET_WM_BYPASS_COMPOSITOR", "u32", v)
def do_set_strut(): if has_partial: prop_set(self.get_window(), "_NET_WM_STRUT_PARTIAL", ["u32"], values) prop_set(self.get_window(), "_NET_WM_STRUT", ["u32"], values[:4])