ecere/gui/drivers/XInterface: Centralized function to set _NET_WM_STATE hints
[sdk] / ecere / src / gui / drivers / XInterface.ec
index bdb741f..bdc9585 100644 (file)
@@ -95,6 +95,8 @@ static int joystickFD[4];
 static X11Window activeWindow;
 static X11Cursor systemCursors[SystemCursor];
 
+static enum NETWMStateAction { remove = 0, add = 1, toggle = 2 };
+
 static enum AtomIdents
 {
    clipboard, multiple, targets, utf8_string, wm_delete_window, wm_hints, wm_name, wm_protocols, wm_state, wm_take_focus, wm_transient_for,
@@ -174,6 +176,70 @@ PixelFormat xSystemPixelFormat;
 Visual * xSystemVisual;
 bool xSharedMemory;
 
+static void SetNETWMState(X11Window windowHandle, bool throughRoot, NETWMStateAction action, Atom atom1, Atom atom2)
+{
+   if(atomsSupported[_net_wm_state])
+   {
+      int format;
+      unsigned long count, fill;
+      Atom type;
+      char * data = null;
+      uint state = WithdrawnState;
+
+      /*if(XGetWindowProperty(xGlobalDisplay, windowHandle, atoms[wm_state], 0, 3, False,
+                 atoms[wm_state], &type, &format, &count, &fill, &data) == Success && count)
+      {
+         state = *(uint *)data;
+         XFree(data);
+      } */
+      if(!throughRoot) //state == WithdrawnState)
+      {
+         // We need to handle modifying these ourselves for withdrawn windows...
+         if(action == add)
+         {
+            Atom values[2] = { atom1, atom2 };
+            XChangeProperty(xGlobalDisplay, windowHandle, atoms[_net_wm_state], XA_ATOM,
+               32, PropModeAppend, (byte *)values, atom2 ? 2 : 1);
+         }
+         else if(XGetWindowProperty(xGlobalDisplay, windowHandle, atoms[_net_wm_state], 0, 32, False,
+             XA_ATOM, &type, &format, &count, &fill, &data) == Success)
+         {
+            Atom * values = (Atom *) data;
+            int i;
+            for (i = 0; i < count; i++)
+            {
+               if(values[i] == atom1 || (atom2 && values[i] == atom2))
+               {
+                  if(i < count - 1)
+                     memmove(values + i, values + i + 1, sizeof(Atom) * (count - i - 1));
+                  count--;
+                  i--;
+               }
+            }
+            XChangeProperty(xGlobalDisplay, windowHandle, atoms[_net_wm_state], XA_ATOM, 32, PropModeReplace, (byte *)values, (int)count);
+            XFree(data);
+         }
+      }
+      else
+      {
+         XClientMessageEvent event = { 0 };
+         event.type = ClientMessage;
+         event.message_type = atoms[_net_wm_state];
+         event.display = xGlobalDisplay;
+         event.serial = 0;
+         event.window = windowHandle;
+         event.send_event = 1;
+         event.format = 32;
+         event.data.l[0] = action;
+         event.data.l[1] = atom1;
+         event.data.l[2] = atom2;
+         XSendEvent(xGlobalDisplay, DefaultRootWindow(xGlobalDisplay), bool::false,
+            SubstructureRedirectMask | SubstructureNotifyMask, (union _XEvent *)&event);
+      }
+   }
+
+}
+
 static Time timeStamp;
 
 class XWindowData : struct
@@ -1042,13 +1108,25 @@ static bool GetFrameExtents(Window window, bool update)
                XInterface::UpdateRootWindow(window);
             }
          }
+         result = true;
       }
       XFree(data);
-      result = true;
    }
    return result;
 }
 
+static bool WaitForFrameExtents(Window window)
+{
+   int attempts = 0;
+   //XFlush(xGlobalDisplay);
+   while(attempts++ < 10)
+   {
+      if(GetFrameExtents(window, false)) return true;
+      Sleep(1.0 / RESOLUTION);
+   }
+   return false;
+}
+
 /****************************************************************************
    /// DRIVER IMPLEMENTATION /////////////
 ****************************************************************************/
@@ -1842,10 +1920,11 @@ class XInterface : Interface
                case ConfigureNotify:
                {
                   XConfigureEvent * event = (XConfigureEvent *) thisEvent;
+                  bool unmaximized = false;
                   while(XCheckIfEvent(xGlobalDisplay, (XEvent *)thisEvent, (void *)ConfigureNotifyChecker, (void *)window.windowHandle));
                   //if(event->x - desktopX != window.position.x || event->y - desktopY != window.position.y || event->width != window.size.w || event->height != window.size.h)
 
-                  if(window.nativeDecorations)
+                  if(atomsSupported[_net_wm_state]) //window.nativeDecorations)
                   {
                      int format;
                      unsigned long len, fill;
@@ -1897,36 +1976,56 @@ class XInterface : Interface
                      }
                   }
                   {
-                     int x = event->x;
-                     int y = event->y;
-                     int w = event->width, h = event->height;
-
-                     //if(event->send_event)
+                     int x, y, w, h;
+                     if(unmaximized)
                      {
-                        X11Window rootChild;
-                        int rootX, rootY;
-                        XTranslateCoordinates(xGlobalDisplay, event->window,
-                           RootWindow(xGlobalDisplay, DefaultScreen(xGlobalDisplay)), 0, 0,
-                           &rootX, &rootY, &rootChild);
-                        x = rootX;
-                        y = rootY;
+                        // Ensure we set the normal size anchor when un-maximizing
+                        if(window.nativeDecorations && RequestFrameExtents(window))
+                           WaitForFrameExtents(window);
+                        x = window.position.x, y = window.position.y, w = window.size.w, h = window.size.h;
+                        window.ComputeAnchors(window.normalAnchor, window.normalSizeAnchor, &x, &y, &w, &h);
                      }
+                     else
+                     {
+                        x = event->x;
+                        y = event->y;
+                        w = event->width, h = event->height;
+
+                        //if(event->send_event)
+                        {
+                           X11Window rootChild;
+                           int rootX, rootY;
+                           XTranslateCoordinates(xGlobalDisplay, event->window,
+                              RootWindow(xGlobalDisplay, DefaultScreen(xGlobalDisplay)), 0, 0,
+                              &rootX, &rootY, &rootChild);
+                           x = rootX;
+                           y = rootY;
+                        }
 
-                     x -= desktopX;
-                     y -= desktopY;
+                        x -= desktopX;
+                        y -= desktopY;
 
-                     if(window.nativeDecorations && window.state != maximized)
+                        if(window.nativeDecorations && window.state != maximized)
+                        {
+                           x -= windowData.decor.left;
+                           y -= windowData.decor.top;
+                           w += windowData.decor.left + windowData.decor.right;
+                           h += windowData.decor.top + windowData.decor.bottom;
+                           /*
+                           x -= window.clientStart.x;
+                           y -= window.clientStart.y - (window.hasMenuBar ? skinMenuHeight : 0);
+                           w += window.size.w - window.clientSize.w;
+                           h += window.size.h - window.clientSize.h;
+                           */
+                        }
+                     }
+
+                     // Break the anchors for moveable/resizable windows
+                     if(window.style.fixed && window.state == normal)
                      {
-                        x -= windowData.decor.left;
-                        y -= windowData.decor.top;
-                        w += windowData.decor.left + windowData.decor.right;
-                        h += windowData.decor.top + windowData.decor.bottom;
-                        /*
-                        x -= window.clientStart.x;
-                        y -= window.clientStart.y - (window.hasMenuBar ? skinMenuHeight : 0);
-                        w += window.size.w - window.clientSize.w;
-                        h += window.size.h - window.clientSize.h;
-                        */
+                        window.normalAnchor = Anchor { left = x, top = y };
+                        window.normalSizeAnchor = SizeAnchor { { w, h } };
+                        window.anchored = false;
                      }
 
                      // Break the anchors for moveable/resizable windows
@@ -1937,7 +2036,7 @@ class XInterface : Interface
                         window.anchored = false;
                      }
 
-                     window.Position(x, y, w, h, true, true, true, true, false, false);
+                     window.Position(x, y, w, h, true, true, true, true, false, unmaximized);
                   }
                   break;
                }
@@ -2280,9 +2379,14 @@ class XInterface : Interface
          else
          {
             X11Window parentWindow = (X11Window)null;
-            int x = window.position.x, y = window.position.y;
+            int x = window.position.x + desktopX, y = window.position.y + desktopY;
             int w = window.state == normal ? Max(1, window.size.w) : Max(1, window.normalSizeAnchor.size.w);
             int h = window.state == normal ? Max(1, window.size.h) : Max(1, window.normalSizeAnchor.size.h);
+            if(!window.nativeDecorations && window.state != normal)
+            {
+               w += window.size.w - window.clientSize.w;
+               h += window.size.h - window.clientSize.h;
+            }
 
             if(window.master.rootWindow && window.master.rootWindow != guiApp.desktop && (window._isModal || window.style.interim))
             {
@@ -2309,10 +2413,7 @@ class XInterface : Interface
                //printf("Done.\n");
                //XChangeProperty(xGlobalDisplay, windowHandle, atoms[wm_transient_for], XA_WINDOW, 32, PropModeReplace, (unsigned char*)&parentWindow, 1);
                if(window.isModal)
-               {
-                  Atom hints[1] = { atoms[_net_wm_state_modal] };
-                  XChangeProperty(xGlobalDisplay, windowHandle, atoms[_net_wm_state], XA_ATOM, 32, PropModeReplace, (unsigned char*)&hints, 1);
-               }
+                  SetNETWMState(windowHandle, false, add, atoms[_net_wm_state_modal], 0);
             }
 
             {
@@ -2531,7 +2632,17 @@ class XInterface : Interface
             // Was commenting this out was part of #700/#795 fix, but this causes jumping of e.g. About box after getting frame extents PropertyNotify
 
                // && window.state != maximized -- required for Cinnamon on Mint 14/15
-            if(!windowData.gotFrameExtents && window.state != maximized) return;
+            if(!windowData.gotFrameExtents && window.state != maximized)
+            {
+               if(WaitForFrameExtents(window))
+               {
+                  x += windowData.decor.left;
+                  y += windowData.decor.top ;
+
+                  w += windowData.decor.left + windowData.decor.right;
+                  h += windowData.decor.top  + windowData.decor.bottom;
+               }
+            }
 
             x -= windowData.decor.left;
             y -= windowData.decor.top;
@@ -2540,22 +2651,25 @@ class XInterface : Interface
             h -= windowData.decor.top + windowData.decor.bottom;
 
             // Tweak for first unmaximize on Unity on Ubuntu 11.10
-            if(window.state == maximized && (desktopX + w > desktopW || desktopY + h > desktopH))
+            /*if(window.state == maximized && (desktopX + w > desktopW || desktopY + h > desktopH))
             {
                w -= 40;
                h -= 40;
-            }
+            }*/
          }
 
          x += desktopX;
          y += desktopY;
 
-         if(move && resize)
-            XMoveResizeWindow(xGlobalDisplay, (X11Window)window.windowHandle, x, y, w, h);
-         else if(move)
-            XMoveWindow(xGlobalDisplay, (X11Window)window.windowHandle, x, y);
-         else if(resize)
-            XResizeWindow(xGlobalDisplay, (X11Window)window.windowHandle, w, h);
+         if(!atomsSupported[_net_wm_state] || window.state != maximized)
+         {
+            if(move && resize)
+               XMoveResizeWindow(xGlobalDisplay, (X11Window)window.windowHandle, x, y, w, h);
+            else if(move)
+               XMoveWindow(xGlobalDisplay, (X11Window)window.windowHandle, x, y);
+            else if(resize)
+               XResizeWindow(xGlobalDisplay, (X11Window)window.windowHandle, w, h);
+         }
 #if defined(__APPLE__)
 //         if(window.created && !visible)
   //          XUnmapWindow(xGlobalDisplay, (X11Window)window.windowHandle);
@@ -2618,10 +2732,8 @@ class XInterface : Interface
             if(state == minimized && atomsSupported[_net_wm_state])
             {
                uint iconic = IconicState;
-               /*
-               XChangeProperty(xGlobalDisplay, window.windowHandle, atoms[_net_wm_state], XA_ATOM, 32,
-                  PropModeReplace, (unsigned char*)&atoms[_net_wm_state_hidden], 1);
-               */
+
+               // SetNETWMState(window.windowHandle, true, add, atoms[_net_wm_state_hidden], null);
                /*
                XChangeProperty(xGlobalDisplay, window.windowHandle, atoms[wm_state], XA_CARDINAL, 32,
                   PropModeReplace, &iconic, 1);
@@ -2630,19 +2742,6 @@ class XInterface : Interface
                /*
                XClientMessageEvent event = { 0 };
                event.type = ClientMessage;
-               event.message_type = atoms[_net_wm_state];
-               event.display = xGlobalDisplay;
-               event.serial = 0;
-               event.window = window.windowHandle;
-               event.send_event = 1;
-               event.format = 32;
-               event.data.l[0] = 2; // 1;
-               event.data.l[1] = atoms[_net_wm_state_hidden];
-               XSendEvent(xGlobalDisplay, DefaultRootWindow(xGlobalDisplay), bool::false, SubstructureRedirectMask | SubstructureNotifyMask, &event);
-               */
-               /*
-               XClientMessageEvent event = { 0 };
-               event.type = ClientMessage;
                event.message_type = atoms[wm_state];
                event.display = xGlobalDisplay;
                event.window = window.windowHandle;
@@ -2657,10 +2756,11 @@ class XInterface : Interface
             }
             else
             {
-               if(!atomsSupported[_net_wm_state] || !window.nativeDecorations || (!((XWindowData)window.windowData).gotFrameExtents && window.state == maximized)) //((XWindowData)window.windowData).gotFrameExtents && (!window.nativeDecorations || window.state == state))
+               //((XWindowData)window.windowData).gotFrameExtents && (!window.nativeDecorations || window.state == state))
+               if(!atomsSupported[_net_wm_state] || (!((XWindowData)window.windowData).gotFrameExtents && window.state == maximized))
                {
-                  // With native decorations, we do it the first time
-                  // or the WM (Gnome) is sticking it to the top/right!
+                  // Running this block avoids the initial IDE maximized->unmaximized flicker
+                  //if(window.state != maximized || !atomsSupported[_net_wm_state] || window.nativeDecorations)
                   {
                      int x = window.position.x;
                      int y = window.position.y;
@@ -2688,19 +2788,15 @@ class XInterface : Interface
                if(atomsSupported[_net_wm_state])
                {
                   // Maximize / Restore the window
-                  XClientMessageEvent event = { 0 };
-                  event.type = ClientMessage;
-                  event.message_type = atoms[_net_wm_state];
-                  event.display = xGlobalDisplay;
-                  event.serial = 0;
-                  event.window = (X11Window)window.windowHandle;
-                  event.send_event = 1;
-                  event.format = 32;
-                  event.data.l[0] = (state == maximized) ? 1 : 0;
-                  event.data.l[1] = atoms[_net_wm_state_maximized_vert];
-                  event.data.l[2] = atoms[_net_wm_state_maximized_horz];
-                  XSendEvent(xGlobalDisplay, DefaultRootWindow(xGlobalDisplay), bool::false,
-                     SubstructureRedirectMask | SubstructureNotifyMask, (union _XEvent *)&event);
+                  SetNETWMState((X11Window)window.windowHandle, true, state == maximized ? add: remove,
+                     atoms[_net_wm_state_maximized_vert], atoms[_net_wm_state_maximized_horz]);
+                  if(state == maximized)
+                  {
+                     // Prevent the code in ConfigureNotify to think the window has been unmaximized
+                     // if the Window Manager hasn't set the hints yet.
+                     XFlush(xGlobalDisplay);
+                     Sleep(0.01);
+                  }
                }
             }
          }
@@ -2712,18 +2808,8 @@ class XInterface : Interface
 
    void FlashRootWindow(Window window)
    {
-      XClientMessageEvent event = { 0 };
       // printf("Attempting to flash root window\n");
-      event.type = ClientMessage;
-      event.message_type = atoms[_net_wm_state];
-      event.display = xGlobalDisplay;
-      event.serial = 0;
-      event.window = (X11Window)window.windowHandle;
-      event.send_event = 1;
-      event.format = 32;
-      event.data.l[0] = 1;
-      event.data.l[1] = atoms[_net_wm_state_demands_attention];
-      XSendEvent(xGlobalDisplay, DefaultRootWindow(xGlobalDisplay), bool::false, SubstructureRedirectMask | SubstructureNotifyMask, (union _XEvent *)&event);
+      SetNETWMState((X11Window)window.windowHandle, true, add, atoms[_net_wm_state_demands_attention], 0);
    }
 
    void ActivateRootWindow(Window window)