Browse Source

support fullscreen and z-order better on Linux

David Rose 19 years ago
parent
commit
3c2a1d95e0

+ 6 - 0
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 = XInternAtom(_display, "_NET_WM_WINDOW_TYPE", false);
   _net_wm_window_type_splash = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_SPLASH", 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_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);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 6 - 0
panda/src/glxdisplay/glxGraphicsPipe.h

@@ -114,6 +114,12 @@ public:
   Atom _net_wm_window_type;
   Atom _net_wm_window_type;
   Atom _net_wm_window_type_splash;
   Atom _net_wm_window_type_splash;
   Atom _net_wm_window_type_fullscreen;
   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:
 protected:
   virtual PT(GraphicsOutput) make_output(const string &name,
   virtual PT(GraphicsOutput) make_output(const string &name,

+ 149 - 22
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 = glx_pipe->_net_wm_window_type;
   _net_wm_window_type_splash = glx_pipe->_net_wm_window_type_splash;
   _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_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 device =
     GraphicsWindowInputDevice::pointer_and_keyboard("keyboard/mouse");
     GraphicsWindowInputDevice::pointer_and_keyboard("keyboard/mouse");
@@ -431,16 +437,23 @@ set_properties_now(WindowProperties &properties) {
   glxGraphicsPipe *glx_pipe;
   glxGraphicsPipe *glx_pipe;
   DCAST_INTO_V(glx_pipe, _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.
   // The window title may be changed by issuing another hint request.
   // Assume this will be honored.
   // Assume this will be honored.
   if (properties.has_title()) {
   if (properties.has_title()) {
-    WindowProperties wm_properties;
-    wm_properties.set_title(properties.get_title());
     _properties.set_title(properties.get_title());
     _properties.set_title(properties.get_title());
-    set_wm_properties(wm_properties);
     properties.clear_title();
     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
   // The size and position of an already-open window are changed via
   // explicit X calls.  These may still get intercepted by the window
   // explicit X calls.  These may still get intercepted by the window
   // manager.  Rather than changing _properties immediately, we'll
   // manager.  Rather than changing _properties immediately, we'll
@@ -460,9 +473,31 @@ set_properties_now(WindowProperties &properties) {
     value_mask |= (CWWidth | CWHeight);
     value_mask |= (CWWidth | CWHeight);
     properties.clear_size();
     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) {
   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.
     // Don't draw anything until this is done reconfiguring.
     _awaiting_configure = true;
     _awaiting_configure = true;
@@ -478,6 +513,8 @@ set_properties_now(WindowProperties &properties) {
     }
     }
     properties.clear_cursor_hidden();
     properties.clear_cursor_hidden();
   }
   }
+
+  set_wm_properties(wm_properties, true);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -600,7 +637,7 @@ open_window() {
       << "failed to create X window.\n";
       << "failed to create X window.\n";
     return false;
     return false;
   }
   }
-  set_wm_properties(_properties);
+  set_wm_properties(_properties, false);
 
 
   // We don't specify any fancy properties of the XIC.  It would be
   // 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,
   // 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
 //               specified directly by the application; they must be
 //               requested via the window manager, which may or may
 //               requested via the window manager, which may or may
 //               not choose to honor the request.
 //               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::
 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
   // Name the window if there is a name
   XTextProperty window_name;
   XTextProperty window_name;
   XTextProperty *window_name_p = (XTextProperty *)NULL;
   XTextProperty *window_name_p = (XTextProperty *)NULL;
@@ -698,6 +739,40 @@ set_wm_properties(const WindowProperties &properties) {
     wm_hints_p->flags = StateHint;
     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
   // If we asked for a window without a border, there's no excellent
   // way to arrange that.  For users whose window managers follow the
   // way to arrange that.  For users whose window managers follow the
   // EWMH specification, we can ask for a "splash" screen, which is
   // 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 = XAllocClassHint();
     class_hints_p->res_class = "Undecorated";
     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;
+
+    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;
 
 
-    XChangeProperty(_display, _xwindow, _net_wm_window_type,
-                    XA_ATOM, 32, PropModeReplace,
-                    (unsigned char *)data, 1);
+    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,
   XSetWMProperties(_display, _xwindow, window_name_p, window_name_p,

+ 8 - 1
panda/src/glxdisplay/glxGraphicsWindow.h

@@ -54,7 +54,8 @@ protected:
   virtual bool open_window();
   virtual bool open_window();
 
 
 private:
 private:
-  void set_wm_properties(const WindowProperties &properties);
+  void set_wm_properties(const WindowProperties &properties,
+                         bool already_mapped);
 
 
 #ifdef HAVE_GLXFBCONFIG
 #ifdef HAVE_GLXFBCONFIG
   void setup_colormap(GLXFBConfig fbconfig);
   void setup_colormap(GLXFBConfig fbconfig);
@@ -82,6 +83,12 @@ private:
   Atom _net_wm_window_type;
   Atom _net_wm_window_type;
   Atom _net_wm_window_type_splash;
   Atom _net_wm_window_type_splash;
   Atom _net_wm_window_type_fullscreen;
   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:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {