merge with/from trunk at r35190
[blender.git] / intern / ghost / intern / GHOST_SystemX11.cpp
index 25e60002de0992b434e070a15d9714c55fe85c97..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 
@@ -107,6 +124,16 @@ GHOST_SystemX11(
 
        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);
        m_targets= XInternAtom(m_display, "TARGETS", False);
        m_string= XInternAtom(m_display, "STRING", False);
        m_compound_text= XInternAtom(m_display, "COMPOUND_TEXT", False);
@@ -116,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;
@@ -138,6 +167,13 @@ GHOST_SystemX11(
        
 }
 
+GHOST_SystemX11::
+~GHOST_SystemX11()
+{
+       XCloseDisplay(m_display);
+}
+
+
        GHOST_TSuccess 
 GHOST_SystemX11::
 init(
@@ -201,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).
         */
@@ -215,6 +253,7 @@ createWindow(
        GHOST_TWindowState state,
        GHOST_TDrawingContextType type,
        bool stereoVisual,
+       const GHOST_TUns16 numOfAASamples,
        const GHOST_TEmbedderWindowID parentWindow
 ){
        GHOST_WindowX11 * window = 0;
@@ -229,11 +268,8 @@ createWindow(
        );
 
        if (window) {
-
-               // Install a new protocol for this window - so we can overide
-               // the default window closure mechanism.
-
-               XSetWMProtocols(m_display, window->getXWindow(), &m_delete_window_atom, 1);
+               // Both are now handle in GHOST_WindowX11.cpp
+               // Focus and Delete atoms.
 
                if (window->getValid()) {
                        // Store the pointer to the window 
@@ -296,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(
@@ -306,6 +397,10 @@ processEvents(
        
        bool anyProcessed = false;
        
+       if (playingEvents(&anyProcessed)) {
+               return anyProcessed;
+       }
+       
        do {
                GHOST_TimerManager* timerMgr = getTimerManager();
                
@@ -315,7 +410,10 @@ processEvents(
                        if (next==GHOST_kFireTimeNever) {
                                SleepTillEvent(m_display, -1);
                        } else {
-                               SleepTillEvent(m_display, next - getMilliSeconds());
+                               GHOST_TInt64 maxSleep = next - getMilliSeconds();
+
+                               if(maxSleep >= 0)
+                                       SleepTillEvent(m_display, next - getMilliSeconds());
                        }
                }
                
@@ -357,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;
                }
@@ -371,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;
                }
 
@@ -411,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(),
@@ -555,12 +703,45 @@ GHOST_SystemX11::processEvent(XEvent *xe)
                // We're not interested in the following things.(yet...)
                case NoExpose : 
                case GraphicsExpose :
+                       break;
                
                case EnterNotify:
                case LeaveNotify:
+               {
                        // XCrossingEvents pointer leave enter window.
+                       // also do cursor move here, MotionNotify only
+                       // happens when motion starts & ends inside window
+                       XCrossingEvent &xce = xe->xcrossing;
+                       
+                       g_event = new 
+                       GHOST_EventCursor(
+                               getMilliSeconds(),
+                               GHOST_kEventCursorMove,
+                               window,
+                               xce.x_root,
+                               xce.y_root
+                       );
                        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:
@@ -634,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;
                }
@@ -667,7 +848,10 @@ GHOST_SystemX11::
 getModifierKeys(
        GHOST_ModifierKeys& keys
 ) const {
-
+       if (this->playingEvents(NULL)) {
+               return getEventManager()->getModifierKeys(keys);
+       }
+       
        // analyse the masks retuned from XQueryPointer.
 
        memset((void *)m_keyboard_vector,0,sizeof(m_keyboard_vector));
@@ -683,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) {
@@ -720,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;
 }
 
@@ -778,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)),
@@ -802,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
@@ -817,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;
 }
@@ -927,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);
@@ -976,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);
@@ -999,7 +1210,6 @@ convertXKey(
 
 #undef GXMAP
 
-
 /* from xclip.c xcout() v0.11 */
 
 #define XCLIB_XCOUT_NONE               0 /* no context */
@@ -1174,13 +1384,9 @@ void GHOST_SystemX11::getClipboard_xcout(XEvent evt,
        return;
 }
 
-GHOST_TUns8 *GHOST_SystemX11::getClipboard(int flag) const
+GHOST_TUns8 *GHOST_SystemX11::getClipboard(bool selection) const
 {
-       //Flag 
-       //0 = Regular clipboard 1 = selection
-       
-       // Options for where to get the selection from
-       Atom sseln= flag ? m_primary : m_clipboard;
+       Atom sseln;
        Atom target= m_string;
        Window owner;
 
@@ -1190,6 +1396,11 @@ GHOST_TUns8 *GHOST_SystemX11::getClipboard(int flag) const
        XEvent evt;
        unsigned int context= XCLIB_XCOUT_NONE;
 
+       if (selection == True)
+               sseln= m_primary;
+       else
+               sseln= m_clipboard;
+
        vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
        vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
        GHOST_WindowX11 * window = static_cast<GHOST_WindowX11 *>(*win_it);
@@ -1262,7 +1473,7 @@ GHOST_TUns8 *GHOST_SystemX11::getClipboard(int flag) const
        return(NULL);
 }
 
-void GHOST_SystemX11::putClipboard(GHOST_TInt8 *buffer, int flag) const
+void GHOST_SystemX11::putClipboard(GHOST_TInt8 *buffer, bool selection) const
 {
        Window m_window, owner;
 
@@ -1272,7 +1483,7 @@ void GHOST_SystemX11::putClipboard(GHOST_TInt8 *buffer, int flag) const
        m_window = window->getXWindow();
 
        if (buffer) {
-               if (flag == 0) {
+               if (selection == False) {
                        XSetSelectionOwner(m_display, m_clipboard, m_window, CurrentTime);
                        owner= XGetSelectionOwner(m_display, m_clipboard);
                        if (txt_cut_buffer)
@@ -1295,3 +1506,4 @@ void GHOST_SystemX11::putClipboard(GHOST_TInt8 *buffer, int flag) const
        }
 }
 
+