merge with/from trunk at r35190
[blender.git] / intern / ghost / intern / GHOST_SystemX11.cpp
index fcf78d9ad200df42c059426525d2dbebd4e4f832..09a4d9d4de262cd8a947c7e8fad44bcf26ef94af 100644 (file)
@@ -1,4 +1,4 @@
-/**
+/*
  * $Id$
  * ***** BEGIN GPL LICENSE BLOCK *****
  *
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
  * All rights reserved.
  *
  * Contributor(s): none yet.
  *
+ * Part of this code has been taken from Qt, under LGPL license
+ * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *
  * ***** END GPL LICENSE BLOCK *****
  */
 
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+/** \file ghost/intern/GHOST_SystemX11.cpp
+ *  \ingroup GHOST
+ */
+
 
 #include "GHOST_SystemX11.h"
 #include "GHOST_WindowX11.h"
 #include <X11/keysym.h>
 #include <X11/XKBlib.h> /* allow detectable autorepeate */
 
+#ifdef WITH_XF86KEYSYM
+#include <X11/XF86keysym.h>
+#endif
+
 #ifdef __sgi
 
 #if defined(_SGI_EXTRA_PREDEFINES) && !defined(NO_FAST_ATOMS)
 #include <sys/time.h>
 #include <unistd.h>
 
+#include <iostream>
 #include <vector>
 #include <stdio.h> // for fprintf only
+#include <cstdlib> // for exit
+
+#ifndef PREFIX
+#error "PREFIX not defined"
+#endif
 
 typedef struct NDOFPlatformInfo {
        Display *display;
@@ -93,7 +107,10 @@ GHOST_SystemX11(
 {
        m_display = XOpenDisplay(NULL);
        
-       if (!m_display) return;
+       if (!m_display) {
+               std::cerr << "Unable to open a display" << std::endl;
+               abort(); //was return before, but this would just mean it will crash later
+       }
        
 #ifdef __sgi
        m_delete_window_atom 
@@ -126,6 +143,8 @@ GHOST_SystemX11(
        m_xclip_out= XInternAtom(m_display, "XCLIP_OUT", False);
        m_incr= XInternAtom(m_display, "INCR", False);
        m_utf8_string= XInternAtom(m_display, "UTF8_STRING", False);
+       m_last_warp = 0;
+
 
        // compute the initial time
        timeval tv;
@@ -148,6 +167,13 @@ GHOST_SystemX11(
        
 }
 
+GHOST_SystemX11::
+~GHOST_SystemX11()
+{
+       XCloseDisplay(m_display);
+}
+
+
        GHOST_TSuccess 
 GHOST_SystemX11::
 init(
@@ -155,11 +181,9 @@ init(
        GHOST_TSuccess success = GHOST_System::init();
 
        if (success) {
-               m_keyboard_vector = new char[32];
-
                m_displayManager = new GHOST_DisplayManagerX11(this);
 
-               if (m_keyboard_vector && m_displayManager) {
+               if (m_displayManager) {
                        return GHOST_kSuccess;
                }
        }
@@ -213,6 +237,8 @@ getMainDisplayDimensions(
         * @param       height  The height the window.
         * @param       state   The state of the window when opened.
         * @param       type    The type of drawing context installed in this window.
+        * @param       stereoVisual    Stereo visual for quad buffered stereo.
+        * @param       numOfAASamples  Number of samples used for AA (zero if no AA)
         * @param       parentWindow    Parent (embedder) window
         * @return      The new window (or 0 if creation failed).
         */
@@ -227,6 +253,7 @@ createWindow(
        GHOST_TWindowState state,
        GHOST_TDrawingContextType type,
        bool stereoVisual,
+       const GHOST_TUns16 numOfAASamples,
        const GHOST_TEmbedderWindowID parentWindow
 ){
        GHOST_WindowX11 * window = 0;
@@ -305,6 +332,61 @@ static void SleepTillEvent(Display *display, GHOST_TInt64 maxSleep) {
        }
 }
 
+/* This function borrowed from Qt's X11 support
+ * qclipboard_x11.cpp
+ *  */
+struct init_timestamp_data
+{
+    Time timestamp;
+};
+
+static Bool init_timestamp_scanner(Display*, XEvent *event, XPointer arg)
+{
+       init_timestamp_data *data =
+        reinterpret_cast<init_timestamp_data*>(arg);
+    switch(event->type)
+    {
+    case ButtonPress:
+    case ButtonRelease:
+        data->timestamp = event->xbutton.time;
+        break;
+    case MotionNotify:
+        data->timestamp = event->xmotion.time;
+        break;
+    case KeyPress:
+    case KeyRelease:
+        data->timestamp = event->xkey.time;
+        break;
+    case PropertyNotify:
+        data->timestamp = event->xproperty.time;
+        break;
+    case EnterNotify:
+    case LeaveNotify:
+        data->timestamp = event->xcrossing.time;
+        break;
+    case SelectionClear:
+        data->timestamp = event->xselectionclear.time;
+        break;
+    default:
+        break;
+    }
+
+    return false;
+}
+
+Time
+GHOST_SystemX11::
+lastEventTime(Time default_time) {
+    init_timestamp_data data;
+    data.timestamp = default_time;
+    XEvent ev;
+    XCheckIfEvent(m_display, &ev, &init_timestamp_scanner, (XPointer)&data);
+
+    return data.timestamp;
+}
+
+
+
        bool 
 GHOST_SystemX11::
 processEvents(
@@ -315,6 +397,10 @@ processEvents(
        
        bool anyProcessed = false;
        
+       if (playingEvents(&anyProcessed)) {
+               return anyProcessed;
+       }
+       
        do {
                GHOST_TimerManager* timerMgr = getTimerManager();
                
@@ -369,12 +455,12 @@ GHOST_SystemX11::processEvent(XEvent *xe)
                                // Only generate a single expose event
                                // per read of the event queue.
 
-                               g_event = new 
+                               g_event = new
                                GHOST_Event(
                                        getMilliSeconds(),
                                        GHOST_kEventWindowUpdate,
                                        window
-                               );                      
+                               );
                        }
                        break;
                }
@@ -383,14 +469,54 @@ GHOST_SystemX11::processEvent(XEvent *xe)
                {
                        XMotionEvent &xme = xe->xmotion;
                        
-                       g_event = new 
-                       GHOST_EventCursor(
-                               getMilliSeconds(),
-                               GHOST_kEventCursorMove,
-                               window,
-                               xme.x_root,
-                               xme.y_root
-                       );
+                       if(window->getCursorGrabMode() != GHOST_kGrabDisable && window->getCursorGrabMode() != GHOST_kGrabNormal)
+                       {
+                               GHOST_TInt32 x_new= xme.x_root;
+                               GHOST_TInt32 y_new= xme.y_root;
+                               GHOST_TInt32 x_accum, y_accum;
+                               GHOST_Rect bounds;
+
+                               /* fallback to window bounds */
+                               if(window->getCursorGrabBounds(bounds)==GHOST_kFailure)
+                                       window->getClientBounds(bounds);
+
+                               /* could also clamp to screen bounds
+                                * wrap with a window outside the view will fail atm  */
+                               bounds.wrapPoint(x_new, y_new, 8); /* offset of one incase blender is at screen bounds */
+                               window->getCursorGrabAccum(x_accum, y_accum);
+
+                               if(x_new != xme.x_root || y_new != xme.y_root) {
+                                       if (xme.time > m_last_warp) {
+                                               /* when wrapping we don't need to add an event because the
+                                                * setCursorPosition call will cause a new event after */
+                                               setCursorPosition(x_new, y_new); /* wrap */
+                                               window->setCursorGrabAccum(x_accum + (xme.x_root - x_new), y_accum + (xme.y_root - y_new));
+                                               m_last_warp = lastEventTime(xme.time);
+                                       } else {
+                                               setCursorPosition(x_new, y_new); /* wrap but don't accumulate */
+                                       }
+                               }
+                               else {
+                                       g_event = new
+                                       GHOST_EventCursor(
+                                               getMilliSeconds(),
+                                               GHOST_kEventCursorMove,
+                                               window,
+                                               xme.x_root + x_accum,
+                                               xme.y_root + y_accum
+                                       );
+                               }
+                       }
+                       else {
+                               g_event = new
+                               GHOST_EventCursor(
+                                       getMilliSeconds(),
+                                       GHOST_kEventCursorMove,
+                                       window,
+                                       xme.x_root,
+                                       xme.y_root
+                               );
+                       }
                        break;
                }
 
@@ -423,33 +549,43 @@ GHOST_SystemX11::processEvent(XEvent *xe)
                }
 
                case ButtonPress:
-               {
-                       /* process wheel mouse events and break */
-                       if (xe->xbutton.button == 4) {
-                               g_event = new GHOST_EventWheel(getMilliSeconds(), window, 1);
-                               break;
-                       }
-                       if (xe->xbutton.button == 5) {
-                               g_event = new GHOST_EventWheel(getMilliSeconds(), window, -1);
-                               break;
-                       }
-               }
                case ButtonRelease:
                {
-
                        XButtonEvent & xbe = xe->xbutton;
                        GHOST_TButtonMask gbmask = GHOST_kButtonMaskLeft;
-
-                       switch (xbe.button) {
-                               case Button1 : gbmask = GHOST_kButtonMaskLeft; break;
-                               case Button3 : gbmask = GHOST_kButtonMaskRight; break;
-                               default:
-                               case Button2 : gbmask = GHOST_kButtonMaskMiddle; break;
-                       }
-                       
                        GHOST_TEventType type = (xbe.type == ButtonPress) ? 
                                GHOST_kEventButtonDown : GHOST_kEventButtonUp;
+
+                       /* process wheel mouse events and break, only pass on press events */
+                       if(xbe.button == Button4) {
+                               if(xbe.type == ButtonPress)
+                                       g_event = new GHOST_EventWheel(getMilliSeconds(), window, 1);
+                               break;
+                       }
+                       else if(xbe.button == Button5) {
+                               if(xbe.type == ButtonPress)
+                                       g_event = new GHOST_EventWheel(getMilliSeconds(), window, -1);
+                               break;
+                       }
                        
+                       /* process rest of normal mouse buttons */
+                       if(xbe.button == Button1)
+                               gbmask = GHOST_kButtonMaskLeft;
+                       else if(xbe.button == Button2)
+                               gbmask = GHOST_kButtonMaskMiddle;
+                       else if(xbe.button == Button3)
+                               gbmask = GHOST_kButtonMaskRight;
+                       /* It seems events 6 and 7 are for horizontal scrolling.
+                       * you can re-order button mapping like this... (swaps 6,7 with 8,9)
+                       *   xmodmap -e "pointer = 1 2 3 4 5 8 9 6 7" 
+                       */
+                       else if(xbe.button == 8)
+                               gbmask = GHOST_kButtonMaskButton4;
+                       else if(xbe.button == 9)
+                               gbmask = GHOST_kButtonMaskButton5;
+                       else
+                               break;
+
                        g_event = new
                        GHOST_EventButton(
                                getMilliSeconds(),
@@ -534,11 +670,28 @@ GHOST_SystemX11::processEvent(XEvent *xe)
                                                                      window, data);
                                }
                        } else if (((Atom)xcme.data.l[0]) == m_wm_take_focus) {
+                               XWindowAttributes attr;
+                               Window fwin;
+                               int revert_to;
+
                                /* as ICCCM say, we need reply this event
                                 * with a SetInputFocus, the data[1] have
                                 * the valid timestamp (send by the wm).
+                                *
+                                * Some WM send this event before the
+                                * window is really mapped (for example
+                                * change from virtual desktop), so we need
+                                * to be sure that our windows is mapped
+                                * or this call fail and close blender.
                                 */
-                               XSetInputFocus(m_display, xcme.window, RevertToParent, xcme.data.l[1]);
+                               if (XGetWindowAttributes(m_display, xcme.window, &attr) == True) {
+                                       if (XGetInputFocus(m_display, &fwin, &revert_to) == True) {
+                                               if (attr.map_state == IsViewable) {
+                                                       if (fwin != xcme.window)
+                                                               XSetInputFocus(m_display, xcme.window, RevertToParent, xcme.data.l[1]);
+                                               }
+                                       }
+                               }
                        } else {
                                /* Unknown client message, ignore */
                        }
@@ -662,12 +815,12 @@ GHOST_SystemX11::processEvent(XEvent *xe)
                        {
                                XProximityNotifyEvent* data = (XProximityNotifyEvent*)xe;
                                if(data->deviceid == window->GetXTablet().StylusID)
-                                       window->GetXTablet().CommonData.Active= 1;
+                                       window->GetXTablet().CommonData.Active= GHOST_kTabletModeStylus;
                                else if(data->deviceid == window->GetXTablet().EraserID)
-                                       window->GetXTablet().CommonData.Active= 2;
+                                       window->GetXTablet().CommonData.Active= GHOST_kTabletModeEraser;
                        }
                        else if(xe->type == window->GetXTablet().ProxOutEvent)
-                               window->GetXTablet().CommonData.Active= 0;
+                               window->GetXTablet().CommonData.Active= GHOST_kTabletModeNone;
 
                        break;
                }
@@ -695,12 +848,15 @@ GHOST_SystemX11::
 getModifierKeys(
        GHOST_ModifierKeys& keys
 ) const {
-
+       if (this->playingEvents(NULL)) {
+               return getEventManager()->getModifierKeys(keys);
+       }
+       
        // analyse the masks retuned from XQueryPointer.
 
-       memset(m_keyboard_vector,0,sizeof(m_keyboard_vector));
+       memset((void *)m_keyboard_vector,0,sizeof(m_keyboard_vector));
 
-       XQueryKeymap(m_display,m_keyboard_vector);
+       XQueryKeymap(m_display,(char *)m_keyboard_vector);
 
        // now translate key symobols into keycodes and
        // test with vector.
@@ -711,6 +867,8 @@ getModifierKeys(
        const KeyCode control_r = XKeysymToKeycode(m_display,XK_Control_R);
        const KeyCode alt_l = XKeysymToKeycode(m_display,XK_Alt_L);
        const KeyCode alt_r = XKeysymToKeycode(m_display,XK_Alt_R);
+       const KeyCode super_l = XKeysymToKeycode(m_display,XK_Super_L);
+       const KeyCode super_r = XKeysymToKeycode(m_display,XK_Super_R);
 
        // Shift
        if ((m_keyboard_vector[shift_l >> 3] >> (shift_l & 7)) & 1) {
@@ -748,6 +906,15 @@ getModifierKeys(
        } else {
                keys.set(GHOST_kModifierKeyRightAlt,false);
        }
+
+       // Super (Windows) - only one GHOST-kModifierKeyOS, so mapping
+       // to either
+       if ( ((m_keyboard_vector[super_l >> 3] >> (super_l & 7)) & 1) || 
+            ((m_keyboard_vector[super_r >> 3] >> (super_r & 7)) & 1) ) {
+               keys.set(GHOST_kModifierKeyOS,true);
+       } else {
+               keys.set(GHOST_kModifierKeyOS,false);
+       }
        return GHOST_kSuccess;
 }
 
@@ -806,7 +973,11 @@ getCursorPosition(
        Window root_return, child_return;
        int rx,ry,wx,wy;
        unsigned int mask_return;
-
+       
+       if (playingEvents(NULL)) {
+               return getEventManager()->getCursorPosition(x, y);
+       }
+       
        if (XQueryPointer(
                m_display,
                RootWindow(m_display,DefaultScreen(m_display)),
@@ -830,7 +1001,7 @@ GHOST_SystemX11::
 setCursorPosition(
        GHOST_TInt32 x,
        GHOST_TInt32 y
-) const {
+) {
 
        // This is a brute force move in screen coordinates
        // XWarpPointer does relative moves so first determine the
@@ -845,7 +1016,7 @@ setCursorPosition(
        int rely = y-cy;
 
        XWarpPointer(m_display,None,None,0,0,0,0,relx,rely);
-       XFlush(m_display);
+       XSync(m_display, 0); /* Sync to process all requests */
        
        return GHOST_kSuccess;
 }
@@ -955,6 +1126,8 @@ convertXKey(
                        GXMAP(type,XK_Control_R,        GHOST_kKeyRightControl);
                        GXMAP(type,XK_Alt_L,            GHOST_kKeyLeftAlt);
                        GXMAP(type,XK_Alt_R,            GHOST_kKeyRightAlt);
+                       GXMAP(type,XK_Super_L,          GHOST_kKeyOS);
+                       GXMAP(type,XK_Super_R,          GHOST_kKeyOS);
 
                        GXMAP(type,XK_Insert,           GHOST_kKeyInsert);
                        GXMAP(type,XK_Delete,           GHOST_kKeyDelete);
@@ -1004,6 +1177,16 @@ convertXKey(
                        GXMAP(type,XK_KP_Multiply,      GHOST_kKeyNumpadAsterisk);
                        GXMAP(type,XK_KP_Divide,        GHOST_kKeyNumpadSlash);
 
+                       /* Media keys in some keyboards and laptops with XFree86/Xorg */
+#ifdef WITH_XF86KEYSYM
+                       GXMAP(type,XF86XK_AudioPlay,    GHOST_kKeyMediaPlay);
+                       GXMAP(type,XF86XK_AudioStop,    GHOST_kKeyMediaStop);
+                       GXMAP(type,XF86XK_AudioPrev,    GHOST_kKeyMediaFirst);
+                       GXMAP(type,XF86XK_AudioRewind,  GHOST_kKeyMediaFirst);
+                       GXMAP(type,XF86XK_AudioNext,    GHOST_kKeyMediaLast);
+                       GXMAP(type,XF86XK_AudioForward, GHOST_kKeyMediaLast);
+#endif
+
                                /* some extra sun cruft (NICE KEYBOARD!) */
 #ifdef __sun__
                        GXMAP(type,0xffde,                      GHOST_kKeyNumpad1);
@@ -1322,3 +1505,5 @@ void GHOST_SystemX11::putClipboard(GHOST_TInt8 *buffer, bool selection) const
                        fprintf(stderr, "failed to own primary\n");
        }
 }
+
+