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