diff --git a/src/xpra/x11/gtk2/models/base.py b/src/xpra/x11/gtk2/models/base.py index 75cab7e66a..be407404f2 100644 --- a/src/xpra/x11/gtk2/models/base.py +++ b/src/xpra/x11/gtk2/models/base.py @@ -10,6 +10,7 @@ from xpra.x11.gtk2.models.core import CoreX11WindowModel, gobject, xswallow, gdk from xpra.x11.bindings.window_bindings import X11WindowBindings, constants #@UnresolvedImport from xpra.x11.gtk2.gdk_bindings import get_pywindow, get_pyatom, calc_constrained_size #@UnresolvedImport +from xpra.x11.gtk2.models.size_hints_util import sanitize_size_hints from xpra.log import Logger log = Logger("x11", "window") @@ -47,45 +48,58 @@ class BaseWindowModel(CoreX11WindowModel): """ __common_properties__ = CoreX11WindowModel.__common_properties__.copy() __common_properties__.update({ + #from WM_TRANSIENT_FOR "transient-for": (gobject.TYPE_PYOBJECT, "Transient for (or None)", "", gobject.PARAM_READABLE), + #from _NET_WM_WINDOW_OPACITY "opacity": (gobject.TYPE_INT64, "Opacity", "", -1, 0xffffffff, -1, gobject.PARAM_READABLE), + #from WM_HINTS.window_group "group-leader": (gobject.TYPE_PYOBJECT, "Window group leader as a pair: (xid, gdk window)", "", gobject.PARAM_READABLE), + #from WM_HINTS.urgency or _NET_WM_STATE "attention-requested": (gobject.TYPE_BOOLEAN, "Urgency hint from client, or us", "", False, gobject.PARAM_READWRITE), + #from WM_HINTS.input or WM_TAKE_FOCUS "can-focus": (gobject.TYPE_BOOLEAN, "Does this window ever accept keyboard input?", "", True, gobject.PARAM_READWRITE), + # + "size-hints": (gobject.TYPE_PYOBJECT, + "Client hints on constraining its size", "", + gobject.PARAM_READABLE), + #from _NET_WM_BYPASS_COMPOSITOR "bypass-compositor": (gobject.TYPE_INT, "hint that the window would benefit from running uncomposited ", "", 0, 2, 0, gobject.PARAM_READABLE), + #from _NET_WM_FULLSCREEN_MONITORS "fullscreen-monitors": (gobject.TYPE_PYOBJECT, "List of 4 monitor indices indicating the top, bottom, left, and right edges of the window when the fullscreen state is enabled", "", gobject.PARAM_READABLE), + #from _NET_WM_STRUT_PARTIAL or _NET_WM_STRUT "strut": (gobject.TYPE_PYOBJECT, "Struts requested by window, or None", "", gobject.PARAM_READABLE), + #from _NET_WM_DESKTOP "workspace": (gobject.TYPE_UINT, "The workspace this window is on", "", 0, 2**32-1, WORKSPACE_UNSET, gobject.PARAM_READWRITE), + #set initially only by the window model class + #(derived from XGetWindowAttributes.override_redirect) "override-redirect": (gobject.TYPE_BOOLEAN, "Is the window of type override-redirect", "", False, gobject.PARAM_READABLE), - "modal": (gobject.TYPE_PYOBJECT, - "Modal (boolean)", "", - gobject.PARAM_READWRITE), + #from _NET_WM_WINDOW_TYPE "window-type": (gobject.TYPE_PYOBJECT, "Window type", "NB, most preferred comes first, then fallbacks", @@ -95,6 +109,9 @@ class BaseWindowModel(CoreX11WindowModel): "State, as per _NET_WM_STATE", "", gobject.PARAM_READABLE), #all the attributes below are virtual attributes from WM_STATE: + "modal": (gobject.TYPE_PYOBJECT, + "Modal (boolean)", "", + gobject.PARAM_READWRITE), "fullscreen": (gobject.TYPE_BOOLEAN, "Fullscreen-ness of window", "", False, @@ -133,11 +150,11 @@ class BaseWindowModel(CoreX11WindowModel): gobject.PARAM_READWRITE), }) _property_names = CoreX11WindowModel._property_names + [ - "transient-for", "fullscreen-monitors", "bypass-compositor", "group-leader", "window-type", "workspace", "strut", + "size-hints", "transient-for", "fullscreen-monitors", "bypass-compositor", "group-leader", "window-type", "workspace", "strut", #virtual attributes: "fullscreen", "focused", "maximized", "above", "below", "shaded", "skip-taskbar", "skip-pager", "sticky"] _dynamic_property_names = CoreX11WindowModel._dynamic_property_names + [ - "attention-requested", + "size-hints", "attention-requested", "fullscreen", "focused", "maximized", "above", "below", "shaded", "skip-taskbar", "skip-pager", "sticky"] _internal_property_names = CoreX11WindowModel._internal_property_names+["state"] _initial_x11_properties = CoreX11WindowModel._initial_x11_properties + [ @@ -313,6 +330,34 @@ def _update_can_focus(self, *args): self._updateprop("can-focus", can_focus) + def _handle_wm_normal_hints_change(self): + with xswallow: + size_hints = X11Window.getSizeHints(self.xid) + metalog("WM_NORMAL_HINTS=%s", size_hints) + #getSizeHints exports fields using their X11 names as defined in the "XSizeHints" structure, + #but we use a different naming (for historical reason and backwards compatibility) + #so rename the fields: + hints = {} + if size_hints: + for k,v in size_hints.items(): + hints[{"min_size" : "minimum-size", + "max_size" : "maximum-size", + "base_size" : "base-size", + "resize_inc" : "increment", + "win_gravity" : "gravity", + }.get(k, k)] = v + sanitize_size_hints(hints) + # Don't send out notify and ConfigureNotify events when this property + # gets no-op updated -- some apps like FSF Emacs 21 like to update + # their properties every time they see a ConfigureNotify, and this + # reduces the chance for us to get caught in loops: + old_hints = self.get_property("size-hints") + if hints and hints!=old_hints: + self._internal_set_property("size-hints", hints) + if self._setup_done: + self._update_client_geometry() + + _x11_property_handlers = CoreX11WindowModel._x11_property_handlers.copy() _x11_property_handlers.update({ "WM_TRANSIENT_FOR" : _handle_transient_for_change, @@ -324,6 +369,7 @@ def _update_can_focus(self, *args): "_NET_WM_STRUT_PARTIAL" : _handle_wm_strut_change, "_NET_WM_WINDOW_OPACITY" : _handle_opacity_change, "WM_HINTS" : _handle_wm_hints_change, + "WM_NORMAL_HINTS" : _handle_wm_normal_hints_change, }) diff --git a/src/xpra/x11/gtk2/models/core.py b/src/xpra/x11/gtk2/models/core.py index 3528f80ad0..2e4981ab5c 100644 --- a/src/xpra/x11/gtk2/models/core.py +++ b/src/xpra/x11/gtk2/models/core.py @@ -77,50 +77,62 @@ class CoreX11WindowModel(AutoPropGObjectMixin, gobject.GObject): the py_property_handlers do it in the other direction. """ __common_properties__ = { + #the actual X11 client window "client-window": (gobject.TYPE_PYOBJECT, "gtk.gdk.Window representing the client toplevel", "", gobject.PARAM_READABLE), + #the X11 window id "xid": (gobject.TYPE_INT, "X11 window id", "", -1, 65535, -1, gobject.PARAM_READABLE), + #FIXME: this is an ugly virtual property "geometry": (gobject.TYPE_PYOBJECT, "current (border-corrected, relative to parent) coordinates (x, y, w, h) for the window", "", gobject.PARAM_READABLE), + #if the window depth is 32 bit "has-alpha": (gobject.TYPE_BOOLEAN, "Does the window use transparency", "", False, gobject.PARAM_READABLE), + #from WM_CLIENT_MACHINE "client-machine": (gobject.TYPE_PYOBJECT, "Host where client process is running", "", gobject.PARAM_READABLE), + #from _NET_WM_PID "pid": (gobject.TYPE_INT, "PID of owning process", "", -1, 65535, -1, gobject.PARAM_READABLE), + #from _NET_WM_NAME or WM_NAME "title": (gobject.TYPE_PYOBJECT, "Window title (unicode or None)", "", gobject.PARAM_READABLE), + #from WM_WINDOW_ROLE "role" : (gobject.TYPE_PYOBJECT, "The window's role (ICCCM session management)", "", gobject.PARAM_READABLE), + #from WM_PROTOCOLS via XGetWMProtocols "protocols": (gobject.TYPE_PYOBJECT, "Supported WM protocols", "", gobject.PARAM_READABLE), + #from WM_COMMAND "command": (gobject.TYPE_PYOBJECT, "Command used to start or restart the client", "", gobject.PARAM_READABLE), + #from WM_CLASS via getClassHint "class-instance": (gobject.TYPE_PYOBJECT, "Classic X 'class' and 'instance'", "", gobject.PARAM_READABLE), + #ShapeNotify events will populate this using XShapeQueryExtents "shape": (gobject.TYPE_PYOBJECT, "Window XShape data", "", gobject.PARAM_READABLE), - #this is synced to "_NET_FRAME_EXTENTS" + #synced to "_NET_FRAME_EXTENTS" "frame": (gobject.TYPE_PYOBJECT, "Size of the window frame, as per _NET_FRAME_EXTENTS", "", gobject.PARAM_READWRITE), - #this is ssynced to "_NET_WM_ALLOWED_ACTIONS" + #synced to "_NET_WM_ALLOWED_ACTIONS" "allowed-actions": (gobject.TYPE_PYOBJECT, "Supported WM actions", "", gobject.PARAM_READWRITE), diff --git a/src/xpra/x11/gtk2/models/window.py b/src/xpra/x11/gtk2/models/window.py index aee5ec3be9..45a6f51c7c 100644 --- a/src/xpra/x11/gtk2/models/window.py +++ b/src/xpra/x11/gtk2/models/window.py @@ -14,7 +14,6 @@ from xpra.x11.gtk_x11.prop import prop_set, prop_get, MotifWMHints from xpra.x11.bindings.window_bindings import X11WindowBindings #@UnresolvedImport from xpra.x11.gtk2.models import Unmanageable, MAX_WINDOW_SIZE -from xpra.x11.gtk2.models.size_hints_util import sanitize_size_hints from xpra.x11.gtk2.models.base import BaseWindowModel, constants from xpra.x11.gtk2.models.core import sanestr, gobject, xswallow, xsync from xpra.x11.gtk2.gdk_bindings import ( @@ -74,6 +73,9 @@ class WindowModel(BaseWindowModel): __gproperties__ = dict(BaseWindowModel.__common_properties__) __gproperties__.update({ + "owner": (gobject.TYPE_PYOBJECT, + "Owner", "", + gobject.PARAM_READABLE), # Interesting properties of the client window, that will be # automatically kept up to date: "actual-size": (gobject.TYPE_PYOBJECT, @@ -88,9 +90,6 @@ class WindowModel(BaseWindowModel): "requested-size": (gobject.TYPE_PYOBJECT, "Client-requested size on screen", "", gobject.PARAM_READABLE), - "size-hints": (gobject.TYPE_PYOBJECT, - "Client hints on constraining its size", "", - gobject.PARAM_READABLE), # Toggling this property does not actually make the window iconified, # i.e. make it appear or disappear from the screen -- it merely # updates the various window manager properties that inform the world @@ -99,18 +98,19 @@ class WindowModel(BaseWindowModel): "ICCCM 'iconic' state -- any sort of 'not on desktop'.", "", False, gobject.PARAM_READWRITE), + #from _NET_WM_ICON_NAME or WM_ICON_NAME "icon-title": (gobject.TYPE_PYOBJECT, "Icon title (unicode or None)", "", gobject.PARAM_READABLE), + #from _NET_WM_ICON "icon": (gobject.TYPE_PYOBJECT, "Icon (local Cairo surface)", "", gobject.PARAM_READABLE), + #from _NET_WM_ICON "icon-pixmap": (gobject.TYPE_PYOBJECT, "Icon (server Pixmap)", "", gobject.PARAM_READABLE), - "owner": (gobject.TYPE_PYOBJECT, - "Owner", "", - gobject.PARAM_READABLE), + #from _MOTIF_WM_HINTS.decorations "decorations": (gobject.TYPE_BOOLEAN, "Should the window decorations be shown", "", True, @@ -125,9 +125,9 @@ class WindowModel(BaseWindowModel): }) _property_names = BaseWindowModel._property_names + [ - "size-hints", "icon-title", "icon", "decorations"] + "icon-title", "icon", "decorations"] _dynamic_property_names = BaseWindowModel._dynamic_property_names + [ - "size-hints", "icon-title", "icon", "decorations"] + "icon-title", "icon", "decorations"] _initial_x11_properties = BaseWindowModel._initial_x11_properties + [ "WM_HINTS", "WM_NORMAL_HINTS", "_MOTIF_WM_HINTS", "WM_ICON_NAME", "_NET_WM_ICON_NAME", "_NET_WM_ICON", @@ -194,7 +194,6 @@ def setup(self): w,h = X11Window.getGeometry(self.xid)[2:4] hints = self.get_property("size-hints") log("setup() hints=%s size=%ix%i", hints, w, h) - sanitize_size_hints(hints) nw, nh = calc_constrained_size(w, h, hints)[:2] if nw>=MAX_WINDOW_SIZE or nh>=MAX_WINDOW_SIZE: #we can't handle windows that big! @@ -501,33 +500,6 @@ def do_child_configure_request_event(self, event): # X11 properties synced to Python objects ######################################### - def _handle_wm_normal_hints_change(self): - with xswallow: - size_hints = X11Window.getSizeHints(self.xid) - metalog("WM_NORMAL_HINTS=%s", size_hints) - #getSizeHints exports fields using their X11 names as defined in the "XSizeHints" structure, - #but we use a different naming (for historical reason and backwards compatibility) - #so rename the fields: - hints = {} - if size_hints: - for k,v in size_hints.items(): - hints[{"min_size" : "minimum-size", - "max_size" : "maximum-size", - "base_size" : "base-size", - "resize_inc" : "increment", - "win_gravity" : "gravity", - }.get(k, k)] = v - sanitize_size_hints(hints) - # Don't send out notify and ConfigureNotify events when this property - # gets no-op updated -- some apps like FSF Emacs 21 like to update - # their properties every time they see a ConfigureNotify, and this - # reduces the chance for us to get caught in loops: - old_hints = self.get_property("size-hints") - if hints and hints!=old_hints: - self._internal_set_property("size-hints", hints) - if self._setup_done: - self._update_client_geometry() - def _handle_icon_title_change(self): icon_name = self.prop_get("_NET_WM_ICON_NAME", "utf8", True) iconlog("_NET_WM_ICON_NAME=%s", icon_name) @@ -569,7 +541,6 @@ def _handle_net_wm_icon_change(self): _x11_property_handlers = dict(BaseWindowModel._x11_property_handlers) _x11_property_handlers.update({ - "WM_NORMAL_HINTS" : _handle_wm_normal_hints_change, "WM_ICON_NAME" : _handle_icon_title_change, "_NET_WM_ICON_NAME" : _handle_icon_title_change, "_MOTIF_WM_HINTS" : _handle_motif_wm_hints_change,