more preliminary NDOF handling stuff (untested)
[blender.git] / intern / ghost / intern / GHOST_SystemX11.cpp
index abf0b612f9a7b65a993c76b0faf7fa609309ace6..a70162a5039d5a06a22ddc026de613872d8b2818 100644 (file)
@@ -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
-
 #include "GHOST_SystemX11.h"
 #include "GHOST_WindowX11.h"
 #include "GHOST_WindowManager.h"
 #include "GHOST_EventButton.h"
 #include "GHOST_EventWheel.h"
 #include "GHOST_EventNDOF.h"
-#include "GHOST_NDOFManager.h"
+#include "GHOST_NDOFManagerX11.h"
 #include "GHOST_DisplayManagerX11.h"
 
 #include "GHOST_Debug.h"
 
 #include <X11/Xatom.h>
 #include <X11/keysym.h>
-#include <X11/XKBlib.h> /* allow detectable autorepeate */
+#include <X11/XKBlib.h> /* allow detectable autorepeat */
 
 #ifdef __sgi
 
 #include <sys/time.h>
 #include <unistd.h>
 
+#include <iostream>
 #include <vector>
 #include <stdio.h> // for fprintf only
+#include <cstdlib> // for exit
 
+// [mce] these are for communication with the plugin
 typedef struct NDOFPlatformInfo {
        Display *display;
        Window window;
-       volatile GHOST_TEventNDOFData *currValues;
+//     volatile GHOST_TEventNDOFData *currValues;
        Atom cmdAtom;
        Atom motionAtom;
        Atom btnPressAtom;
@@ -78,7 +80,6 @@ typedef struct NDOFPlatformInfo {
 
 static NDOFPlatformInfo sNdofInfo = {NULL, 0, NULL, 0, 0, 0, 0};
 
-
 //these are for copy and select copy
 static char *txt_cut_buffer= NULL;
 static char *txt_select_buffer= NULL;
@@ -93,7 +94,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 +130,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;
@@ -136,7 +142,7 @@ GHOST_SystemX11(
        m_start_time = GHOST_TUns64(tv.tv_sec*1000 + tv.tv_usec/1000);
        
        
-       /* use detectable autorepeate, mac and windows also do this */
+       /* use detectable autorepeat, mac and windows also do this */
        int use_xkb;
        int xkb_opcode, xkb_event, xkb_error;
        int xkb_major = XkbMajorVersion, xkb_minor = XkbMinorVersion;
@@ -162,6 +168,9 @@ init(
        GHOST_TSuccess success = GHOST_System::init();
 
        if (success) {
+
+               m_ndofManager = new GHOST_NDOFManagerX11;
+
                m_displayManager = new GHOST_DisplayManagerX11(this);
 
                if (m_displayManager) {
@@ -218,6 +227,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).
         */
@@ -232,14 +243,12 @@ createWindow(
        GHOST_TWindowState state,
        GHOST_TDrawingContextType type,
        bool stereoVisual,
+       const GHOST_TUns16 numOfAASamples,
        const GHOST_TEmbedderWindowID parentWindow
 ){
        GHOST_WindowX11 * window = 0;
        
        if (!m_display) return 0;
-       
-
-       
 
        window = new GHOST_WindowX11 (
                this,m_display,title, left, top, width, height, state, parentWindow, type, stereoVisual
@@ -310,6 +319,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(
@@ -395,29 +459,36 @@ GHOST_SystemX11::processEvent(XEvent *xe)
                                GHOST_TInt32 x_accum, y_accum;
                                GHOST_Rect bounds;
 
-                               window->getClientBounds(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, 1); /* offset of one incase blender is at screen bounds */
-
+                               bounds.wrapPoint(x_new, y_new, 2); /* 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) {
-                                       setCursorPosition(x_new, y_new); /* wrap */
-                                       window->setCursorGrabAccum(x_accum + (xme.x_root - x_new), y_accum + (xme.y_root - y_new));
+                                       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
+                                       );
                                }
-
-                               g_event = new
-                               GHOST_EventCursor(
-                                       getMilliSeconds(),
-                                       GHOST_kEventCursorMove,
-                                       window,
-                                       xme.x_root + x_accum,
-                                       xme.y_root + y_accum
-                               );
-
                        }
                        else {
                                g_event = new
@@ -461,38 +532,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;
-                               /* 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" 
-                                */
-                               case 8 : gbmask = GHOST_kButtonMaskButton4; break; /* Button4 is the wheel */
-                               case 9 : gbmask = GHOST_kButtonMaskButton5; break; /* Button5 is a wheel too */
-                               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(),
@@ -552,29 +628,49 @@ GHOST_SystemX11::processEvent(XEvent *xe)
                        } else 
 #endif
                        if (sNdofInfo.currValues) {
-                               static GHOST_TEventNDOFData data = {0,0,0,0,0,0,0,0,0,0,0};
+//                             static GHOST_TEventNDOFData data = {0,0,0,0,0,0,0,0,0,0,0};
                                if (xcme.message_type == sNdofInfo.motionAtom)
                                {
-                                       data.changed = 1;
-                                       data.delta = xcme.data.s[8] - data.time;
-                                       data.time = xcme.data.s[8];
-                                       data.tx = xcme.data.s[2] >> 2;
-                                       data.ty = xcme.data.s[3] >> 2;
-                                       data.tz = xcme.data.s[4] >> 2;
-                                       data.rx = xcme.data.s[5];
-                                       data.ry = xcme.data.s[6];
-                                       data.rz =-xcme.data.s[7];
-                                       g_event = new GHOST_EventNDOF(getMilliSeconds(),
-                                                                     GHOST_kEventNDOFMotion,
-                                                                     window, data);
+//                                     data.changed = 1;
+//                                     data.delta = xcme.data.s[8] - data.time;
+//                                     data.time = xcme.data.s[8];
+//                                     data.tx = xcme.data.s[2] >> 2;
+//                                     data.ty = xcme.data.s[3] >> 2;
+//                                     data.tz = xcme.data.s[4] >> 2;
+//                                     data.rx = xcme.data.s[5];
+//                                     data.ry = xcme.data.s[6];
+//                                     data.rz =-xcme.data.s[7];
+                                               
+                                       short t[3], r[3];
+                                       t[0] = xcme.data.s[2] >> 2;
+                                       t[1] = xcme.data.s[3] >> 2;
+                                       t[2] = xcme.data.s[4] >> 2;
+                                       r[0] = xcme.data.s[5];
+                                       r[1] = xcme.data.s[6];
+                                       r[2] =-xcme.data.s[7];
+
+                                       // [mce] look into this soon, as in find out why some values
+                                       // are shifted and where this message originates.
+
+                                       m_ndofManager->updateTranslation(t, getMilliseconds);
+                                       m_ndofManager->updateRotation(r, getMilliseconds);
+
+//                                     g_event = new GHOST_EventNDOF(getMilliSeconds(),
+//                                                                   GHOST_kEventNDOFMotion,
+//                                                                   window, data);
+
                                } else if (xcme.message_type == sNdofInfo.btnPressAtom) {
-                                       data.changed = 2;
-                                       data.delta = xcme.data.s[8] - data.time;
-                                       data.time = xcme.data.s[8];
-                                       data.buttons = xcme.data.s[2];
-                                       g_event = new GHOST_EventNDOF(getMilliSeconds(),
-                                                                     GHOST_kEventNDOFButton,
-                                                                     window, data);
+//                                     data.changed = 2;
+//                                     data.delta = xcme.data.s[8] - data.time;
+//                                     data.time = xcme.data.s[8];
+//                                     data.buttons = xcme.data.s[2];
+
+                                       unsigned short buttons = xcme.data.s[2];
+                                       m_ndofManager->updateButtons(buttons, getMilliseconds());
+
+//                                     g_event = new GHOST_EventNDOF(getMilliSeconds(),
+//                                                                   GHOST_kEventNDOFButton,
+//                                                                   window, data);
                                }
                        } else if (((Atom)xcme.data.l[0]) == m_wm_take_focus) {
                                XWindowAttributes attr;
@@ -738,6 +834,7 @@ GHOST_SystemX11::processEvent(XEvent *xe)
        }
 }
 
+/*
        void *
 GHOST_SystemX11::
 prepareNdofInfo(volatile GHOST_TEventNDOFData *currentNdofValues)
@@ -749,6 +846,7 @@ prepareNdofInfo(volatile GHOST_TEventNDOFData *currentNdofValues)
        sNdofInfo.currValues = currentNdofValues;
        return (void*)&sNdofInfo;
 }
+*/
 
        GHOST_TSuccess 
 GHOST_SystemX11::
@@ -890,7 +988,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
@@ -905,7 +1003,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;
 }
@@ -1382,3 +1480,22 @@ void GHOST_SystemX11::putClipboard(GHOST_TInt8 *buffer, bool selection) const
                        fprintf(stderr, "failed to own primary\n");
        }
 }
+
+const GHOST_TUns8* GHOST_SystemX11::getSystemDir() const
+{
+       return (GHOST_TUns8*)"/usr/share/blender";
+}
+
+const GHOST_TUns8* GHOST_SystemX11::getUserDir() const
+{
+       static char path[256];
+       char* env = getenv("HOME");
+       if(env) {
+               strncpy(path, env, 245);
+               path[245]=0;
+               strcat(path, "/.blender/");
+               return (GHOST_TUns8*) path;
+       } else {
+               return NULL;
+       }
+}