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