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