From 3c2a1d95e07332b1ceff8cbefc2e9a8d3aca572b Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 1 Mar 2007 19:50:56 +0000 Subject: [PATCH] support fullscreen and z-order better on Linux --- panda/src/glxdisplay/glxGraphicsPipe.cxx | 6 + panda/src/glxdisplay/glxGraphicsPipe.h | 6 + panda/src/glxdisplay/glxGraphicsWindow.cxx | 171 ++++++++++++++++++--- panda/src/glxdisplay/glxGraphicsWindow.h | 9 +- 4 files changed, 169 insertions(+), 23 deletions(-) diff --git a/panda/src/glxdisplay/glxGraphicsPipe.cxx b/panda/src/glxdisplay/glxGraphicsPipe.cxx index 7cb91f9e0c..f1d6fd912e 100644 --- a/panda/src/glxdisplay/glxGraphicsPipe.cxx +++ b/panda/src/glxdisplay/glxGraphicsPipe.cxx @@ -121,6 +121,12 @@ glxGraphicsPipe(const string &display) { _net_wm_window_type = XInternAtom(_display, "_NET_WM_WINDOW_TYPE", false); _net_wm_window_type_splash = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_SPLASH", false); _net_wm_window_type_fullscreen = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_FULLSCREEN", false); + _net_wm_state = XInternAtom(_display, "_NET_WM_STATE", false); + _net_wm_state_fullscreen = XInternAtom(_display, "_NET_WM_STATE_FULLSCREEN", false); + _net_wm_state_above = XInternAtom(_display, "_NET_WM_STATE_ABOVE", false); + _net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", false); + _net_wm_state_add = XInternAtom(_display, "_NET_WM_STATE_ADD", false); + _net_wm_state_remove = XInternAtom(_display, "_NET_WM_STATE_REMOVE", false); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/glxdisplay/glxGraphicsPipe.h b/panda/src/glxdisplay/glxGraphicsPipe.h index 83fdc99d91..f3d507f0fd 100644 --- a/panda/src/glxdisplay/glxGraphicsPipe.h +++ b/panda/src/glxdisplay/glxGraphicsPipe.h @@ -114,6 +114,12 @@ public: Atom _net_wm_window_type; Atom _net_wm_window_type_splash; Atom _net_wm_window_type_fullscreen; + Atom _net_wm_state; + Atom _net_wm_state_fullscreen; + Atom _net_wm_state_above; + Atom _net_wm_state_below; + Atom _net_wm_state_add; + Atom _net_wm_state_remove; protected: virtual PT(GraphicsOutput) make_output(const string &name, diff --git a/panda/src/glxdisplay/glxGraphicsWindow.cxx b/panda/src/glxdisplay/glxGraphicsWindow.cxx index 8c08f831b7..340ff8563f 100644 --- a/panda/src/glxdisplay/glxGraphicsWindow.cxx +++ b/panda/src/glxdisplay/glxGraphicsWindow.cxx @@ -65,6 +65,12 @@ glxGraphicsWindow(GraphicsPipe *pipe, _net_wm_window_type = glx_pipe->_net_wm_window_type; _net_wm_window_type_splash = glx_pipe->_net_wm_window_type_splash; _net_wm_window_type_fullscreen = glx_pipe->_net_wm_window_type_fullscreen; + _net_wm_state = glx_pipe->_net_wm_state; + _net_wm_state_fullscreen = glx_pipe->_net_wm_state_fullscreen; + _net_wm_state_above = glx_pipe->_net_wm_state_above; + _net_wm_state_below = glx_pipe->_net_wm_state_below; + _net_wm_state_add = glx_pipe->_net_wm_state_add; + _net_wm_state_remove = glx_pipe->_net_wm_state_remove; GraphicsWindowInputDevice device = GraphicsWindowInputDevice::pointer_and_keyboard("keyboard/mouse"); @@ -431,16 +437,23 @@ set_properties_now(WindowProperties &properties) { glxGraphicsPipe *glx_pipe; DCAST_INTO_V(glx_pipe, _pipe); + // We'll pass some property requests on as a window manager hint. + WindowProperties wm_properties = _properties; + wm_properties.add_properties(properties); + // The window title may be changed by issuing another hint request. // Assume this will be honored. if (properties.has_title()) { - WindowProperties wm_properties; - wm_properties.set_title(properties.get_title()); _properties.set_title(properties.get_title()); - set_wm_properties(wm_properties); properties.clear_title(); } + // Ditto for fullscreen. + if (properties.has_fullscreen()) { + _properties.set_fullscreen(properties.get_fullscreen()); + properties.clear_fullscreen(); + } + // The size and position of an already-open window are changed via // explicit X calls. These may still get intercepted by the window // manager. Rather than changing _properties immediately, we'll @@ -460,9 +473,31 @@ set_properties_now(WindowProperties &properties) { value_mask |= (CWWidth | CWHeight); properties.clear_size(); } + if (properties.has_z_order()) { + // We'll send the classing stacking request through the standard + // interface, for users of primitive window managers; but we'll + // also send it as a window manager hint, for users of modern + // window managers. + switch (properties.get_z_order()) { + case WindowProperties::Z_bottom: + changes.stack_mode = Below; + break; + + case WindowProperties::Z_normal: + changes.stack_mode = TopIf; + break; + + case WindowProperties::Z_top: + changes.stack_mode = Above; + break; + } + + value_mask |= (CWStackMode); + properties.clear_z_order(); + } if (value_mask != 0) { - XConfigureWindow(_display, _xwindow, value_mask, &changes); + XReconfigureWMWindow(_display, _xwindow, _screen, value_mask, &changes); // Don't draw anything until this is done reconfiguring. _awaiting_configure = true; @@ -478,6 +513,8 @@ set_properties_now(WindowProperties &properties) { } properties.clear_cursor_hidden(); } + + set_wm_properties(wm_properties, true); } //////////////////////////////////////////////////////////////////// @@ -600,7 +637,7 @@ open_window() { << "failed to create X window.\n"; return false; } - set_wm_properties(_properties); + set_wm_properties(_properties, false); // We don't specify any fancy properties of the XIC. It would be // nicer if we could support fancy IM's that want preedit callbacks, @@ -645,9 +682,13 @@ open_window() { // specified directly by the application; they must be // requested via the window manager, which may or may // not choose to honor the request. +// +// If already_mapped is true, the window has already +// been mapped (manifested) on the display. This means +// we may need to use a different action in some cases. //////////////////////////////////////////////////////////////////// void glxGraphicsWindow:: -set_wm_properties(const WindowProperties &properties) { +set_wm_properties(const WindowProperties &properties, bool already_mapped) { // Name the window if there is a name XTextProperty window_name; XTextProperty *window_name_p = (XTextProperty *)NULL; @@ -698,6 +739,40 @@ set_wm_properties(const WindowProperties &properties) { wm_hints_p->flags = StateHint; } + // Two competing window manager interfaces have evolved. One of + // them allows to set certain properties as a "type"; the other one + // as a "state". We'll try to honor both. + static const int max_type_data = 32; + PN_int32 type_data[max_type_data]; + int next_type_data = 0; + + static const int max_state_data = 32; + PN_int32 state_data[max_state_data]; + int next_state_data = 0; + + static const int max_set_data = 32; + class SetAction { + public: + inline SetAction() { } + inline SetAction(Atom state, Atom action) : _state(state), _action(action) { } + Atom _state; + Atom _action; + }; + SetAction set_data[max_set_data]; + int next_set_data = 0; + + if (properties.get_fullscreen()) { + // For a "fullscreen" request, we pass this through, hoping the + // window manager will support EWMH. + type_data[next_type_data++] = _net_wm_window_type_fullscreen; + + // We also request it as a state. + state_data[next_state_data++] = _net_wm_state_fullscreen; + set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, _net_wm_state_add); + } else { + set_data[next_set_data++] = SetAction(_net_wm_state_fullscreen, _net_wm_state_remove); + } + // If we asked for a window without a border, there's no excellent // way to arrange that. For users whose window managers follow the // EWMH specification, we can ask for a "splash" screen, which is @@ -712,25 +787,77 @@ set_wm_properties(const WindowProperties &properties) { class_hints_p = XAllocClassHint(); class_hints_p->res_class = "Undecorated"; - long data[2]; - data[0] = _net_wm_window_type_splash; - data[1] = None; - - XChangeProperty(_display, _xwindow, _net_wm_window_type, - XA_ATOM, 32, PropModeReplace, - (unsigned char *)data, 1); + if (!properties.get_fullscreen()) { + type_data[next_type_data++] = _net_wm_window_type_splash; + } } - if (properties.get_fullscreen()) { - // For a "fullscreen" request, we pass this through, hoping the - // window manager will support EWMH. - long data[2]; - data[0] = _net_wm_window_type_fullscreen; - data[1] = None; + if (properties.has_z_order()) { + switch (properties.get_z_order()) { + case WindowProperties::Z_bottom: + state_data[next_state_data++] = _net_wm_state_below; + set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_add); + set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove); + break; - XChangeProperty(_display, _xwindow, _net_wm_window_type, - XA_ATOM, 32, PropModeReplace, - (unsigned char *)data, 1); + case WindowProperties::Z_normal: + set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove); + set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_remove); + break; + + case WindowProperties::Z_top: + state_data[next_state_data++] = _net_wm_state_above; + set_data[next_set_data++] = SetAction(_net_wm_state_below, _net_wm_state_remove); + set_data[next_set_data++] = SetAction(_net_wm_state_above, _net_wm_state_add); + break; + } + } + + nassertv(next_type_data < max_type_data); + nassertv(next_state_data < max_state_data); + nassertv(next_set_data < max_set_data); + + XChangeProperty(_display, _xwindow, _net_wm_window_type, + XA_ATOM, 32, PropModeReplace, + (unsigned char *)type_data, next_type_data); + + // Request the state properties all at once. + XChangeProperty(_display, _xwindow, _net_wm_state, + XA_ATOM, 32, PropModeReplace, + (unsigned char *)state_data, next_state_data); + + if (!already_mapped) { + // We have to request state changes differently when the window + // has been mapped. To do this, we need to send a client message + // to the root window for each change. + + // This doesn't appear to be working for me, though I think I'm + // doing everything right. There's no feedback mechanism, + // however, so it's impossible to tell whether I'm actually doing + // something wrong, or whether this feature is simply not + // supported in my current window manager. I'll leave it here for + // now. + + glxGraphicsPipe *glx_pipe; + DCAST_INTO_V(glx_pipe, _pipe); + + for (int i = 0; i < next_set_data; ++i) { + XClientMessageEvent event; + memset(&event, 0, sizeof(event)); + + event.type = ClientMessage; + event.send_event = True; + event.display = _display; + event.window = _xwindow; + event.message_type = _net_wm_state; + event.format = 32; + event.data.l[0] = set_data[i]._action; + event.data.l[1] = set_data[i]._state; + event.data.l[2] = 0; + event.data.l[3] = 1; + + XSendEvent(_display, glx_pipe->get_root(), True, 0, (XEvent *)&event); + } } XSetWMProperties(_display, _xwindow, window_name_p, window_name_p, diff --git a/panda/src/glxdisplay/glxGraphicsWindow.h b/panda/src/glxdisplay/glxGraphicsWindow.h index 6bf7c2fd01..9b3bb03e7e 100644 --- a/panda/src/glxdisplay/glxGraphicsWindow.h +++ b/panda/src/glxdisplay/glxGraphicsWindow.h @@ -54,7 +54,8 @@ protected: virtual bool open_window(); private: - void set_wm_properties(const WindowProperties &properties); + void set_wm_properties(const WindowProperties &properties, + bool already_mapped); #ifdef HAVE_GLXFBCONFIG void setup_colormap(GLXFBConfig fbconfig); @@ -82,6 +83,12 @@ private: Atom _net_wm_window_type; Atom _net_wm_window_type_splash; Atom _net_wm_window_type_fullscreen; + Atom _net_wm_state; + Atom _net_wm_state_fullscreen; + Atom _net_wm_state_above; + Atom _net_wm_state_below; + Atom _net_wm_state_add; + Atom _net_wm_state_remove; public: static TypeHandle get_class_type() {