Patch [#20588] Adding multisample support to Win32 Ghost - by Mitchell Stokes (Moguri)
[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 "GHOST_SystemWin32.h"
44 #include "GHOST_DropTargetWin32.h"
45
46 // Need glew for some defines
47 #include <GL/glew.h>
48 #include <GL/wglew.h>
49 #include <math.h>
50
51 // MSVC6 still doesn't define M_PI
52 #ifndef M_PI
53 #define M_PI 3.1415926536
54 #endif
55
56 // Some more multisample defines
57 #define WGL_SAMPLE_BUFERS_ARB   0x2041
58 #define WGL_SAMPLES_ARB                 0x2042
59
60 // win64 doesn't define GWL_USERDATA
61 #ifdef WIN32
62 #ifndef GWL_USERDATA
63 #define GWL_USERDATA GWLP_USERDATA
64 #define GWL_WNDPROC GWLP_WNDPROC
65 #endif
66 #endif
67
68 LPCSTR GHOST_WindowWin32::s_windowClassName = "GHOST_WindowClass";
69 const int GHOST_WindowWin32::s_maxTitleLength = 128;
70 HGLRC GHOST_WindowWin32::s_firsthGLRc = NULL;
71
72 static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd);
73 static int EnumPixelFormats(HDC hdc);
74
75 /*
76  * Color and depth bit values are not to be trusted.
77  * For instance, on TNT2:
78  * When the screen color depth is set to 16 bit, we get 5 color bits
79  * and 16 depth bits.
80  * When the screen color depth is set to 32 bit, we get 8 color bits
81  * and 24 depth bits.
82  * Just to be safe, we request high waulity settings.
83  */
84 static PIXELFORMATDESCRIPTOR sPreferredFormat = {
85         sizeof(PIXELFORMATDESCRIPTOR),  /* size */
86         1,                              /* version */
87         PFD_SUPPORT_OPENGL |
88         PFD_DRAW_TO_WINDOW |
89         PFD_SWAP_COPY |                                 /* support swap copy */
90         PFD_DOUBLEBUFFER,               /* support double-buffering */
91         PFD_TYPE_RGBA,                  /* color type */
92         32,                             /* prefered color depth */
93         0, 0, 0, 0, 0, 0,               /* color bits (ignored) */
94         0,                              /* no alpha buffer */
95         0,                              /* alpha bits (ignored) */
96         0,                              /* no accumulation buffer */
97         0, 0, 0, 0,                     /* accum bits (ignored) */
98         32,                             /* depth buffer */
99         0,                              /* no stencil buffer */
100         0,                              /* no auxiliary buffers */
101         PFD_MAIN_PLANE,                 /* main layer */
102         0,                              /* reserved */
103         0, 0, 0                         /* no layer, visible, damage masks */
104 };
105
106 GHOST_WindowWin32::GHOST_WindowWin32(
107         GHOST_SystemWin32 * system,
108         const STR_String& title,
109         GHOST_TInt32 left,
110         GHOST_TInt32 top,
111         GHOST_TUns32 width,
112         GHOST_TUns32 height,
113         GHOST_TWindowState state,
114         GHOST_TDrawingContextType type,
115         const bool stereoVisual,
116         const GHOST_TUns16 numOfAASamples,
117         GHOST_TSuccess msEnabled,
118         int msPixelFormat)
119 :
120         GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone,
121         stereoVisual,numOfAASamples),
122         m_system(system),
123         m_hDC(0),
124         m_hGlRc(0),
125         m_hasMouseCaptured(false),
126         m_nPressedButtons(0),
127         m_customCursor(0),
128         m_wintab(NULL),
129         m_tabletData(NULL),
130         m_tablet(0),
131         m_maxPressure(0),
132         m_multisample(numOfAASamples),
133         m_multisampleEnabled(msEnabled),
134         m_msPixelFormat(msPixelFormat),
135         //For recreation
136         m_title(title),
137         m_left(left),
138         m_top(top),
139         m_width(width),
140         m_height(height),
141         m_stereo(stereoVisual),
142         m_nextWindow(NULL)
143 {
144         if (state != GHOST_kWindowStateFullScreen) {
145                 RECT rect;
146                 GHOST_TUns32 tw, th; 
147
148                 width += GetSystemMetrics(SM_CXSIZEFRAME)*2;
149                 height += GetSystemMetrics(SM_CYSIZEFRAME)*2 + GetSystemMetrics(SM_CYCAPTION);
150
151                 // take taskbar into account
152                 SystemParametersInfo(SPI_GETWORKAREA,0,&rect,0);
153                 th = rect.bottom - rect.top;
154                 tw = rect.right - rect.left;
155
156                 if(tw < width)
157                 {
158                         width = tw;
159                         left = rect.left;
160                 }
161                 if(th < height)
162                 {
163                         height = th;
164                         top = rect.top;
165                 }
166
167                 m_hWnd = ::CreateWindow(
168                         s_windowClassName,                      // pointer to registered class name
169                         title,                                          // pointer to window name
170                         WS_OVERLAPPEDWINDOW,            // window style
171                         left,                                   // horizontal position of window
172                         top,                                    // vertical position of window
173                         width,                                          // window width
174                         height,                                         // window height
175                         0,                                                      // handle to parent or owner window
176                         0,                                                      // handle to menu or child-window identifier
177                         ::GetModuleHandle(0),           // handle to application instance
178                         0);                                                     // pointer to window-creation data
179         }
180         else {
181                 m_hWnd = ::CreateWindow(
182                         s_windowClassName,                      // pointer to registered class name
183                         title,                                          // pointer to window name
184                         WS_POPUP | WS_MAXIMIZE,         // window style
185                         left,                                           // horizontal position of window
186                         top,                                            // vertical position of window
187                         width,                                          // window width
188                         height,                                         // window height
189                         0,                                                      // handle to parent or owner window
190                         0,                                                      // handle to menu or child-window identifier
191                         ::GetModuleHandle(0),           // handle to application instance
192                         0);                                                     // pointer to window-creation data
193         }
194         if (m_hWnd) {
195                 // Register this window as a droptarget. Requires m_hWnd to be valid.
196                 // Note that OleInitialize(0) has to be called prior to this. Done in GHOST_SystemWin32.
197                 m_dropTarget = new GHOST_DropTargetWin32(this, m_system);
198                 // Store a pointer to this class in the window structure
199                 ::SetWindowLongPtr(m_hWnd, GWL_USERDATA, (LONG_PTR)this);
200
201                 // Store the device context
202                 m_hDC = ::GetDC(m_hWnd);
203
204                 // Show the window
205                 int nCmdShow;
206                 switch (state) {
207                         case GHOST_kWindowStateMaximized:
208                                 nCmdShow = SW_SHOWMAXIMIZED;
209                                 break;
210                         case GHOST_kWindowStateMinimized:
211                                 nCmdShow = SW_SHOWMINIMIZED;
212                                 break;
213                         case GHOST_kWindowStateNormal:
214                         default:
215                                 nCmdShow = SW_SHOWNORMAL;
216                                 break;
217                 }
218                 GHOST_TSuccess success;
219                 success = setDrawingContextType(type);
220
221                 if (success)
222                 {
223                         ::ShowWindow(m_hWnd, nCmdShow);
224                         // Force an initial paint of the window
225                         ::UpdateWindow(m_hWnd);
226                 }
227                 else
228                 {
229                         //invalidate the window
230                         m_hWnd = 0;
231                 }
232         }
233
234         m_wintab = ::LoadLibrary("Wintab32.dll");
235         if (m_wintab) {
236                 GHOST_WIN32_WTInfo fpWTInfo = ( GHOST_WIN32_WTInfo ) ::GetProcAddress( m_wintab, "WTInfoA" );
237                 GHOST_WIN32_WTOpen fpWTOpen = ( GHOST_WIN32_WTOpen ) ::GetProcAddress( m_wintab, "WTOpenA" );
238
239                 // let's see if we can initialize tablet here
240                 /* check if WinTab available. */
241                 if (fpWTInfo && fpWTInfo(0, 0, NULL)) {
242                         // Now init the tablet
243                         LOGCONTEXT lc;
244                         AXIS TabletX, TabletY, Pressure, Orientation[3]; /* The maximum tablet size, pressure and orientation (tilt) */
245
246                         // Open a Wintab context
247
248                         // Get default context information
249                         fpWTInfo( WTI_DEFCONTEXT, 0, &lc );
250
251                         // Open the context
252                         lc.lcPktData = PACKETDATA;
253                         lc.lcPktMode = PACKETMODE;
254                         lc.lcOptions |= CXO_MESSAGES | CXO_SYSTEM;
255
256                         /* Set the entire tablet as active */
257                         fpWTInfo(WTI_DEVICES,DVC_X,&TabletX);
258                         fpWTInfo(WTI_DEVICES,DVC_Y,&TabletY);
259
260                         /* get the max pressure, to divide into a float */
261                         BOOL pressureSupport = fpWTInfo (WTI_DEVICES, DVC_NPRESSURE, &Pressure);
262                         if (pressureSupport)
263                                 m_maxPressure = Pressure.axMax;
264                         else
265                                 m_maxPressure = 0;
266
267                         /* get the max tilt axes, to divide into floats */
268                         BOOL tiltSupport = fpWTInfo (WTI_DEVICES, DVC_ORIENTATION, &Orientation);
269                         if (tiltSupport) {
270                                 /* does the tablet support azimuth ([0]) and altitude ([1]) */
271                                 if (Orientation[0].axResolution && Orientation[1].axResolution) {
272                                         /* all this assumes the minimum is 0 */
273                                         m_maxAzimuth = Orientation[0].axMax;
274                                         m_maxAltitude = Orientation[1].axMax;
275                                 }
276                                 else {  /* no so dont do tilt stuff */
277                                         m_maxAzimuth = m_maxAltitude = 0;
278                                 }
279                         }
280
281                         if (fpWTOpen) {
282                                 m_tablet = fpWTOpen( m_hWnd, &lc, TRUE );
283                                 if (m_tablet) {
284                                         m_tabletData = new GHOST_TabletData();
285                                         m_tabletData->Active = GHOST_kTabletModeNone;
286                                 }
287                         }
288                 }
289         }
290 }
291
292
293 GHOST_WindowWin32::~GHOST_WindowWin32()
294 {
295         if (m_wintab) {
296                 GHOST_WIN32_WTClose fpWTClose = ( GHOST_WIN32_WTClose ) ::GetProcAddress( m_wintab, "WTClose" );
297                 if (fpWTClose) {
298                         if (m_tablet)
299                                 fpWTClose(m_tablet);
300                         if (m_tabletData)
301                                 delete m_tabletData;
302                                 m_tabletData = NULL;
303                 }
304         }
305         if (m_customCursor) {
306                 DestroyCursor(m_customCursor);
307                 m_customCursor = NULL;
308         }
309
310         m_multisampleEnabled = GHOST_kFailure;
311         m_multisample = 0;
312         setDrawingContextType(GHOST_kDrawingContextTypeNone);
313         if (m_hDC) {
314                 ::ReleaseDC(m_hWnd, m_hDC);
315                 m_hDC = 0;
316         }
317         if (m_hWnd) {
318                 m_dropTarget->Release(); // frees itself.
319                 ::DestroyWindow(m_hWnd);
320                 m_hWnd = 0;
321         }
322 }
323
324 GHOST_Window *GHOST_WindowWin32::getNextWindow()
325 {
326         return m_nextWindow;
327 }
328
329 bool GHOST_WindowWin32::getValid() const
330 {
331         return m_hWnd != 0;
332 }
333
334 HWND GHOST_WindowWin32::getHWND() const
335 {
336         return m_hWnd;
337 }
338
339 void GHOST_WindowWin32::setTitle(const STR_String& title)
340 {
341         ::SetWindowText(m_hWnd, title);
342 }
343
344
345 void GHOST_WindowWin32::getTitle(STR_String& title) const
346 {
347         char buf[s_maxTitleLength];
348         ::GetWindowText(m_hWnd, buf, s_maxTitleLength);
349         STR_String temp (buf);
350         title = buf;
351 }
352
353
354 void GHOST_WindowWin32::getWindowBounds(GHOST_Rect& bounds) const
355 {
356         RECT rect;
357         ::GetWindowRect(m_hWnd, &rect);
358         bounds.m_b = rect.bottom;
359         bounds.m_l = rect.left;
360         bounds.m_r = rect.right;
361         bounds.m_t = rect.top;
362 }
363
364
365 void GHOST_WindowWin32::getClientBounds(GHOST_Rect& bounds) const
366 {
367         RECT rect;
368
369         LONG_PTR result = ::GetWindowLongPtr(m_hWnd, GWL_STYLE);
370         ::GetWindowRect(m_hWnd, &rect);
371
372         if((result & (WS_POPUP | WS_MAXIMIZE)) != (WS_POPUP | WS_MAXIMIZE)) {
373                 bounds.m_b = rect.bottom-GetSystemMetrics(SM_CYCAPTION)-GetSystemMetrics(SM_CYSIZEFRAME)*2;
374                 bounds.m_l = rect.left;
375                 bounds.m_r = rect.right-GetSystemMetrics(SM_CYSIZEFRAME)*2;
376                 bounds.m_t = rect.top;
377         } else {
378                 ::GetWindowRect(m_hWnd, &rect);
379                 bounds.m_b = rect.bottom;
380                 bounds.m_l = rect.left;
381                 bounds.m_r = rect.right;
382                 bounds.m_t = rect.top;
383         }
384 }
385
386
387 GHOST_TSuccess GHOST_WindowWin32::setClientWidth(GHOST_TUns32 width)
388 {
389         GHOST_TSuccess success;
390         GHOST_Rect cBnds, wBnds;
391         getClientBounds(cBnds);
392         if (cBnds.getWidth() != (GHOST_TInt32)width) {
393                 getWindowBounds(wBnds);
394                 int cx = wBnds.getWidth() + width - cBnds.getWidth();
395                 int cy = wBnds.getHeight();
396                 success =  ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
397                         GHOST_kSuccess : GHOST_kFailure;
398         }
399         else {
400                 success = GHOST_kSuccess;
401         }
402         return success;
403 }
404
405
406 GHOST_TSuccess GHOST_WindowWin32::setClientHeight(GHOST_TUns32 height)
407 {
408         GHOST_TSuccess success;
409         GHOST_Rect cBnds, wBnds;
410         getClientBounds(cBnds);
411         if (cBnds.getHeight() != (GHOST_TInt32)height) {
412                 getWindowBounds(wBnds);
413                 int cx = wBnds.getWidth();
414                 int cy = wBnds.getHeight() + height - cBnds.getHeight();
415                 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
416                         GHOST_kSuccess : GHOST_kFailure;
417         }
418         else {
419                 success = GHOST_kSuccess;
420         }
421         return success;
422 }
423
424
425 GHOST_TSuccess GHOST_WindowWin32::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
426 {
427         GHOST_TSuccess success;
428         GHOST_Rect cBnds, wBnds;
429         getClientBounds(cBnds);
430         if ((cBnds.getWidth() != (GHOST_TInt32)width) || (cBnds.getHeight() != (GHOST_TInt32)height)) {
431                 getWindowBounds(wBnds);
432                 int cx = wBnds.getWidth() + width - cBnds.getWidth();
433                 int cy = wBnds.getHeight() + height - cBnds.getHeight();
434                 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
435                         GHOST_kSuccess : GHOST_kFailure;
436         }
437         else {
438                 success = GHOST_kSuccess;
439         }
440         return success;
441 }
442
443
444 GHOST_TWindowState GHOST_WindowWin32::getState() const
445 {
446         GHOST_TWindowState state;
447         if (::IsIconic(m_hWnd)) {
448                 state = GHOST_kWindowStateMinimized;
449         }
450         else if (::IsZoomed(m_hWnd)) {
451                 LONG_PTR result = ::GetWindowLongPtr(m_hWnd, GWL_STYLE);
452                 if((result & (WS_POPUP | WS_MAXIMIZE)) != (WS_POPUP | WS_MAXIMIZE))
453                         state = GHOST_kWindowStateMaximized;
454                 else
455                         state = GHOST_kWindowStateFullScreen;
456         }
457         else {
458                 state = GHOST_kWindowStateNormal;
459         }
460         return state;
461 }
462
463
464 void GHOST_WindowWin32::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
465 {
466         POINT point = { inX, inY };
467         ::ScreenToClient(m_hWnd, &point);
468         outX = point.x;
469         outY = point.y;
470 }
471
472
473 void GHOST_WindowWin32::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
474 {
475         POINT point = { inX, inY };
476         ::ClientToScreen(m_hWnd, &point);
477         outX = point.x;
478         outY = point.y;
479 }
480
481
482 GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
483 {
484         WINDOWPLACEMENT wp;
485         wp.length = sizeof(WINDOWPLACEMENT);
486         ::GetWindowPlacement(m_hWnd, &wp);
487         switch (state) {
488         case GHOST_kWindowStateMinimized:
489                 wp.showCmd = SW_SHOWMINIMIZED;
490                 break;
491         case GHOST_kWindowStateMaximized:
492                 ShowWindow(m_hWnd, SW_HIDE);
493                 wp.showCmd = SW_SHOWMAXIMIZED;
494                 SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
495                 break;
496         case GHOST_kWindowStateFullScreen:
497                 wp.showCmd = SW_SHOWMAXIMIZED;
498                 wp.ptMaxPosition.x = 0;
499                 wp.ptMaxPosition.y = 0;
500                 SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_POPUP | WS_MAXIMIZE);
501                 break;
502         case GHOST_kWindowStateNormal:
503         default:
504                 ShowWindow(m_hWnd, SW_HIDE);
505                 wp.showCmd = SW_SHOWNORMAL;
506                 SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
507                 break;
508         }
509         return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
510 }
511
512
513 GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order)
514 {
515         HWND hWndInsertAfter = order == GHOST_kWindowOrderTop ? HWND_TOP : HWND_BOTTOM;
516         return ::SetWindowPos(m_hWnd, hWndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
517 }
518
519
520 GHOST_TSuccess GHOST_WindowWin32::swapBuffers()
521 {
522         return ::SwapBuffers(m_hDC) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
523 }
524
525
526 GHOST_TSuccess GHOST_WindowWin32::activateDrawingContext()
527 {
528         GHOST_TSuccess success;
529         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
530                 if (m_hDC && m_hGlRc) {
531                         success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
532                 }
533                 else {
534                         success = GHOST_kFailure;
535                 }
536         }
537         else {
538                 success = GHOST_kSuccess;
539         }
540         return success;
541 }
542
543
544 GHOST_TSuccess GHOST_WindowWin32::invalidate()
545 {
546         GHOST_TSuccess success;
547         if (m_hWnd) {
548                 success = ::InvalidateRect(m_hWnd, 0, FALSE) != 0 ? GHOST_kSuccess : GHOST_kFailure;
549         }
550         else {
551                 success = GHOST_kFailure;
552         }
553         return success;
554 }
555
556 GHOST_TSuccess GHOST_WindowWin32::initMultisample(PIXELFORMATDESCRIPTOR pfd)
557 {
558         int pixelFormat;
559         bool success;
560         UINT numFormats;
561         HDC hDC = GetDC(getHWND());
562         float fAttributes[] = {0, 0};
563
564         // The attributes to look for
565         int iAttributes[] = {
566                 WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
567                 WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
568                 WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
569                 WGL_COLOR_BITS_ARB, pfd.cColorBits,
570                 WGL_DEPTH_BITS_ARB, pfd.cDepthBits,
571                 WGL_STENCIL_BITS_ARB, pfd.cStencilBits,
572                 WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
573                 WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
574                 WGL_SAMPLES_ARB, m_multisample,
575                 0, 0
576         };
577
578         // Get the function
579         PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");  
580
581         if (!wglChoosePixelFormatARB)
582         {
583                 m_multisampleEnabled = GHOST_kFailure;
584                 return GHOST_kFailure;
585         }
586
587         // See if the format is valid
588         success = wglChoosePixelFormatARB(hDC, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
589
590         if (success && numFormats >= 1)
591         {
592                 m_multisampleEnabled = GHOST_kSuccess;
593                 m_msPixelFormat = pixelFormat;
594                 return GHOST_kSuccess;
595         }
596         else
597         {
598                 // See if any formats are supported
599                 while (!success && iAttributes[19] != 0)
600                 {
601                         iAttributes[19] /= 2;
602
603                         success = wglChoosePixelFormatARB(m_hDC, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
604
605                         if (success && numFormats >= 1)
606                         {
607                                 m_multisampleEnabled = GHOST_kSuccess;
608                                 m_msPixelFormat = pixelFormat;
609                                 return GHOST_kSuccess;
610                         }
611
612                         success = GHOST_kFailure;
613                 }
614         }
615
616         // No available pixel format...
617         return GHOST_kFailure;
618 }
619
620 GHOST_TSuccess GHOST_WindowWin32::installDrawingContext(GHOST_TDrawingContextType type)
621 {
622         GHOST_TSuccess success;
623         switch (type) {
624         case GHOST_kDrawingContextTypeOpenGL:
625                 {
626                 // If this window has multisample enabled, use the supplied format
627                 if (m_multisampleEnabled)
628                 {
629                         if (SetPixelFormat(m_hDC, m_msPixelFormat, &sPreferredFormat)==FALSE)
630                         {
631                                 success = GHOST_kFailure;
632                                 break;
633                         }
634
635                         // Create the context
636                         m_hGlRc = ::wglCreateContext(m_hDC);
637                         if (m_hGlRc) {
638                                 if (s_firsthGLRc) {
639                                         wglShareLists(s_firsthGLRc, m_hGlRc);
640                                 } else {
641                                         s_firsthGLRc = m_hGlRc;
642                                 }
643
644                                 success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
645                         }
646                         else {
647                                 printf("Failed to get a context....\n");
648                                 success = GHOST_kFailure;
649                         }
650                 }
651                 else
652                 {
653                         if(m_stereoVisual)
654                                 sPreferredFormat.dwFlags |= PFD_STEREO;
655
656                         // Attempt to match device context pixel format to the preferred format
657                         int iPixelFormat = EnumPixelFormats(m_hDC);
658                         if (iPixelFormat == 0) {
659                                 success = GHOST_kFailure;
660                                 break;
661                         }
662                         if (::SetPixelFormat(m_hDC, iPixelFormat, &sPreferredFormat) == FALSE) {
663                                 success = GHOST_kFailure;
664                                 break;
665                         }
666                         // For debugging only: retrieve the pixel format chosen
667                         PIXELFORMATDESCRIPTOR preferredFormat;
668                         ::DescribePixelFormat(m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &preferredFormat);
669
670                         // Create the context
671                         m_hGlRc = ::wglCreateContext(m_hDC);
672                         if (m_hGlRc) {
673                                 if (s_firsthGLRc) {
674                                         wglShareLists(s_firsthGLRc, m_hGlRc);
675                                 } else {
676                                         s_firsthGLRc = m_hGlRc;
677                                 }
678
679                                 success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
680                         }
681                         else {
682                                 printf("Failed to get a context....\n");
683                                 success = GHOST_kFailure;
684                         }
685                                         
686                         // Attempt to enable multisample
687                         if (m_multisample && WGL_ARB_multisample && !m_multisampleEnabled)
688                         {
689                                 success = initMultisample(preferredFormat);
690
691                                 if (success)
692                                 {
693
694                                         // Make sure we don't screw up the context
695                                         m_drawingContextType = GHOST_kDrawingContextTypeOpenGL;
696                                         removeDrawingContext();
697
698                                         // Create a new window
699                                         GHOST_TWindowState new_state = getState();
700
701                                         m_nextWindow = new GHOST_WindowWin32((GHOST_SystemWin32*)GHOST_ISystem::getSystem(),
702                                                                                                         m_title,
703                                                                                                         m_left,
704                                                                                                         m_top,
705                                                                                                         m_width,
706                                                                                                         m_height,
707                                                                                                         new_state,
708                                                                                                         type,
709                                                                                                         m_stereo,
710                                                                                                         m_multisample,
711                                                                                                         m_multisampleEnabled,
712                                                                                                         m_msPixelFormat);
713
714                                         // Return failure so we can trash this window.
715                                         success = GHOST_kFailure;
716                                         break;
717                                 }
718                         }
719                 }
720
721                 }
722                 break;
723
724         case GHOST_kDrawingContextTypeNone:
725                 success = GHOST_kSuccess;
726                 break;
727
728         default:
729                 success = GHOST_kFailure;
730         }
731         return success;
732 }
733
734 GHOST_TSuccess GHOST_WindowWin32::removeDrawingContext()
735 {
736         GHOST_TSuccess success;
737         switch (m_drawingContextType) {
738         case GHOST_kDrawingContextTypeOpenGL:
739                 if (m_hGlRc) {
740                         success = ::wglDeleteContext(m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
741                         if (m_hGlRc == s_firsthGLRc) {
742                                 s_firsthGLRc = NULL;
743                         }
744                         m_hGlRc = 0;
745                 }
746                 else {
747                         success = GHOST_kFailure;
748                 }
749                 break;
750         case GHOST_kDrawingContextTypeNone:
751                 success = GHOST_kSuccess;
752                 break;
753         default:
754                 success = GHOST_kFailure;
755         }
756         return success;
757 }
758
759 void GHOST_WindowWin32::lostMouseCapture()
760 {
761         if (m_hasMouseCaptured) {
762                 m_hasMouseCaptured = false;
763                 m_nPressedButtons = 0;
764         }
765 }
766
767 void GHOST_WindowWin32::registerMouseClickEvent(bool press)
768 {
769         if (press) {
770                 if (!m_hasMouseCaptured) {
771                         ::SetCapture(m_hWnd);
772                         m_hasMouseCaptured = true;
773                 }
774                 m_nPressedButtons++;
775         } else {
776                 if (m_nPressedButtons) {
777                         m_nPressedButtons--;
778                         if (!m_nPressedButtons) {
779                                 ::ReleaseCapture();
780                                 m_hasMouseCaptured = false;
781                         }
782                 }
783         }
784 }
785
786
787 void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
788 {
789         if (!visible) {
790                 while (::ShowCursor(FALSE) >= 0);
791         }
792         else {
793                 while (::ShowCursor(TRUE) < 0);
794         }
795
796         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
797                 ::SetCursor( m_customCursor );
798         } else {
799                 // Convert GHOST cursor to Windows OEM cursor
800                 bool success = true;
801                 LPCSTR id;
802                 switch (cursor) {
803                         case GHOST_kStandardCursorDefault:              id = IDC_ARROW;         break;
804                         case GHOST_kStandardCursorRightArrow:           id = IDC_ARROW;         break;
805                         case GHOST_kStandardCursorLeftArrow:            id = IDC_ARROW;         break;
806                         case GHOST_kStandardCursorInfo:                 id = IDC_SIZEALL;       break;  // Four-pointed arrow pointing north, south, east, and west
807                         case GHOST_kStandardCursorDestroy:              id = IDC_NO;            break;  // Slashed circle
808                         case GHOST_kStandardCursorHelp:                 id = IDC_HELP;          break;  // Arrow and question mark
809                         case GHOST_kStandardCursorCycle:                id = IDC_NO;            break;  // Slashed circle
810                         case GHOST_kStandardCursorSpray:                id = IDC_SIZEALL;       break;  // Four-pointed arrow pointing north, south, east, and west
811                         case GHOST_kStandardCursorWait:                 id = IDC_WAIT;          break;  // Hourglass
812                         case GHOST_kStandardCursorText:                 id = IDC_IBEAM;         break;  // I-beam
813                         case GHOST_kStandardCursorCrosshair:            id = IDC_CROSS;         break;  // Crosshair
814                         case GHOST_kStandardCursorUpDown:               id = IDC_SIZENS;        break;  // Double-pointed arrow pointing north and south
815                         case GHOST_kStandardCursorLeftRight:            id = IDC_SIZEWE;        break;  // Double-pointed arrow pointing west and east
816                         case GHOST_kStandardCursorTopSide:              id = IDC_UPARROW;       break;  // Vertical arrow
817                         case GHOST_kStandardCursorBottomSide:           id = IDC_SIZENS;        break;
818                         case GHOST_kStandardCursorLeftSide:             id = IDC_SIZEWE;        break;
819                         case GHOST_kStandardCursorTopLeftCorner:        id = IDC_SIZENWSE;      break;
820                         case GHOST_kStandardCursorTopRightCorner:       id = IDC_SIZENESW;      break;
821                         case GHOST_kStandardCursorBottomRightCorner:    id = IDC_SIZENWSE;      break;
822                         case GHOST_kStandardCursorBottomLeftCorner:     id = IDC_SIZENESW;      break;
823                         case GHOST_kStandardCursorPencil:               id = IDC_ARROW;         break;
824                         default:
825                         success = false;
826                 }
827
828                 if (success) {
829                         ::SetCursor(::LoadCursor(0, id));
830                 }
831         }
832 }
833
834 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible)
835 {
836         if (::GetForegroundWindow() == m_hWnd) {
837                 loadCursor(visible, getCursorShape());
838         }
839
840         return GHOST_kSuccess;
841 }
842
843 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
844 {
845         if(mode != GHOST_kGrabDisable) {
846                 if(mode != GHOST_kGrabNormal) {
847                         m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
848                         setCursorGrabAccum(0, 0);
849
850                         if(mode == GHOST_kGrabHide)
851                                 setWindowCursorVisibility(false);
852                 }
853                 registerMouseClickEvent(true);
854         }
855         else {
856                 if (m_cursorGrab==GHOST_kGrabHide) {
857                         m_system->setCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
858                         setWindowCursorVisibility(true);
859                 }
860                 if(m_cursorGrab != GHOST_kGrabNormal) {
861                         /* use to generate a mouse move event, otherwise the last event
862                          * blender gets can be outside the screen causing menus not to show
863                          * properly unless the user moves the mouse */
864                          GHOST_TInt32 pos[2];
865                          m_system->getCursorPosition(pos[0], pos[1]);
866                          m_system->setCursorPosition(pos[0], pos[1]);
867                 }
868
869                 /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
870                 setCursorGrabAccum(0, 0);
871                 m_cursorGrabBounds.m_l= m_cursorGrabBounds.m_r= -1; /* disable */
872                 registerMouseClickEvent(false);
873         }
874         
875         return GHOST_kSuccess;
876 }
877
878 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
879 {
880         if (m_customCursor) {
881                 DestroyCursor(m_customCursor);
882                 m_customCursor = NULL;
883         }
884
885         if (::GetForegroundWindow() == m_hWnd) {
886                 loadCursor(getCursorVisibility(), cursorShape);
887         }
888
889         return GHOST_kSuccess;
890 }
891
892 void GHOST_WindowWin32::processWin32TabletInitEvent()
893 {
894         if (m_wintab) {
895                 GHOST_WIN32_WTInfo fpWTInfo = ( GHOST_WIN32_WTInfo ) ::GetProcAddress( m_wintab, "WTInfoA" );
896
897                 // let's see if we can initialize tablet here
898                 /* check if WinTab available. */
899                 if (fpWTInfo) {
900                         AXIS Pressure, Orientation[3]; /* The maximum tablet size */
901
902                         BOOL pressureSupport = fpWTInfo (WTI_DEVICES, DVC_NPRESSURE, &Pressure);
903                         if (pressureSupport)
904                                 m_maxPressure = Pressure.axMax;
905                         else
906                                 m_maxPressure = 0;
907
908                         BOOL tiltSupport = fpWTInfo (WTI_DEVICES, DVC_ORIENTATION, &Orientation);
909                         if (tiltSupport) {
910                                 /* does the tablet support azimuth ([0]) and altitude ([1]) */
911                                 if (Orientation[0].axResolution && Orientation[1].axResolution) {
912                                         m_maxAzimuth = Orientation[0].axMax;
913                                         m_maxAltitude = Orientation[1].axMax;
914                                 }
915                                 else {  /* no so dont do tilt stuff */
916                                         m_maxAzimuth = m_maxAltitude = 0;
917                                 }
918                         }
919
920                         m_tabletData->Active = GHOST_kTabletModeNone;
921                 }
922         }
923 }
924
925 void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam)
926 {
927         PACKET pkt;
928         if (m_wintab) {
929                 GHOST_WIN32_WTPacket fpWTPacket = ( GHOST_WIN32_WTPacket ) ::GetProcAddress( m_wintab, "WTPacket" );
930                 if (fpWTPacket) {
931                         if (fpWTPacket((HCTX)lParam, wParam, &pkt)) {
932                                 if (m_tabletData) {
933                                         switch (pkt.pkCursor) {
934                                                 case 0: /* first device */
935                                                 case 3: /* second device */
936                                                         m_tabletData->Active = GHOST_kTabletModeNone; /* puck - not yet supported */
937                                                         break;
938                                                 case 1:
939                                                 case 4:
940                                                         m_tabletData->Active = GHOST_kTabletModeStylus; /* stylus */
941                                                         break;
942                                                 case 2:
943                                                 case 5:
944                                                         m_tabletData->Active = GHOST_kTabletModeEraser; /* eraser */
945                                                         break;
946                                         }
947                                         if (m_maxPressure > 0) {
948                                                 m_tabletData->Pressure = (float)pkt.pkNormalPressure / (float)m_maxPressure;
949                                         } else {
950                                                 m_tabletData->Pressure = 1.0f;
951                                         }
952
953                                         if ((m_maxAzimuth > 0) && (m_maxAltitude > 0)) {
954                                                 ORIENTATION ort = pkt.pkOrientation;
955                                                 float vecLen;
956                                                 float altRad, azmRad;   /* in radians */
957
958                                                 /*
959                                                 from the wintab spec:
960                                                 orAzimuth       Specifies the clockwise rotation of the
961                                                 cursor about the z axis through a full circular range.
962
963                                                 orAltitude      Specifies the angle with the x-y plane
964                                                 through a signed, semicircular range.  Positive values
965                                                 specify an angle upward toward the positive z axis;
966                                                 negative values specify an angle downward toward the negative z axis.
967
968                                                 wintab.h defines .orAltitude as a UINT but documents .orAltitude
969                                                 as positive for upward angles and negative for downward angles.
970                                                 WACOM uses negative altitude values to show that the pen is inverted;
971                                                 therefore we cast .orAltitude as an (int) and then use the absolute value.
972                                                 */
973
974                                                 /* convert raw fixed point data to radians */
975                                                 altRad = (float)((fabs((float)ort.orAltitude)/(float)m_maxAltitude) * M_PI/2.0);
976                                                 azmRad = (float)(((float)ort.orAzimuth/(float)m_maxAzimuth) * M_PI*2.0);
977
978                                                 /* find length of the stylus' projected vector on the XY plane */
979                                                 vecLen = cos(altRad);
980
981                                                 /* from there calculate X and Y components based on azimuth */
982                                                 m_tabletData->Xtilt = sin(azmRad) * vecLen;
983                                                 m_tabletData->Ytilt = (float)(sin(M_PI/2.0 - azmRad) * vecLen);
984
985                                         } else {
986                                                 m_tabletData->Xtilt = 0.0f;
987                                                 m_tabletData->Ytilt = 0.0f;
988                                         }
989                                 }
990                         }
991                 }
992         }
993 }
994
995 /** Reverse the bits in a GHOST_TUns8 */
996 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
997 {
998         ch= ((ch>>1)&0x55) | ((ch<<1)&0xAA);
999         ch= ((ch>>2)&0x33) | ((ch<<2)&0xCC);
1000         ch= ((ch>>4)&0x0F) | ((ch<<4)&0xF0);
1001         return ch;
1002 }
1003
1004 /** Reverse the bits in a GHOST_TUns16 */
1005 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
1006 {
1007         shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
1008         shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
1009         shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
1010         shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
1011         return shrt;
1012 }
1013 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2],
1014                                         GHOST_TUns8 mask[16][2], int hotX, int hotY)
1015 {
1016         return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*)mask,
1017                                                                         16, 16, hotX, hotY, 0, 1);
1018 }
1019
1020 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 *bitmap,
1021                                         GHOST_TUns8 *mask, int sizeX, int sizeY, int hotX, int hotY,
1022                                         int fg_color, int bg_color)
1023 {
1024         GHOST_TUns32 andData[32];
1025         GHOST_TUns32 xorData[32];
1026         GHOST_TUns32 fullBitRow, fullMaskRow;
1027         int x, y, cols;
1028
1029         cols=sizeX/8; /* Num of whole bytes per row (width of bm/mask) */
1030         if (sizeX%8) cols++;
1031
1032         if (m_customCursor) {
1033                 DestroyCursor(m_customCursor);
1034                 m_customCursor = NULL;
1035         }
1036
1037         memset(&andData, 0xFF, sizeof(andData));
1038         memset(&xorData, 0, sizeof(xorData));
1039
1040         for (y=0; y<sizeY; y++) {
1041                 fullBitRow=0;
1042                 fullMaskRow=0;
1043                 for (x=cols-1; x>=0; x--){
1044                         fullBitRow<<=8;
1045                         fullMaskRow<<=8;
1046                         fullBitRow  |= uns8ReverseBits(bitmap[cols*y + x]);
1047                         fullMaskRow |= uns8ReverseBits(  mask[cols*y + x]);
1048                 }
1049                 xorData[y]= fullBitRow & fullMaskRow;
1050                 andData[y]= ~fullMaskRow;
1051         }
1052
1053         m_customCursor = ::CreateCursor(::GetModuleHandle(0), hotX, hotY, 32, 32, andData, xorData);
1054         if (!m_customCursor) {
1055                 return GHOST_kFailure;
1056         }
1057
1058         if (::GetForegroundWindow() == m_hWnd) {
1059                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
1060         }
1061
1062         return GHOST_kSuccess;
1063 }
1064
1065
1066 /*  Ron Fosner's code for weighting pixel formats and forcing software.
1067         See http://www.opengl.org/resources/faq/technical/weight.cpp */
1068
1069 static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd) {
1070         int weight = 0;
1071
1072         /* assume desktop color depth is 32 bits per pixel */
1073
1074         /* cull unusable pixel formats */
1075         /* if no formats can be found, can we determine why it was rejected? */
1076         if( !(pfd.dwFlags & PFD_SUPPORT_OPENGL) ||
1077                 !(pfd.dwFlags & PFD_DRAW_TO_WINDOW) ||
1078                 !(pfd.dwFlags & PFD_DOUBLEBUFFER) || /* Blender _needs_ this */
1079                 ( pfd.cDepthBits <= 8 ) ||
1080                 !(pfd.iPixelType == PFD_TYPE_RGBA) )
1081                 return 0;
1082
1083         weight = 1;  /* it's usable */
1084
1085         /* the bigger the depth buffer the better */
1086         /* give no weight to a 16-bit depth buffer, because those are crap */
1087         weight += pfd.cDepthBits - 16;
1088
1089         weight += pfd.cColorBits - 8;
1090
1091         /* want swap copy capability -- it matters a lot */
1092         if(pfd.dwFlags & PFD_SWAP_COPY) weight += 16;
1093
1094         /* but if it's a generic (not accelerated) view, it's really bad */
1095         if(pfd.dwFlags & PFD_GENERIC_FORMAT) weight /= 10;
1096
1097         return weight;
1098 }
1099
1100 /* A modification of Ron Fosner's replacement for ChoosePixelFormat */
1101 /* returns 0 on error, else returns the pixel format number to be used */
1102 static int EnumPixelFormats(HDC hdc) {
1103         int iPixelFormat;
1104         int i, n, w, weight = 0;
1105         PIXELFORMATDESCRIPTOR pfd;
1106
1107         /* we need a device context to do anything */
1108         if(!hdc) return 0;
1109
1110         iPixelFormat = 1; /* careful! PFD numbers are 1 based, not zero based */
1111
1112         /* obtain detailed information about
1113         the device context's first pixel format */
1114         n = 1+::DescribePixelFormat(hdc, iPixelFormat,
1115                 sizeof(PIXELFORMATDESCRIPTOR), &pfd);
1116
1117         /* choose a pixel format using the useless Windows function in case
1118                 we come up empty handed */
1119         iPixelFormat = ::ChoosePixelFormat( hdc, &sPreferredFormat );
1120
1121         if(!iPixelFormat) return 0; /* couldn't find one to use */
1122
1123         for(i=1; i<=n; i++) { /* not the idiom, but it's right */
1124                 ::DescribePixelFormat( hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd );
1125                 w = WeightPixelFormat(pfd);
1126                 // be strict on stereo
1127                 if (!((sPreferredFormat.dwFlags ^ pfd.dwFlags) & PFD_STEREO))   {
1128                         if(w > weight) {
1129                                 weight = w;
1130                                 iPixelFormat = i;
1131                         }
1132                 }
1133         }
1134         if (weight == 0) {
1135                 // we could find the correct stereo setting, just find any suitable format
1136                 for(i=1; i<=n; i++) { /* not the idiom, but it's right */
1137                         ::DescribePixelFormat( hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd );
1138                         w = WeightPixelFormat(pfd);
1139                         if(w > weight) {
1140                                 weight = w;
1141                                 iPixelFormat = i;
1142                         }
1143                 }
1144         }
1145         return iPixelFormat;
1146 }
1147
1148