* also distinguish between maximised and fullscreen on GHOST win32.
[blender-staging.git] / intern / ghost / intern / GHOST_WindowWin32.cpp
index dcf02611d1aad1c7e837589fa271e3b47f222fb3..81a6c89a433e23abaeb105c645165d5044746ecf 100644 (file)
 #include <string.h>
 #include "GHOST_WindowWin32.h"
 #include <GL/gl.h>
+#include <math.h>
+
+// MSVC6 still doesn't define M_PI
+#ifndef M_PI
+#define M_PI 3.1415926536
+#endif
 
 LPCSTR GHOST_WindowWin32::s_windowClassName = "GHOST_WindowClass";
 const int GHOST_WindowWin32::s_maxTitleLength = 128;
 HGLRC GHOST_WindowWin32::s_firsthGLRc = NULL;
 
+static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd);
+static int EnumPixelFormats(HDC hdc);
+
 /*
  * Color and depth bit values are not to be trusted.
  * For instance, on TNT2:
@@ -63,6 +72,7 @@ static PIXELFORMATDESCRIPTOR sPreferredFormat = {
        1,                              /* version */
        PFD_SUPPORT_OPENGL |
        PFD_DRAW_TO_WINDOW |
+       PFD_SWAP_COPY |                                 /* support swap copy */
        PFD_DOUBLEBUFFER,               /* support double-buffering */
        PFD_TYPE_RGBA,                  /* color type */
        32,                             /* prefered color depth */
@@ -95,7 +105,11 @@ GHOST_WindowWin32::GHOST_WindowWin32(
        m_hGlRc(0),
        m_hasMouseCaptured(false),
        m_nPressedButtons(0),
-       m_customCursor(0)
+       m_customCursor(0),
+       m_tabletData(NULL),
+       m_tablet(0),
+       m_wintab(NULL),
+       m_maxPressure(0)
 {
        if (state != GHOST_kWindowStateFullScreen) {
                        /* Convert client size into window size */
@@ -131,7 +145,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(
        }
        if (m_hWnd) {
                // Store a pointer to this class in the window structure
-               LONG result = ::SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this);
+               ::SetWindowLongPtr(m_hWnd, GWL_USERDATA, (LONG_PTR)this);
 
                // Store the device context
                m_hDC = ::GetDC(m_hWnd);
@@ -155,11 +169,78 @@ GHOST_WindowWin32::GHOST_WindowWin32(
                // Force an initial paint of the window
                ::UpdateWindow(m_hWnd);
        }
+
+       m_wintab = ::LoadLibrary("Wintab32.dll");
+       if (m_wintab) {
+               GHOST_WIN32_WTInfo fpWTInfo = ( GHOST_WIN32_WTInfo ) ::GetProcAddress( m_wintab, "WTInfoA" );
+               GHOST_WIN32_WTOpen fpWTOpen = ( GHOST_WIN32_WTOpen ) ::GetProcAddress( m_wintab, "WTOpenA" );
+
+               // let's see if we can initialize tablet here
+               /* check if WinTab available. */
+               if (fpWTInfo && fpWTInfo(0, 0, NULL)) {
+                       // Now init the tablet
+                       LOGCONTEXT lc;
+                       AXIS TabletX, TabletY, Pressure, Orientation[3]; /* The maximum tablet size, pressure and orientation (tilt) */
+
+                       // Open a Wintab context
+
+                       // Get default context information
+                       fpWTInfo( WTI_DEFCONTEXT, 0, &lc );
+
+                       // Open the context
+                       lc.lcPktData = PACKETDATA;
+                       lc.lcPktMode = PACKETMODE;
+                       lc.lcOptions |= CXO_MESSAGES | CXO_SYSTEM;
+
+                       /* Set the entire tablet as active */
+                       fpWTInfo(WTI_DEVICES,DVC_X,&TabletX);
+                       fpWTInfo(WTI_DEVICES,DVC_Y,&TabletY);
+
+                       /* get the max pressure, to divide into a float */
+                       BOOL pressureSupport = fpWTInfo (WTI_DEVICES, DVC_NPRESSURE, &Pressure);
+                       if (pressureSupport)
+                               m_maxPressure = Pressure.axMax;
+                       else
+                               m_maxPressure = 0;
+
+                       /* get the max tilt axes, to divide into floats */
+                       BOOL tiltSupport = fpWTInfo (WTI_DEVICES, DVC_ORIENTATION, &Orientation);
+                       if (tiltSupport) {
+                               /* does the tablet support azimuth ([0]) and altitude ([1]) */
+                               if (Orientation[0].axResolution && Orientation[1].axResolution) {
+                                       /* all this assumes the minimum is 0 */
+                                       m_maxAzimuth = Orientation[0].axMax;
+                                       m_maxAltitude = Orientation[1].axMax;
+                               }
+                               else {  /* no so dont do tilt stuff */
+                                       m_maxAzimuth = m_maxAltitude = 0;
+                               }
+                       }
+
+                       if (fpWTOpen) {
+                               m_tablet = fpWTOpen( m_hWnd, &lc, TRUE );
+                               if (m_tablet) {
+                                       m_tabletData = new GHOST_TabletData();
+                                       m_tabletData->Active = 0;
+                               }
+                       }
+               }
+       }
 }
 
 
 GHOST_WindowWin32::~GHOST_WindowWin32()
 {
+       if (m_wintab) {
+               GHOST_WIN32_WTClose fpWTClose = ( GHOST_WIN32_WTClose ) ::GetProcAddress( m_wintab, "WTClose" );
+               if (fpWTClose) {
+                       if (m_tablet) 
+                               fpWTClose(m_tablet);
+                       if (m_tabletData)
+                               delete m_tabletData;
+                               m_tabletData = NULL;
+               }
+       }
        if (m_customCursor) {
                DestroyCursor(m_customCursor);
                m_customCursor = NULL;
@@ -211,11 +292,20 @@ void GHOST_WindowWin32::getWindowBounds(GHOST_Rect& bounds) const
 void GHOST_WindowWin32::getClientBounds(GHOST_Rect& bounds) const
 {
        RECT rect;
-       ::GetClientRect(m_hWnd, &rect);
-       bounds.m_b = rect.bottom;
-       bounds.m_l = rect.left;
-       bounds.m_r = rect.right;
-       bounds.m_t = rect.top;
+       ::GetWindowRect(m_hWnd, &rect);
+
+       LONG_PTR result = ::GetWindowLongPtr(m_hWnd, GWL_STYLE);
+       if((result & (WS_POPUP | WS_MAXIMIZE)) != (WS_POPUP | WS_MAXIMIZE)) {
+               bounds.m_b = rect.bottom-GetSystemMetrics(SM_CYCAPTION)-GetSystemMetrics(SM_CYSIZEFRAME)*2;
+               bounds.m_l = rect.left;
+               bounds.m_r = rect.right-GetSystemMetrics(SM_CYSIZEFRAME)*2;
+               bounds.m_t = rect.top;
+       } else {
+               bounds.m_b = rect.bottom;
+               bounds.m_l = rect.left;
+               bounds.m_r = rect.right;
+               bounds.m_t = rect.top;
+       }
 }
 
 
@@ -283,7 +373,11 @@ GHOST_TWindowState GHOST_WindowWin32::getState() const
                state = GHOST_kWindowStateMinimized;
        }
        else if (::IsZoomed(m_hWnd)) {
-               state = GHOST_kWindowStateMaximized;
+               LONG_PTR result = ::GetWindowLongPtr(m_hWnd, GWL_STYLE);
+               if((result & (WS_POPUP | WS_MAXIMIZE)) != (WS_POPUP | WS_MAXIMIZE))
+                       state = GHOST_kWindowStateMaximized;
+               else
+                       state = GHOST_kWindowStateFullScreen;
        }
        else {
                state = GHOST_kWindowStateNormal;
@@ -324,11 +418,11 @@ GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
                                //Solves redraw problems when switching from fullscreen to normal.
                                
                wp.showCmd = SW_SHOWMAXIMIZED; 
-               SetWindowLong(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
+               SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
                break;
        case GHOST_kWindowStateFullScreen:
                wp.showCmd = SW_SHOWMAXIMIZED;
-               SetWindowLong(m_hWnd, GWL_STYLE, WS_POPUP | WS_MAXIMIZE);
+               SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_POPUP | WS_MAXIMIZE);
                break;
        case GHOST_kWindowStateNormal: 
        default: 
@@ -393,11 +487,11 @@ GHOST_TSuccess GHOST_WindowWin32::installDrawingContext(GHOST_TDrawingContextTyp
        switch (type) {
        case GHOST_kDrawingContextTypeOpenGL:
                {
-               if(m_stereoVisual)
+               if(m_stereoVisual) 
                        sPreferredFormat.dwFlags |= PFD_STEREO;
 
                // Attempt to match device context pixel format to the preferred format
-               int iPixelFormat = ::ChoosePixelFormat(m_hDC, &sPreferredFormat);
+               int iPixelFormat = EnumPixelFormats(m_hDC);
                if (iPixelFormat == 0) {
                        success = GHOST_kFailure;
                        break;
@@ -532,7 +626,7 @@ void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) c
                }
                
                if (success) {
-                       HCURSOR hCursor = ::SetCursor(::LoadCursor(0, id));
+                       ::SetCursor(::LoadCursor(0, id));
                }
        }
 }
@@ -559,6 +653,108 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cur
 
        return GHOST_kSuccess;
 }
+void GHOST_WindowWin32::processWin32TabletInitEvent()
+{
+       if (m_wintab) {
+               GHOST_WIN32_WTInfo fpWTInfo = ( GHOST_WIN32_WTInfo ) ::GetProcAddress( m_wintab, "WTInfoA" );
+               
+               // let's see if we can initialize tablet here
+               /* check if WinTab available. */
+               if (fpWTInfo) {
+                       AXIS Pressure, Orientation[3]; /* The maximum tablet size */
+
+                       BOOL pressureSupport = fpWTInfo (WTI_DEVICES, DVC_NPRESSURE, &Pressure);
+                       if (pressureSupport)
+                               m_maxPressure = Pressure.axMax;
+                       else
+                               m_maxPressure = 0;
+                       
+                       BOOL tiltSupport = fpWTInfo (WTI_DEVICES, DVC_ORIENTATION, &Orientation);
+                       if (tiltSupport) {
+                               /* does the tablet support azimuth ([0]) and altitude ([1]) */
+                               if (Orientation[0].axResolution && Orientation[1].axResolution) {
+                                       m_maxAzimuth = Orientation[0].axMax;
+                                       m_maxAltitude = Orientation[1].axMax;
+                               }
+                               else {  /* no so dont do tilt stuff */
+                                       m_maxAzimuth = m_maxAltitude = 0;
+                               }
+                       }
+
+                       m_tabletData->Active = 0;
+               }
+       }
+}
+
+void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam)
+{
+       PACKET pkt;
+       if (m_wintab) {
+               GHOST_WIN32_WTPacket fpWTPacket = ( GHOST_WIN32_WTPacket ) ::GetProcAddress( m_wintab, "WTPacket" );
+               if (fpWTPacket) {
+                       if (fpWTPacket((HCTX)lParam, wParam, &pkt)) {
+                               if (m_tabletData) {
+                                       switch (pkt.pkCursor) {
+                                               case 0: /* first device */
+                                               case 3: /* second device */
+                                                       m_tabletData->Active = 0; /* puck - not yet supported */
+                                                       break;
+                                               case 1:
+                                               case 4:
+                                                       m_tabletData->Active = 1; /* stylus */
+                                                       break;
+                                               case 2:
+                                               case 5:
+                                                       m_tabletData->Active = 2; /* eraser */
+                                                       break;
+                                       }
+                                       if (m_maxPressure > 0) {
+                                               m_tabletData->Pressure = (float)pkt.pkNormalPressure / (float)m_maxPressure;
+                                       } else {
+                                               m_tabletData->Pressure = 1.0f;
+                                       }
+
+                                       if ((m_maxAzimuth > 0) && (m_maxAltitude > 0)) {
+                                               ORIENTATION ort = pkt.pkOrientation;
+                                               float vecLen;
+                                               float altRad, azmRad;   /* in radians */
+
+                                               /*
+                                               from the wintab spec:
+                                               orAzimuth       Specifies the clockwise rotation of the
+                                               cursor about the z axis through a full circular range.
+
+                                               orAltitude      Specifies the angle with the x-y plane
+                                               through a signed, semicircular range.  Positive values
+                                               specify an angle upward toward the positive z axis;
+                                               negative values specify an angle downward toward the negative z axis.
+
+                                               wintab.h defines .orAltitude as a UINT but documents .orAltitude
+                                               as positive for upward angles and negative for downward angles.
+                                               WACOM uses negative altitude values to show that the pen is inverted;
+                                               therefore we cast .orAltitude as an (int) and then use the absolute value.
+                                               */
+                                               
+                                               /* convert raw fixed point data to radians */
+                                               altRad = (fabs((float)ort.orAltitude)/(float)m_maxAltitude) * M_PI/2.0;
+                                               azmRad = ((float)ort.orAzimuth/(float)m_maxAzimuth) * M_PI*2.0;
+
+                                               /* find length of the stylus' projected vector on the XY plane */
+                                               vecLen = cos(altRad);
+
+                                               /* from there calculate X and Y components based on azimuth */
+                                               m_tabletData->Xtilt = sin(azmRad) * vecLen;
+                                               m_tabletData->Ytilt = sin(M_PI/2.0 - azmRad) * vecLen;
+
+                                       } else {
+                                               m_tabletData->Xtilt = 0.0f;
+                                               m_tabletData->Ytilt = 0.0f;
+                                       }
+                               }
+                       }
+               }
+       }
+}
 
 /** Reverse the bits in a GHOST_TUns8 */
 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
@@ -581,9 +777,8 @@ static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], 
                                        GHOST_TUns8 mask[16][2], int hotX, int hotY)
 {
-       setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*)mask, 
+       return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*)mask, 
                                                                        16, 16, hotX, hotY, 0, 1);
-
 }
 
 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, 
@@ -631,3 +826,74 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 *bitmap
        return GHOST_kSuccess;
 }
 
+
+/*  Ron Fosner's code for weighting pixel formats and forcing software.
+       See http://www.opengl.org/resources/faq/technical/weight.cpp */
+
+static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd) {
+       int weight = 0;
+       
+       /* assume desktop color depth is 32 bits per pixel */
+       
+       /* cull unusable pixel formats */
+       /* if no formats can be found, can we determine why it was rejected? */
+       if( !(pfd.dwFlags & PFD_SUPPORT_OPENGL) ||
+               !(pfd.dwFlags & PFD_DRAW_TO_WINDOW) ||
+               !(pfd.dwFlags & PFD_DOUBLEBUFFER) || /* Blender _needs_ this */
+               ( pfd.cDepthBits <= 8 ) ||
+               !(pfd.iPixelType == PFD_TYPE_RGBA) )
+               return 0;
+       
+       weight = 1;  /* it's usable */
+       
+       /* the bigger the depth buffer the better */
+       /* give no weight to a 16-bit depth buffer, because those are crap */
+       weight += pfd.cDepthBits - 16;
+       
+       weight += pfd.cColorBits - 8;
+       
+       /* want swap copy capability -- it matters a lot */
+       if(pfd.dwFlags & PFD_SWAP_COPY) weight += 16;
+       
+       /* but if it's a generic (not accelerated) view, it's really bad */
+       if(pfd.dwFlags & PFD_GENERIC_FORMAT) weight /= 10;
+       
+       return weight;
+}
+
+/* A modification of Ron Fosner's replacement for ChoosePixelFormat */ 
+/* returns 0 on error, else returns the pixel format number to be used */
+static int EnumPixelFormats(HDC hdc) {
+       int iPixelFormat;
+       int i, n, w, weight = 0;
+       PIXELFORMATDESCRIPTOR pfd;
+       
+       /* we need a device context to do anything */
+       if(!hdc) return 0;
+
+       iPixelFormat = 1; /* careful! PFD numbers are 1 based, not zero based */
+       
+       /* obtain detailed information about 
+       the device context's first pixel format */
+       n = 1+::DescribePixelFormat(hdc, iPixelFormat, 
+               sizeof(PIXELFORMATDESCRIPTOR), &pfd);
+       
+       /* choose a pixel format using the useless Windows function in case
+               we come up empty handed */
+       iPixelFormat = ::ChoosePixelFormat( hdc, &sPreferredFormat );
+       
+       if(!iPixelFormat) return 0; /* couldn't find one to use */
+
+       for(i=1; i<=n; i++) { /* not the idiom, but it's right */
+               ::DescribePixelFormat( hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd );
+               w = WeightPixelFormat(pfd);
+               if(w > weight) {
+                       weight = w;
+                       iPixelFormat = i;
+               }
+       }
+       
+       return iPixelFormat;
+}
+
+