Apply BGE patch 12799: Fix quad buffer stereo mode for Windows
[blender.git] / intern / ghost / intern / GHOST_WindowWin32.cpp
1 /**
2  * $Id$
3  * ***** BEGIN GPL LICENSE BLOCK *****
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  *
19  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20  * All rights reserved.
21  *
22  * The Original Code is: all of this file.
23  *
24  * Contributor(s): none yet.
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 /**
30
31  * $Id$
32  * Copyright (C) 2001 NaN Technologies B.V.
33  * @author      Maarten Gribnau
34  * @date        May 10, 2001
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #include <string.h>
42 #include "GHOST_WindowWin32.h"
43 #include <GL/gl.h>
44 #include <math.h>
45
46 // MSVC6 still doesn't define M_PI
47 #ifndef M_PI
48 #define M_PI 3.1415926536
49 #endif
50
51 LPCSTR GHOST_WindowWin32::s_windowClassName = "GHOST_WindowClass";
52 const int GHOST_WindowWin32::s_maxTitleLength = 128;
53 HGLRC GHOST_WindowWin32::s_firsthGLRc = NULL;
54
55 static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd);
56 static int EnumPixelFormats(HDC hdc);
57
58 /*
59  * Color and depth bit values are not to be trusted.
60  * For instance, on TNT2:
61  * When the screen color depth is set to 16 bit, we get 5 color bits
62  * and 16 depth bits.
63  * When the screen color depth is set to 32 bit, we get 8 color bits
64  * and 24 depth bits.
65  * Just to be safe, we request high waulity settings.
66  */
67 static PIXELFORMATDESCRIPTOR sPreferredFormat = {
68         sizeof(PIXELFORMATDESCRIPTOR),  /* size */
69         1,                              /* version */
70         PFD_SUPPORT_OPENGL |
71         PFD_DRAW_TO_WINDOW |
72         PFD_SWAP_COPY |                                 /* support swap copy */
73         PFD_DOUBLEBUFFER,               /* support double-buffering */
74         PFD_TYPE_RGBA,                  /* color type */
75         32,                             /* prefered color depth */
76         0, 0, 0, 0, 0, 0,               /* color bits (ignored) */
77         0,                              /* no alpha buffer */
78         0,                              /* alpha bits (ignored) */
79         0,                              /* no accumulation buffer */
80         0, 0, 0, 0,                     /* accum bits (ignored) */
81         32,                             /* depth buffer */
82         0,                              /* no stencil buffer */
83         0,                              /* no auxiliary buffers */
84         PFD_MAIN_PLANE,                 /* main layer */
85         0,                              /* reserved */
86         0, 0, 0                         /* no layer, visible, damage masks */
87 };
88
89 GHOST_WindowWin32::GHOST_WindowWin32(
90         const STR_String& title,
91         GHOST_TInt32 left,
92         GHOST_TInt32 top,
93         GHOST_TUns32 width,
94         GHOST_TUns32 height,
95         GHOST_TWindowState state,
96         GHOST_TDrawingContextType type,
97         const bool stereoVisual)
98 :
99         GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone,
100         stereoVisual),
101         m_hDC(0),
102         m_hGlRc(0),
103         m_hasMouseCaptured(false),
104         m_nPressedButtons(0),
105         m_customCursor(0),
106         m_tabletData(NULL),
107         m_tablet(0),
108         m_wintab(NULL),
109         m_maxPressure(0)
110 {
111         if (state != GHOST_kWindowStateFullScreen) {
112                         /* Convert client size into window size */
113                 width += GetSystemMetrics(SM_CXSIZEFRAME)*2;
114                 height += GetSystemMetrics(SM_CYSIZEFRAME)*2 + GetSystemMetrics(SM_CYCAPTION);
115
116                 m_hWnd = ::CreateWindow(
117                         s_windowClassName,                      // pointer to registered class name
118                         title,                                          // pointer to window name
119                         WS_OVERLAPPEDWINDOW,            // window style
120                         left,                                           // horizontal position of window
121                         top,                                            // vertical position of window
122                         width,                                          // window width
123                         height,                                         // window height
124                         0,                                                      // handle to parent or owner window
125                         0,                                                      // handle to menu or child-window identifier
126                         ::GetModuleHandle(0),           // handle to application instance
127                         0);                                                     // pointer to window-creation data
128         }
129         else {
130                 m_hWnd = ::CreateWindow(
131                         s_windowClassName,                      // pointer to registered class name
132                         title,                                          // pointer to window name
133                         WS_POPUP | WS_MAXIMIZE,         // window style
134                         left,                                           // horizontal position of window
135                         top,                                            // vertical position of window
136                         width,                                          // window width
137                         height,                                         // window height
138                         0,                                                      // handle to parent or owner window
139                         0,                                                      // handle to menu or child-window identifier
140                         ::GetModuleHandle(0),           // handle to application instance
141                         0);                                                     // pointer to window-creation data
142         }
143         if (m_hWnd) {
144                 // Store a pointer to this class in the window structure
145                 LONG result = ::SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this);
146
147                 // Store the device context
148                 m_hDC = ::GetDC(m_hWnd);
149
150                 // Show the window
151                 int nCmdShow;
152                 switch (state) {
153                         case GHOST_kWindowStateMaximized:
154                                 nCmdShow = SW_SHOWMAXIMIZED;
155                                 break;
156                         case GHOST_kWindowStateMinimized:
157                                 nCmdShow = SW_SHOWMINIMIZED;
158                                 break;
159                         case GHOST_kWindowStateNormal:
160                         default:
161                                 nCmdShow = SW_SHOWNORMAL;
162                                 break;
163                 }
164                 setDrawingContextType(type);
165                 ::ShowWindow(m_hWnd, nCmdShow);
166                 // Force an initial paint of the window
167                 ::UpdateWindow(m_hWnd);
168         }
169
170         m_wintab = ::LoadLibrary("Wintab32.dll");
171         if (m_wintab) {
172                 GHOST_WIN32_WTInfo fpWTInfo = ( GHOST_WIN32_WTInfo ) ::GetProcAddress( m_wintab, "WTInfoA" );
173                 GHOST_WIN32_WTOpen fpWTOpen = ( GHOST_WIN32_WTOpen ) ::GetProcAddress( m_wintab, "WTOpenA" );
174
175                 // let's see if we can initialize tablet here
176                 /* check if WinTab available. */
177                 if (fpWTInfo && fpWTInfo(0, 0, NULL)) {
178                         // Now init the tablet
179                         LOGCONTEXT lc;
180                         AXIS TabletX, TabletY, Pressure, Orientation[3]; /* The maximum tablet size, pressure and orientation (tilt) */
181
182                         // Open a Wintab context
183
184                         // Get default context information
185                         fpWTInfo( WTI_DEFCONTEXT, 0, &lc );
186
187                         // Open the context
188                         lc.lcPktData = PACKETDATA;
189                         lc.lcPktMode = PACKETMODE;
190                         lc.lcOptions |= CXO_MESSAGES | CXO_SYSTEM;
191
192                         /* Set the entire tablet as active */
193                         fpWTInfo(WTI_DEVICES,DVC_X,&TabletX);
194                         fpWTInfo(WTI_DEVICES,DVC_Y,&TabletY);
195
196                         /* get the max pressure, to divide into a float */
197                         BOOL pressureSupport = fpWTInfo (WTI_DEVICES, DVC_NPRESSURE, &Pressure);
198                         if (pressureSupport)
199                                 m_maxPressure = Pressure.axMax;
200                         else
201                                 m_maxPressure = 0;
202
203                         /* get the max tilt axes, to divide into floats */
204                         BOOL tiltSupport = fpWTInfo (WTI_DEVICES, DVC_ORIENTATION, &Orientation);
205                         if (tiltSupport) {
206                                 /* does the tablet support azimuth ([0]) and altitude ([1]) */
207                                 if (Orientation[0].axResolution && Orientation[1].axResolution) {
208                                         /* all this assumes the minimum is 0 */
209                                         m_maxAzimuth = Orientation[0].axMax;
210                                         m_maxAltitude = Orientation[1].axMax;
211                                 }
212                                 else {  /* no so dont do tilt stuff */
213                                         m_maxAzimuth = m_maxAltitude = 0;
214                                 }
215                         }
216
217                         if (fpWTOpen) {
218                                 m_tablet = fpWTOpen( m_hWnd, &lc, TRUE );
219                                 if (m_tablet) {
220                                         m_tabletData = new GHOST_TabletData();
221                                         m_tabletData->Active = 0;
222                                 }
223                         }
224                 }
225         }
226 }
227
228
229 GHOST_WindowWin32::~GHOST_WindowWin32()
230 {
231         if (m_wintab) {
232                 GHOST_WIN32_WTClose fpWTClose = ( GHOST_WIN32_WTClose ) ::GetProcAddress( m_wintab, "WTClose" );
233                 if (fpWTClose) {
234                         if (m_tablet) 
235                                 fpWTClose(m_tablet);
236                         if (m_tabletData)
237                                 delete m_tabletData;
238                                 m_tabletData = NULL;
239                 }
240         }
241         if (m_customCursor) {
242                 DestroyCursor(m_customCursor);
243                 m_customCursor = NULL;
244         }
245
246         setDrawingContextType(GHOST_kDrawingContextTypeNone);
247         if (m_hDC) {
248                 ::ReleaseDC(m_hWnd, m_hDC);
249                 m_hDC = 0;
250         }
251         if (m_hWnd) {
252                 ::DestroyWindow(m_hWnd);
253                 m_hWnd = 0;
254         }
255 }
256
257 bool GHOST_WindowWin32::getValid() const
258 {
259         return m_hWnd != 0;
260 }
261
262
263 void GHOST_WindowWin32::setTitle(const STR_String& title)
264 {
265         ::SetWindowText(m_hWnd, title);
266 }
267
268
269 void GHOST_WindowWin32::getTitle(STR_String& title) const
270 {
271         char buf[s_maxTitleLength];
272         ::GetWindowText(m_hWnd, buf, s_maxTitleLength);
273         STR_String temp (buf);
274         title = buf;
275 }
276
277
278 void GHOST_WindowWin32::getWindowBounds(GHOST_Rect& bounds) const
279 {
280         RECT rect;
281         ::GetWindowRect(m_hWnd, &rect);
282         bounds.m_b = rect.bottom;
283         bounds.m_l = rect.left;
284         bounds.m_r = rect.right;
285         bounds.m_t = rect.top;
286 }
287
288
289 void GHOST_WindowWin32::getClientBounds(GHOST_Rect& bounds) const
290 {
291         RECT rect;
292         ::GetClientRect(m_hWnd, &rect);
293         bounds.m_b = rect.bottom;
294         bounds.m_l = rect.left;
295         bounds.m_r = rect.right;
296         bounds.m_t = rect.top;
297 }
298
299
300 GHOST_TSuccess GHOST_WindowWin32::setClientWidth(GHOST_TUns32 width)
301 {
302         GHOST_TSuccess success;
303         GHOST_Rect cBnds, wBnds;
304         getClientBounds(cBnds);
305         if (cBnds.getWidth() != width) {
306                 getWindowBounds(wBnds);
307                 int cx = wBnds.getWidth() + width - cBnds.getWidth();
308                 int cy = wBnds.getHeight();
309                 success =  ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
310                         GHOST_kSuccess : GHOST_kFailure;
311         }
312         else {
313                 success = GHOST_kSuccess;
314         }
315         return success;
316 }
317
318
319 GHOST_TSuccess GHOST_WindowWin32::setClientHeight(GHOST_TUns32 height)
320 {
321         GHOST_TSuccess success;
322         GHOST_Rect cBnds, wBnds;
323         getClientBounds(cBnds);
324         if (cBnds.getHeight() != height) {
325                 getWindowBounds(wBnds);
326                 int cx = wBnds.getWidth();
327                 int cy = wBnds.getHeight() + height - cBnds.getHeight();
328                 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
329                         GHOST_kSuccess : GHOST_kFailure;
330         }
331         else {
332                 success = GHOST_kSuccess;
333         }
334         return success;
335 }
336
337
338 GHOST_TSuccess GHOST_WindowWin32::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
339 {
340         GHOST_TSuccess success;
341         GHOST_Rect cBnds, wBnds;
342         getClientBounds(cBnds);
343         if ((cBnds.getWidth() != width) || (cBnds.getHeight() != height)) {
344                 getWindowBounds(wBnds);
345                 int cx = wBnds.getWidth() + width - cBnds.getWidth();
346                 int cy = wBnds.getHeight() + height - cBnds.getHeight();
347                 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
348                         GHOST_kSuccess : GHOST_kFailure;
349         }
350         else {
351                 success = GHOST_kSuccess;
352         }
353         return success;
354 }
355
356
357 GHOST_TWindowState GHOST_WindowWin32::getState() const
358 {
359         GHOST_TWindowState state;
360         if (::IsIconic(m_hWnd)) {
361                 state = GHOST_kWindowStateMinimized;
362         }
363         else if (::IsZoomed(m_hWnd)) {
364                 state = GHOST_kWindowStateMaximized;
365         }
366         else {
367                 state = GHOST_kWindowStateNormal;
368         }
369         return state;
370 }
371
372
373 void GHOST_WindowWin32::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
374 {
375         POINT point = { inX, inY };
376         ::ScreenToClient(m_hWnd, &point);
377         outX = point.x;
378         outY = point.y;
379 }
380
381
382 void GHOST_WindowWin32::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
383 {
384         POINT point = { inX, inY };
385         ::ClientToScreen(m_hWnd, &point);
386         outX = point.x;
387         outY = point.y;
388 }
389
390
391 GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
392 {
393         WINDOWPLACEMENT wp;
394         wp.length = sizeof(WINDOWPLACEMENT);
395         ::GetWindowPlacement(m_hWnd, &wp);
396         switch (state) {
397         case GHOST_kWindowStateMinimized: 
398                 wp.showCmd = SW_SHOWMINIMIZED; 
399                 break;
400         case GHOST_kWindowStateMaximized: 
401                 ShowWindow(m_hWnd, SW_HIDE); //fe. HACK!
402                                 //Solves redraw problems when switching from fullscreen to normal.
403                                 
404                 wp.showCmd = SW_SHOWMAXIMIZED; 
405                 SetWindowLong(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
406                 break;
407         case GHOST_kWindowStateFullScreen:
408                 wp.showCmd = SW_SHOWMAXIMIZED;
409                 SetWindowLong(m_hWnd, GWL_STYLE, WS_POPUP | WS_MAXIMIZE);
410                 break;
411         case GHOST_kWindowStateNormal: 
412         default: 
413                 wp.showCmd = SW_SHOWNORMAL; 
414                 break;
415         }
416         return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
417 }
418
419
420 GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order)
421 {
422         HWND hWndInsertAfter = order == GHOST_kWindowOrderTop ? HWND_TOP : HWND_BOTTOM;
423         return ::SetWindowPos(m_hWnd, hWndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
424 }
425
426
427 GHOST_TSuccess GHOST_WindowWin32::swapBuffers()
428 {
429         // adding a glFinish() here is to prevent Geforce in 'full scene antialias' mode
430         // from antialising the Blender window. Officially a swapbuffers does a glFinish
431         // itself, so this feels really like a hack... but it won't harm. (ton)
432         glFinish();
433         return ::SwapBuffers(m_hDC) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
434 }
435
436
437 GHOST_TSuccess GHOST_WindowWin32::activateDrawingContext()
438 {
439         GHOST_TSuccess success;
440         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
441                 if (m_hDC && m_hGlRc) {
442                         success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
443                 }
444                 else {
445                         success = GHOST_kFailure;
446                 }
447         }
448         else {
449                 success = GHOST_kSuccess;
450         }
451         return success;
452 }
453
454
455 GHOST_TSuccess GHOST_WindowWin32::invalidate()
456 {
457         GHOST_TSuccess success;
458         if (m_hWnd) {
459                 success = ::InvalidateRect(m_hWnd, 0, FALSE) != 0 ? GHOST_kSuccess : GHOST_kFailure;
460         }
461         else {
462                 success = GHOST_kFailure;
463         }
464         return success;
465 }
466
467
468 GHOST_TSuccess GHOST_WindowWin32::installDrawingContext(GHOST_TDrawingContextType type)
469 {
470         GHOST_TSuccess success;
471         switch (type) {
472         case GHOST_kDrawingContextTypeOpenGL:
473                 {
474                 if(m_stereoVisual) 
475                         sPreferredFormat.dwFlags |= PFD_STEREO;
476
477                 // Attempt to match device context pixel format to the preferred format
478                 int iPixelFormat = EnumPixelFormats(m_hDC);
479                 if (iPixelFormat == 0) {
480                         success = GHOST_kFailure;
481                         break;
482                 }
483                 if (::SetPixelFormat(m_hDC, iPixelFormat, &sPreferredFormat) == FALSE) {
484                         success = GHOST_kFailure;
485                         break;
486                 }
487                 // For debugging only: retrieve the pixel format chosen
488                 PIXELFORMATDESCRIPTOR preferredFormat;
489                 ::DescribePixelFormat(m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &preferredFormat);
490                 // Create the context
491                 m_hGlRc = ::wglCreateContext(m_hDC);
492                 if (m_hGlRc) {
493                         if (s_firsthGLRc) {
494                                 wglShareLists(s_firsthGLRc, m_hGlRc);
495                         } else {
496                                 s_firsthGLRc = m_hGlRc;
497                         }
498
499                         success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
500                 }
501                 else {
502                         success = GHOST_kFailure;
503                 }
504                 }
505                 break;
506
507         case GHOST_kDrawingContextTypeNone:
508                 success = GHOST_kSuccess;
509                 break;
510
511         default:
512                 success = GHOST_kFailure;
513         }
514         return success;
515 }
516
517
518 GHOST_TSuccess GHOST_WindowWin32::removeDrawingContext()
519 {
520         GHOST_TSuccess success;
521         switch (m_drawingContextType) {
522         case GHOST_kDrawingContextTypeOpenGL:
523                 if (m_hGlRc) {
524                         success = ::wglDeleteContext(m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
525                         if (m_hGlRc == s_firsthGLRc) {
526                                 s_firsthGLRc = NULL;
527                         }
528                         m_hGlRc = 0;
529                 }
530                 else {
531                         success = GHOST_kFailure;
532                 }
533                 break;
534         case GHOST_kDrawingContextTypeNone:
535                 success = GHOST_kSuccess;
536                 break;
537         default:
538                 success = GHOST_kFailure;
539         }
540         return success;
541 }
542
543 void GHOST_WindowWin32::lostMouseCapture()
544 {
545         if (m_hasMouseCaptured) {
546                 m_hasMouseCaptured = false;
547                 m_nPressedButtons = 0;
548         }
549 }
550
551 void GHOST_WindowWin32::registerMouseClickEvent(bool press)
552 {
553         if (press) {
554                 if (!m_hasMouseCaptured) {
555                         ::SetCapture(m_hWnd);
556                         m_hasMouseCaptured = true;
557                 }
558                 m_nPressedButtons++;
559         } else {
560                 if (m_nPressedButtons) {
561                         m_nPressedButtons--;
562                         if (!m_nPressedButtons) {
563                                 ::ReleaseCapture();
564                                 m_hasMouseCaptured = false;
565                         }
566                 }
567         }
568 }
569
570
571 void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
572 {
573         if (!visible) {
574                 while (::ShowCursor(FALSE) >= 0);
575         }
576         else {
577                 while (::ShowCursor(TRUE) < 0);
578         }
579
580         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
581                 ::SetCursor( m_customCursor );
582         } else {
583                 // Convert GHOST cursor to Windows OEM cursor
584                 bool success = true;
585                 LPCSTR id;
586                 switch (cursor) {
587                         case GHOST_kStandardCursorDefault:              id = IDC_ARROW;         break;
588                         case GHOST_kStandardCursorRightArrow:           id = IDC_ARROW;         break;
589                         case GHOST_kStandardCursorLeftArrow:            id = IDC_ARROW;         break;
590                         case GHOST_kStandardCursorInfo:                 id = IDC_SIZEALL;       break;  // Four-pointed arrow pointing north, south, east, and west
591                         case GHOST_kStandardCursorDestroy:              id = IDC_NO;            break;  // Slashed circle
592                         case GHOST_kStandardCursorHelp:                 id = IDC_HELP;          break;  // Arrow and question mark
593                         case GHOST_kStandardCursorCycle:                id = IDC_NO;            break;  // Slashed circle
594                         case GHOST_kStandardCursorSpray:                id = IDC_SIZEALL;       break;  // Four-pointed arrow pointing north, south, east, and west
595                         case GHOST_kStandardCursorWait:                 id = IDC_WAIT;          break;  // Hourglass
596                         case GHOST_kStandardCursorText:                 id = IDC_IBEAM;         break;  // I-beam
597                         case GHOST_kStandardCursorCrosshair:            id = IDC_CROSS;         break;  // Crosshair
598                         case GHOST_kStandardCursorUpDown:               id = IDC_SIZENS;        break;  // Double-pointed arrow pointing north and south
599                         case GHOST_kStandardCursorLeftRight:            id = IDC_SIZEWE;        break;  // Double-pointed arrow pointing west and east
600                         case GHOST_kStandardCursorTopSide:              id = IDC_UPARROW;       break;  // Vertical arrow
601                         case GHOST_kStandardCursorBottomSide:           id = IDC_SIZENS;        break;
602                         case GHOST_kStandardCursorLeftSide:             id = IDC_SIZEWE;        break;
603                         case GHOST_kStandardCursorTopLeftCorner:        id = IDC_SIZENWSE;      break;
604                         case GHOST_kStandardCursorTopRightCorner:       id = IDC_SIZENESW;      break;
605                         case GHOST_kStandardCursorBottomRightCorner:    id = IDC_SIZENWSE;      break;
606                         case GHOST_kStandardCursorBottomLeftCorner:     id = IDC_SIZENESW;      break;
607                         case GHOST_kStandardCursorPencil:               id = IDC_ARROW;         break;
608                         default:
609                         success = false;
610                 }
611                 
612                 if (success) {
613                         HCURSOR hCursor = ::SetCursor(::LoadCursor(0, id));
614                 }
615         }
616 }
617
618 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible)
619 {
620         if (::GetForegroundWindow() == m_hWnd) {
621                 loadCursor(visible, getCursorShape());
622         }
623
624         return GHOST_kSuccess;
625 }
626
627 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
628 {
629         if (m_customCursor) {
630                 DestroyCursor(m_customCursor);
631                 m_customCursor = NULL;
632         }
633
634         if (::GetForegroundWindow() == m_hWnd) {
635                 loadCursor(getCursorVisibility(), cursorShape);
636         }
637
638         return GHOST_kSuccess;
639 }
640 void GHOST_WindowWin32::processWin32TabletInitEvent()
641 {
642         if (m_wintab) {
643                 GHOST_WIN32_WTInfo fpWTInfo = ( GHOST_WIN32_WTInfo ) ::GetProcAddress( m_wintab, "WTInfoA" );
644                 
645                 // let's see if we can initialize tablet here
646                 /* check if WinTab available. */
647                 if (fpWTInfo) {
648                         AXIS Pressure, Orientation[3]; /* The maximum tablet size */
649
650                         BOOL pressureSupport = fpWTInfo (WTI_DEVICES, DVC_NPRESSURE, &Pressure);
651                         if (pressureSupport)
652                                 m_maxPressure = Pressure.axMax;
653                         else
654                                 m_maxPressure = 0;
655                         
656                         BOOL tiltSupport = fpWTInfo (WTI_DEVICES, DVC_ORIENTATION, &Orientation);
657                         if (tiltSupport) {
658                                 /* does the tablet support azimuth ([0]) and altitude ([1]) */
659                                 if (Orientation[0].axResolution && Orientation[1].axResolution) {
660                                         m_maxAzimuth = Orientation[0].axMax;
661                                         m_maxAltitude = Orientation[1].axMax;
662                                 }
663                                 else {  /* no so dont do tilt stuff */
664                                         m_maxAzimuth = m_maxAltitude = 0;
665                                 }
666                         }
667
668                         m_tabletData->Active = 0;
669                 }
670         }
671 }
672
673 void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam)
674 {
675         PACKET pkt;
676         if (m_wintab) {
677                 GHOST_WIN32_WTPacket fpWTPacket = ( GHOST_WIN32_WTPacket ) ::GetProcAddress( m_wintab, "WTPacket" );
678                 if (fpWTPacket) {
679                         if (fpWTPacket((HCTX)lParam, wParam, &pkt)) {
680                                 if (m_tabletData) {
681                                         switch (pkt.pkCursor) {
682                                                 case 0: /* first device */
683                                                 case 3: /* second device */
684                                                         m_tabletData->Active = 0; /* puck - not yet supported */
685                                                         break;
686                                                 case 1:
687                                                 case 4:
688                                                         m_tabletData->Active = 1; /* stylus */
689                                                         break;
690                                                 case 2:
691                                                 case 5:
692                                                         m_tabletData->Active = 2; /* eraser */
693                                                         break;
694                                         }
695                                         if (m_maxPressure > 0) {
696                                                 m_tabletData->Pressure = (float)pkt.pkNormalPressure / (float)m_maxPressure;
697                                         } else {
698                                                 m_tabletData->Pressure = 1.0f;
699                                         }
700
701                                         if ((m_maxAzimuth > 0) && (m_maxAltitude > 0)) {
702                                                 ORIENTATION ort = pkt.pkOrientation;
703                                                 float vecLen;
704                                                 float altRad, azmRad;   /* in radians */
705
706                                                 /*
707                                                 from the wintab spec:
708                                                 orAzimuth       Specifies the clockwise rotation of the
709                                                 cursor about the z axis through a full circular range.
710
711                                                 orAltitude      Specifies the angle with the x-y plane
712                                                 through a signed, semicircular range.  Positive values
713                                                 specify an angle upward toward the positive z axis;
714                                                 negative values specify an angle downward toward the negative z axis.
715
716                                                 wintab.h defines .orAltitude as a UINT but documents .orAltitude
717                                                 as positive for upward angles and negative for downward angles.
718                                                 WACOM uses negative altitude values to show that the pen is inverted;
719                                                 therefore we cast .orAltitude as an (int) and then use the absolute value.
720                                                 */
721                                                 
722                                                 /* convert raw fixed point data to radians */
723                                                 altRad = (fabs((float)ort.orAltitude)/(float)m_maxAltitude) * M_PI/2.0;
724                                                 azmRad = ((float)ort.orAzimuth/(float)m_maxAzimuth) * M_PI*2.0;
725
726                                                 /* find length of the stylus' projected vector on the XY plane */
727                                                 vecLen = cos(altRad);
728
729                                                 /* from there calculate X and Y components based on azimuth */
730                                                 m_tabletData->Xtilt = sin(azmRad) * vecLen;
731                                                 m_tabletData->Ytilt = sin(M_PI/2.0 - azmRad) * vecLen;
732
733                                         } else {
734                                                 m_tabletData->Xtilt = 0.0f;
735                                                 m_tabletData->Ytilt = 0.0f;
736                                         }
737                                 }
738                         }
739                 }
740         }
741 }
742
743 /** Reverse the bits in a GHOST_TUns8 */
744 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
745 {
746         ch= ((ch>>1)&0x55) | ((ch<<1)&0xAA);
747         ch= ((ch>>2)&0x33) | ((ch<<2)&0xCC);
748         ch= ((ch>>4)&0x0F) | ((ch<<4)&0xF0);
749         return ch;
750 }
751
752 /** Reverse the bits in a GHOST_TUns16 */
753 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
754 {
755         shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
756         shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
757         shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
758         shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
759         return shrt;
760 }
761 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], 
762                                         GHOST_TUns8 mask[16][2], int hotX, int hotY)
763 {
764         return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*)mask, 
765                                                                         16, 16, hotX, hotY, 0, 1);
766 }
767
768 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, 
769                                         GHOST_TUns8 *mask, int sizeX, int sizeY, int hotX, int hotY, 
770                                         int fg_color, int bg_color)
771 {
772         GHOST_TUns32 andData[32];
773         GHOST_TUns32 xorData[32];
774         GHOST_TUns32 fullBitRow, fullMaskRow;
775         int x, y, cols;
776         
777         cols=sizeX/8; /* Num of whole bytes per row (width of bm/mask) */
778         if (sizeX%8) cols++;
779         
780         if (m_customCursor) {
781                 DestroyCursor(m_customCursor);
782                 m_customCursor = NULL;
783         }
784
785         memset(&andData, 0xFF, sizeof(andData));
786         memset(&xorData, 0, sizeof(xorData));
787
788         for (y=0; y<sizeY; y++) {
789                 fullBitRow=0;
790                 fullMaskRow=0;
791                 for (x=cols-1; x>=0; x--){
792                         fullBitRow<<=8;
793                         fullMaskRow<<=8;
794                         fullBitRow  |= uns8ReverseBits(bitmap[cols*y + x]);
795                         fullMaskRow |= uns8ReverseBits(  mask[cols*y + x]);
796                 }
797                 xorData[y]= fullBitRow & fullMaskRow;
798                 andData[y]= ~fullMaskRow;
799         }
800
801         m_customCursor = ::CreateCursor(::GetModuleHandle(0), hotX, hotY, 32, 32, andData, xorData);
802         if (!m_customCursor) {
803                 return GHOST_kFailure;
804         }
805
806         if (::GetForegroundWindow() == m_hWnd) {
807                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
808         }
809
810         return GHOST_kSuccess;
811 }
812
813
814 /*  Ron Fosner's code for weighting pixel formats and forcing software.
815         See http://www.opengl.org/resources/faq/technical/weight.cpp */
816
817 static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd) {
818         int weight = 0;
819         
820         /* assume desktop color depth is 32 bits per pixel */
821         
822         /* cull unusable pixel formats */
823         /* if no formats can be found, can we determine why it was rejected? */
824         if( !(pfd.dwFlags & PFD_SUPPORT_OPENGL) ||
825                 !(pfd.dwFlags & PFD_DRAW_TO_WINDOW) ||
826                 !(pfd.dwFlags & PFD_DOUBLEBUFFER) || /* Blender _needs_ this */
827                 ( pfd.cDepthBits <= 8 ) ||
828                 !(pfd.iPixelType == PFD_TYPE_RGBA) )
829                 return 0;
830         
831         weight = 1;  /* it's usable */
832         
833         /* the bigger the depth buffer the better */
834         /* give no weight to a 16-bit depth buffer, because those are crap */
835         weight += pfd.cDepthBits - 16;
836         
837         weight += pfd.cColorBits - 8;
838         
839         /* want swap copy capability -- it matters a lot */
840         if(pfd.dwFlags & PFD_SWAP_COPY) weight += 16;
841         
842         /* but if it's a generic (not accelerated) view, it's really bad */
843         if(pfd.dwFlags & PFD_GENERIC_FORMAT) weight /= 10;
844         
845         return weight;
846 }
847
848 /* A modification of Ron Fosner's replacement for ChoosePixelFormat */ 
849 /* returns 0 on error, else returns the pixel format number to be used */
850 static int EnumPixelFormats(HDC hdc) {
851         int iPixelFormat;
852         int i, n, w, weight = 0;
853         PIXELFORMATDESCRIPTOR pfd, pfd_fallback;
854         
855         /* we need a device context to do anything */
856         if(!hdc) return 0;
857
858         iPixelFormat = 1; /* careful! PFD numbers are 1 based, not zero based */
859         
860         /* obtain detailed information about 
861         the device context's first pixel format */
862         n = 1+::DescribePixelFormat(hdc, iPixelFormat, 
863                 sizeof(PIXELFORMATDESCRIPTOR), &pfd);
864         
865         /* choose a pixel format using the useless Windows function in case
866                 we come up empty handed */
867         iPixelFormat = ::ChoosePixelFormat( hdc, &sPreferredFormat );
868         
869         if(!iPixelFormat) return 0; /* couldn't find one to use */
870
871         for(i=1; i<=n; i++) { /* not the idiom, but it's right */
872                 ::DescribePixelFormat( hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd );
873                 w = WeightPixelFormat(pfd);
874                 // be strict on stereo
875                 if (!((sPreferredFormat.dwFlags ^ pfd.dwFlags) & PFD_STEREO))   {
876                         if(w > weight) {
877                                 weight = w;
878                                 iPixelFormat = i;
879                         }
880                 }
881         }
882         if (weight == 0) {
883                 // we could find the correct stereo setting, just find any suitable format 
884                 for(i=1; i<=n; i++) { /* not the idiom, but it's right */
885                         ::DescribePixelFormat( hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd );
886                         w = WeightPixelFormat(pfd);
887                         if(w > weight) {
888                                 weight = w;
889                                 iPixelFormat = i;
890                         }
891                 }
892         }
893         return iPixelFormat;
894 }
895
896