Small fix in GHOST X11 system.
authorDiego Borghetti <bdiego@gmail.com>
Mon, 15 Sep 2008 18:23:34 +0000 (18:23 +0000)
committerDiego Borghetti <bdiego@gmail.com>
Mon, 15 Sep 2008 18:23:34 +0000 (18:23 +0000)
* Fix and a little of cleanup to the full screen, minimzed and
  maximized code.

* Fix bad argument in the ClientMessage event to support the
  _NET_ACTIVE_WINDOW property.

* Fix focus problem in some WM (like TWM), this is because Blender
  don't set the WM_PROTOCOLS list, now it does, a very basic list
  but it's what we need now.

intern/ghost/intern/GHOST_SystemX11.cpp
intern/ghost/intern/GHOST_SystemX11.h
intern/ghost/intern/GHOST_WindowX11.cpp
intern/ghost/intern/GHOST_WindowX11.h

index 1b90831986d57d6634ab4c25ff99400a1e5a4312..047bc6545591cb935a23996af65743cf913aa001 100644 (file)
@@ -96,15 +96,26 @@ GHOST_SystemX11(
        if (!m_display) return;
        
 #ifdef __sgi
-       m_delete_window_atom 
-         = XSGIFastInternAtom(m_display,
+       m_delete_window_atom = XSGIFastInternAtom(m_display,
                               "WM_DELETE_WINDOW", 
                               SGI_XA_WM_DELETE_WINDOW, False);
 #else
-       m_delete_window_atom 
-         = XInternAtom(m_display, "WM_DELETE_WINDOW", True);
+       m_delete_window_atom = XInternAtom(m_display, "WM_DELETE_WINDOW", False);
 #endif
 
+       m_wm_protocols= XInternAtom(m_display, "WM_PROTOCOLS", False);
+       m_wm_take_focus= XInternAtom(m_display, "WM_TAKE_FOCUS", False);
+       m_wm_state= XInternAtom(m_display, "WM_STATE", False);
+       m_wm_change_state= XInternAtom(m_display, "WM_CHANGE_STATE", False);
+       m_net_state= XInternAtom(m_display, "_NET_WM_STATE", False);
+       m_net_max_horz= XInternAtom(m_display,
+                               "_NET_WM_STATE_MAXIMIZED_HORZ", False);
+       m_net_max_vert= XInternAtom(m_display,
+                               "_NET_WM_STATE_MAXIMIZED_VERT", False);
+       m_net_fullscreen= XInternAtom(m_display,
+                               "_NET_WM_STATE_FULLSCREEN", False);
+       m_motif= XInternAtom(m_display, "_MOTIF_WM_HINTS", False);
+
        // compute the initial time
        timeval tv;
        if (gettimeofday(&tv,NULL) == -1) {
@@ -477,7 +488,7 @@ GHOST_SystemX11::processEvent(XEvent *xe)
                        XClientMessageEvent & xcme = xe->xclient;
 
 #ifndef __sgi                  
-                       if (xcme.data.l[0] == m_delete_window_atom) {
+                       if (((Atom)xcme.data.l[0]) == m_delete_window_atom) {
                                g_event = new 
                                GHOST_Event(    
                                        getMilliSeconds(),
@@ -511,6 +522,14 @@ GHOST_SystemX11::processEvent(XEvent *xe)
                                                                      GHOST_kEventNDOFButton,
                                                                      window, data);
                                }
+                       }
+                       else if (((Atom)xcme.data.l[0]) == m_wm_take_focus) {
+                               /* as ICCCM say, we need reply this event
+                                * with a SetInputFocus, the data[1] have
+                                * the valid timestamp (send by the window
+                                * manager).
+                                */
+                               XSetInputFocus(m_display, xcme.window, RevertToParent, xcme.data.l[1]);
                        } else {
                                /* Unknown client message, ignore */
                        }
@@ -528,6 +547,24 @@ GHOST_SystemX11::processEvent(XEvent *xe)
                        // XCrossingEvents pointer leave enter window.
                        break;
                case MapNotify:
+                       /*
+                        * From ICCCM:
+                        * [ Clients can select for StructureNotify on their
+                        *   top-level windows to track transition between
+                        *   Normal and Iconic states. Receipt of a MapNotify
+                        *   event will indicate a transition to the Normal
+                        *   state, and receipt of an UnmapNotify event will
+                        *   indicate a transition to the Iconic state. ]
+                        */
+                       if (window->m_post_init == True) {
+                               /*
+                                * Now we are sure that the window is
+                                * mapped, so only need change the state.
+                                */
+                               window->setState(window->m_post_state);
+                               window->m_post_init= False;
+                       }
+                       break;
                case UnmapNotify:
                        break;
                case MappingNotify:
index c67f7d81e6027f9fd2bac58d31bb482feb07e9dc..683337b47e1465ad1424e71bdeca8ade15f1200d 100644 (file)
@@ -215,15 +215,27 @@ public:
         */
        virtual void putClipboard(GHOST_TInt8 *buffer, int flag) const;
 
-private :
-
-       Display * m_display;
-
        /**
-        * Atom used to detect window close events
+        * Atom used for ICCCM, WM-spec and Motif.
+        * We only need get this atom at the start, it's relative
+        * to the display not the window and are public for every
+        * window that need it.
         */
+       Atom m_wm_protocols;
+       Atom m_wm_take_focus;
+       Atom m_wm_state;
+       Atom m_wm_change_state;
+       Atom m_net_state;
+       Atom m_net_max_horz;
+       Atom m_net_max_vert;
+       Atom m_net_fullscreen;
+       Atom m_motif;
        Atom m_delete_window_atom;
 
+private :
+
+       Display * m_display;
+
        /// The vector of windows that need to be updated.
        std::vector<GHOST_WindowX11 *> m_dirty_windows;
 
index 7b0606c40b7ce7dab306f8db1870f7ac72d4f18c..2f7aee95b82fb5ed372df816fa69d2943822fe71 100644 (file)
@@ -54,6 +54,16 @@ typedef struct {
 
 #define MWM_HINTS_DECORATIONS         (1L << 1)
 
+/*
+ * A client can't change the window property, that is the
+ * work of the window manager. We send a ClientMessage
+ * event to the Root window with the property
+ * and the Action (WM-spec define this):
+ */
+#define _NET_WM_STATE_REMOVE 0
+#define _NET_WM_STATE_ADD 1
+#define _NET_WM_STATE_TOGGLE 2
+
 /*
 import bpy
 I = bpy.data.images['blender.png'] # the 48x48 icon
@@ -154,7 +164,8 @@ GHOST_WindowX11(
        
        // Set up the minimum atrributes that we require and see if
        // X can find us a visual matching those requirements.
-
+       Atom atoms[2];
+       int natom;
        int attributes[40], i = 0;
        
        
@@ -228,7 +239,6 @@ GHOST_WindowX11(
                Window root_return;
                int x_return,y_return;
                unsigned int w_return,h_return,border_w_return,depth_return;
-               GHOST_TInt32 screen_x, screen_y;
                
                XGetGeometry(m_display, parentWindow, &root_return, &x_return, &y_return,
                        &w_return, &h_return, &border_w_return, &depth_return );
@@ -262,46 +272,26 @@ GHOST_WindowX11(
        // Are we in fullscreen mode - then include
        // some obscure blut code to remove decorations.
 
-       if (state == GHOST_kWindowStateFullScreen) {
+       /*
+        * One of the problem with WM_spec is that can't set a property
+        * to a window that isn't mapped. That is why we can't "just
+        * call setState" here.
+        *
+        * To fix this, we first need know that the window is really
+        * mapped waiting for the MapNotify event.
+        *
+        * So, m_post_init indicate that we need wait for the MapNotify
+        * event and then set the window state to the m_post_state.
+        */
+       if ((state != GHOST_kWindowStateNormal) && (state != GHOST_kWindowStateMinimized)) {
+               m_post_init = True;
+               m_post_state = state;
+       }
+       else {
+               m_post_init = False;
+               m_post_state = GHOST_kWindowStateNormal;
+       }
 
-               MotifWmHints hints;
-               Atom atom;
-                                       
-               atom = XInternAtom(m_display, "_MOTIF_WM_HINTS", False);
-               
-               if (atom == None) {
-                       GHOST_PRINT("Could not intern X atom for _MOTIF_WM_HINTS.\n");
-               } else {
-                       hints.flags = MWM_HINTS_DECORATIONS;
-                       hints.decorations = 0;  /* Absolutely no decorations. */
-                       // other hints.decorations make no sense
-                       // you can't select individual decorations
-
-                       XChangeProperty(m_display, m_window,
-                               atom, atom, 32,
-                               PropModeReplace, (unsigned char *) &hints, 4);
-               }
-       } else if (state == GHOST_kWindowStateMaximized) {
-               // With this, xprop should report the following just after launch
-               // _NET_WM_STATE(ATOM) = _NET_WM_STATE_MAXIMIZED_VERT, _NET_WM_STATE_MAXIMIZED_HORZ
-               // After demaximization the right side is empty, though (maybe not the most correct then?)
-               Atom state, atomh, atomv;
-
-               state = XInternAtom(m_display, "_NET_WM_STATE", False);
-               atomh = XInternAtom(m_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
-               atomv = XInternAtom(m_display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
-               if (state == None ) {
-                       GHOST_PRINT("Atom _NET_WM_STATE requested but not avaliable nor created.\n");
-               } else {
-                       XChangeProperty(m_display, m_window,
-                               state, XA_ATOM, 32,
-                               PropModeAppend, (unsigned char *) &atomh, 1);
-                       XChangeProperty(m_display, m_window,
-                               state, XA_ATOM, 32,
-                               PropModeAppend, (unsigned char *) &atomv, 1);
-               }
-       }
-       
        // Create some hints for the window manager on how
        // we want this window treated. 
 
@@ -324,6 +314,25 @@ GHOST_WindowX11(
        free(wmclass);
        XFree(xclasshint);
 
+       /* The basic for a good ICCCM "work" */
+       if (m_system->m_wm_protocols) {
+               natom= 0;
+
+               if (m_system->m_delete_window_atom) {
+                       atoms[natom]= m_system->m_delete_window_atom;
+                       natom++;
+               }
+
+               if (m_system->m_wm_take_focus) {
+                       atoms[natom]= m_system->m_wm_take_focus;
+                       natom++;
+               }
+
+               if (natom) {
+                       /* printf("Register atoms: %d\n", natom); */
+                       XSetWMProtocols(m_display, m_window, atoms, natom);
+               }
+       }
 
        // Set the window icon
        XWMHints *xwmhints = XAllocWMHints();
@@ -645,28 +654,298 @@ clientToScreen(
        outY = ay;
 }
 
+void GHOST_WindowX11::icccmSetState(int state)
+{
+       XEvent xev;
 
-       GHOST_TWindowState 
-GHOST_WindowX11::
-getState(
-) const {
-       //FIXME 
-       return GHOST_kWindowStateNormal;
+       if (state != IconicState)
+               return;
+
+       xev.xclient.type = ClientMessage;
+       xev.xclient.serial = 0;
+       xev.xclient.send_event = True;
+       xev.xclient.display = m_display;
+       xev.xclient.window = m_window;
+       xev.xclient.format = 32;
+       xev.xclient.message_type = m_system->m_wm_change_state;
+       xev.xclient.data.l[0] = state;
+       XSendEvent (m_display, RootWindow(m_display, DefaultScreen(m_display)),
+               False, SubstructureNotifyMask | SubstructureRedirectMask, &xev);
 }
 
-       GHOST_TSuccess 
-GHOST_WindowX11::
-setState(
-       GHOST_TWindowState state
-){
-       //TODO
+int GHOST_WindowX11::icccmGetState(void) const
+{
+       unsigned char *prop_ret;
+       unsigned long bytes_after, num_ret;
+       Atom type_ret;
+       int format_ret, st;
+
+       prop_ret = NULL;
+       st = XGetWindowProperty(m_display, m_window, m_system->m_wm_state, 0,
+                       0x7fffffff, False, m_system->m_wm_state, &type_ret,
+                       &format_ret, &num_ret, &bytes_after, &prop_ret);
+
+       if ((st == Success) && (prop_ret) && (num_ret == 2))
+               st = prop_ret[0];
+       else
+               st = NormalState;
+
+       if (prop_ret)
+               XFree(prop_ret);
+       return (st);
+}
+
+void GHOST_WindowX11::netwmMaximized(bool set)
+{
+       XEvent xev;
+
+       xev.xclient.type= ClientMessage;
+       xev.xclient.serial = 0;
+       xev.xclient.send_event = True;
+       xev.xclient.window = m_window;
+       xev.xclient.message_type = m_system->m_net_state;
+       xev.xclient.format = 32;
+
+       if (set == True)
+               xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
+       else
+               xev.xclient.data.l[0] = _NET_WM_STATE_REMOVE;
+
+       xev.xclient.data.l[1] = m_system->m_net_max_horz;
+       xev.xclient.data.l[2] = m_system->m_net_max_vert;
+       xev.xclient.data.l[3] = 0;
+       xev.xclient.data.l[4] = 0;
+       XSendEvent (m_display, RootWindow(m_display, DefaultScreen(m_display)),
+               False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+}
+
+bool GHOST_WindowX11::netwmIsMaximized(void) const
+{
+       unsigned char *prop_ret;
+       unsigned long bytes_after, num_ret, i;
+       Atom type_ret;
+       bool st;
+       int format_ret, count;
+
+       prop_ret = NULL;
+       st = False;
+       i = XGetWindowProperty(m_display, m_window, m_system->m_net_state, 0,
+                       0x7fffffff, False, XA_ATOM, &type_ret, &format_ret,
+                       &num_ret, &bytes_after, &prop_ret);
+       if ((i == Success) && (prop_ret) && (format_ret == 32)) {
+               count = 0;
+               for (i = 0; i < num_ret; i++) {
+                       if (((unsigned long *) prop_ret)[i] == m_system->m_net_max_horz)
+                               count++;
+                       if (((unsigned long *) prop_ret)[i] == m_system->m_net_max_vert)
+                               count++;
+                       if (count == 2) {
+                               st = True;
+                               break;
+                       }
+               }
+       }
+
+       if (prop_ret)
+               XFree(prop_ret);
+       return (st);
+}
+
+void GHOST_WindowX11::netwmFullScreen(bool set)
+{
+       XEvent xev;
+
+       xev.xclient.type = ClientMessage;
+       xev.xclient.serial = 0;
+       xev.xclient.send_event = True;
+       xev.xclient.window = m_window;
+       xev.xclient.message_type = m_system->m_net_state;
+       xev.xclient.format = 32;
+
+       if (set == True)
+               xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
+       else
+               xev.xclient.data.l[0] = _NET_WM_STATE_REMOVE;
+
+       xev.xclient.data.l[1] = m_system->m_net_fullscreen;
+       xev.xclient.data.l[2] = 0;
+       xev.xclient.data.l[3] = 0;
+       xev.xclient.data.l[4] = 0;
+       XSendEvent (m_display, RootWindow(m_display, DefaultScreen(m_display)),
+               False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+}
+
+bool GHOST_WindowX11::netwmIsFullScreen(void) const
+{
+       unsigned char *prop_ret;
+       unsigned long bytes_after, num_ret, i;
+       Atom type_ret;
+       bool st;
+       int format_ret;
+
+       prop_ret = NULL;
+       st = False;
+       i = XGetWindowProperty(m_display, m_window, m_system->m_net_state, 0,
+                       0x7fffffff, False, XA_ATOM, &type_ret, &format_ret,
+                       &num_ret, &bytes_after, &prop_ret);
+       if ((i == Success) && (prop_ret) && (format_ret == 32)) {
+               for (i = 0; i < num_ret; i++) {
+                       if (((unsigned long *)prop_ret)[i] == m_system->m_net_fullscreen) {
+                               st = True;
+                               break;
+                       }
+               }
+       }
+
+       if (prop_ret)
+               XFree(prop_ret);
+       return (st);
+}
+
+void GHOST_WindowX11::motifFullScreen(bool set)
+{
+       MotifWmHints hints;
+
+       hints.flags = MWM_HINTS_DECORATIONS;
+       if (set == True)
+               hints.decorations = 0;
+       else
+               hints.decorations = 1;
+
+       XChangeProperty(m_display, m_window, m_system->m_motif,
+               m_system->m_motif, 32, PropModeReplace,
+               (unsigned char *)&hints, 4);
+}
+
+bool GHOST_WindowX11::motifIsFullScreen(void) const
+{
+       unsigned char *prop_ret;
+       unsigned long bytes_after, num_ret;
+       MotifWmHints *hints;
+       Atom type_ret;
+       bool state;
+       int format_ret, st;
+
+       prop_ret = NULL;
+       state = False;
+       st = XGetWindowProperty(m_display, m_window, m_system->m_motif, 0,
+                       0x7fffffff, False, m_system->m_motif,
+                       &type_ret, &format_ret, &num_ret,
+                       &bytes_after, &prop_ret);
+       if ((st == Success) && (prop_ret)) {
+               hints = (MotifWmHints *)prop_ret;
+               if (hints->flags & MWM_HINTS_DECORATIONS) {
+                       if (!hints->decorations)
+                               state = True;
+               }
+       }
 
-        if (state == (int)getState()) {
+       if (prop_ret)
+               XFree(prop_ret);
+       return (state);
+}
+
+GHOST_TWindowState GHOST_WindowX11::getState() const
+{
+       GHOST_TWindowState state_ret;
+       int state;
+
+       state_ret = GHOST_kWindowStateNormal;
+       state = icccmGetState();
+       /*
+        * In the Iconic and Withdrawn state, the window is
+        * unmaped, so only need return a Minimized state.
+        */
+       if ((state == IconicState) || (state == WithdrawnState))
+               state_ret = GHOST_kWindowStateMinimized;
+       else if (netwmIsMaximized() == True)
+               state_ret = GHOST_kWindowStateMaximized;
+       else if (netwmIsFullScreen() == True)
+               state_ret = GHOST_kWindowStateFullScreen;
+       else if (motifIsFullScreen() == True)
+               state_ret = GHOST_kWindowStateFullScreen;
+       return (state_ret);
+}
+
+GHOST_TSuccess GHOST_WindowX11::setState(GHOST_TWindowState state)
+{
+       GHOST_TWindowState cur_state;
+       bool is_max, is_full, is_motif_full;
+
+       cur_state = getState();
+       if (state == (int)cur_state)
                return GHOST_kSuccess;
-       } else {
-               return GHOST_kFailure;
+
+       if (cur_state != GHOST_kWindowStateMinimized) {
+               /*
+                * The window don't have this property's
+                * if it's not mapped.
+                */
+               is_max = netwmIsMaximized();
+               is_full = netwmIsFullScreen();
+       }
+       else {
+               is_max = False;
+               is_full = False;
+       }
+
+       is_motif_full = motifIsFullScreen();
+
+       if (state == GHOST_kWindowStateNormal) {
+               if (is_max == True)
+                       netwmMaximized(False);
+               if (is_full == True)
+                       netwmFullScreen(False);
+               if (is_motif_full == True)
+                       motifFullScreen(False);
+               icccmSetState(NormalState);
+               return (GHOST_kSuccess);
+       }
+
+       if (state == GHOST_kWindowStateFullScreen) {
+               /*
+                * We can't change to full screen if the window
+                * isn't mapped.
+                */
+               if (cur_state == GHOST_kWindowStateMinimized)
+                       return (GHOST_kFailure);
+
+               if (is_max == True)
+                       netwmMaximized(False);
+               if (is_full == False)
+                       netwmFullScreen(True);
+               if (is_motif_full == False)
+                       motifFullScreen(True);
+               return (GHOST_kSuccess);
+       }
+
+       if (state == GHOST_kWindowStateMaximized) {
+               /*
+                * We can't change to Maximized if the window
+                * isn't mapped.
+                */
+               if (cur_state == GHOST_kWindowStateMinimized)
+                       return (GHOST_kFailure);
+
+               if (is_full == True)
+                       netwmFullScreen(False);
+               if (is_motif_full == True)
+                       motifFullScreen(False);
+               if (is_max == False)
+                       netwmMaximized(True);
+               return (GHOST_kSuccess);
+       }
+
+       if (state == GHOST_kWindowStateMinimized) {
+               /*
+                * The window manager need save the current state of
+                * the window (maximized, full screen, etc).
+                */
+               icccmSetState(IconicState);
+               return (GHOST_kSuccess);
        }
 
+       return (GHOST_kFailure);
 }
 
 #include <iostream>
@@ -701,9 +980,9 @@ setOrder(
                        xev.xclient.message_type = atom;
 
                        xev.xclient.format = 32;
-                       xev.xclient.data.l[0] = 0;
-                       xev.xclient.data.l[1] = 0;
-                       xev.xclient.data.l[2] = 0;
+                       xev.xclient.data.l[0] = 1;
+                       xev.xclient.data.l[1] = CurrentTime;
+                       xev.xclient.data.l[2] = m_window;
                        xev.xclient.data.l[3] = 0;
                        xev.xclient.data.l[4] = 0;
 
index abb5c131cb74fa1cfac0483abfe95b7cae78d0e1..1d73faaf3f611ed4ad2f1816a26f2184e357119c 100644 (file)
@@ -212,6 +212,15 @@ public:
 
        const GHOST_TabletData* GetTabletData()
        { return &m_xtablet.CommonData; }
+
+       /*
+        * Need this in case that we want start the window
+        * in FullScreen or Maximized state.
+        * Check GHOST_WindowX11.cpp
+        */
+       bool m_post_init;
+       GHOST_TWindowState m_post_state;
+
 protected:
        /**
         * Tries to install a rendering context in this window.
@@ -327,6 +336,18 @@ private :
 
        /* Tablet devices */
        XTablet m_xtablet;
+
+       void icccmSetState(int state);
+       int icccmGetState() const;
+
+       void netwmMaximized(bool set);
+       bool netwmIsMaximized() const;
+
+       void netwmFullScreen(bool set);
+       bool netwmIsFullScreen() const;
+
+       void motifFullScreen(bool set);
+       bool motifIsFullScreen() const;
 };