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