Merge branch 'blender2.7'
[blender.git] / intern / ghost / intern / GHOST_WindowWin32.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Maarten Gribnau.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file ghost/intern/GHOST_WindowWin32.cpp
29  *  \ingroup GHOST
30  */
31
32 #define _USE_MATH_DEFINES
33
34 #include "GHOST_WindowWin32.h"
35 #include "GHOST_SystemWin32.h"
36 #include "GHOST_DropTargetWin32.h"
37 #include "GHOST_ContextNone.h"
38 #include "utfconv.h"
39 #include "utf_winfunc.h"
40
41 #if defined(WITH_GL_EGL)
42 #  include "GHOST_ContextEGL.h"
43 #else
44 #  include "GHOST_ContextWGL.h"
45 #endif
46 #ifdef WIN32_COMPOSITING
47 #include <Dwmapi.h>
48 #endif
49
50 #include <math.h>
51 #include <string.h>
52 #include <assert.h>
53
54
55
56 const wchar_t *GHOST_WindowWin32::s_windowClassName = L"GHOST_WindowClass";
57 const int GHOST_WindowWin32::s_maxTitleLength = 128;
58
59
60
61
62 /* force NVidia Optimus to used dedicated graphics */
63 extern "C" {
64         __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
65 }
66
67 GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
68         const STR_String &title,
69         GHOST_TInt32 left,
70         GHOST_TInt32 top,
71         GHOST_TUns32 width,
72         GHOST_TUns32 height,
73         GHOST_TWindowState state,
74         GHOST_TDrawingContextType type,
75         bool wantStereoVisual,
76         bool alphaBackground,
77         GHOST_TUns16 wantNumOfAASamples,
78         GHOST_TEmbedderWindowID parentwindowhwnd,
79         bool is_debug)
80     : GHOST_Window(width, height, state,
81                    wantStereoVisual, false, wantNumOfAASamples),
82       m_inLiveResize(false),
83       m_system(system),
84       m_hDC(0),
85       m_hasMouseCaptured(false),
86       m_hasGrabMouse(false),
87       m_nPressedButtons(0),
88       m_customCursor(0),
89       m_wantAlphaBackground(alphaBackground),
90       m_wintab(NULL),
91       m_tabletData(NULL),
92       m_tablet(0),
93       m_maxPressure(0),
94       m_normal_state(GHOST_kWindowStateNormal),
95           m_user32(NULL),
96       m_parentWindowHwnd(parentwindowhwnd),
97       m_debug_context(is_debug)
98 {
99         if (state != GHOST_kWindowStateFullScreen) {
100                 RECT rect;
101                 MONITORINFO monitor;
102                 GHOST_TUns32 tw, th;
103
104 #ifndef _MSC_VER
105                 int cxsizeframe = GetSystemMetrics(SM_CXSIZEFRAME);
106                 int cysizeframe = GetSystemMetrics(SM_CYSIZEFRAME);
107 #else
108                 // MSVC 2012+ returns bogus values from GetSystemMetrics, bug in Windows
109                 // http://connect.microsoft.com/VisualStudio/feedback/details/753224/regression-getsystemmetrics-delivers-different-values
110                 RECT cxrect = {0, 0, 0, 0};
111                 AdjustWindowRectEx(&cxrect, WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_THICKFRAME | WS_DLGFRAME, FALSE, 0);
112
113                 int cxsizeframe = abs(cxrect.bottom);
114                 int cysizeframe = abs(cxrect.left);
115 #endif
116
117                 width += cxsizeframe * 2;
118                 height += cysizeframe * 2 + GetSystemMetrics(SM_CYCAPTION);
119
120                 rect.left = left;
121                 rect.right = left + width;
122                 rect.top = top;
123                 rect.bottom = top + height;
124
125                 monitor.cbSize = sizeof(monitor);
126                 monitor.dwFlags = 0;
127
128                 // take taskbar into account
129                 GetMonitorInfo(MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST), &monitor);
130
131                 th = monitor.rcWork.bottom - monitor.rcWork.top;
132                 tw = monitor.rcWork.right - monitor.rcWork.left;
133
134                 if (tw < width) {
135                         width = tw;
136                         left = monitor.rcWork.left;
137                 }
138                 else if (monitor.rcWork.right < left + (int)width)
139                         left = monitor.rcWork.right - width;
140                 else if (left < monitor.rcWork.left)
141                         left = monitor.rcWork.left;
142
143                 if (th < height) {
144                         height = th;
145                         top = monitor.rcWork.top;
146                 }
147                 else if (monitor.rcWork.bottom < top + (int)height)
148                         top = monitor.rcWork.bottom - height;
149                 else if (top < monitor.rcWork.top)
150                         top = monitor.rcWork.top;
151
152                 int wintype = WS_OVERLAPPEDWINDOW;
153                 if (m_parentWindowHwnd != 0) {
154                         wintype = WS_CHILD;
155                         GetWindowRect((HWND)m_parentWindowHwnd, &rect);
156                         left = 0;
157                         top = 0;
158                         width = rect.right - rect.left;
159                         height = rect.bottom - rect.top;
160                 }
161
162                 wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0);
163                 m_hWnd = ::CreateWindowW(
164                         s_windowClassName,          // pointer to registered class name
165                         title_16,                   // pointer to window name
166                         wintype,                    // window style
167                         left,                       // horizontal position of window
168                         top,                        // vertical position of window
169                         width,                      // window width
170                         height,                     // window height
171                         (HWND)m_parentWindowHwnd,  // handle to parent or owner window
172                         0,                          // handle to menu or child-window identifier
173                         ::GetModuleHandle(0),       // handle to application instance
174                         0);                         // pointer to window-creation data
175                 free(title_16);
176         }
177         else {
178                 wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0);
179                 m_hWnd = ::CreateWindowW(
180                     s_windowClassName,          // pointer to registered class name
181                     title_16,                   // pointer to window name
182                     WS_POPUP | WS_MAXIMIZE,     // window style
183                     left,                       // horizontal position of window
184                     top,                        // vertical position of window
185                     width,                      // window width
186                     height,                     // window height
187                     HWND_DESKTOP,               // handle to parent or owner window
188                     0,                          // handle to menu or child-window identifier
189                     ::GetModuleHandle(0),       // handle to application instance
190                     0);                         // pointer to window-creation data
191                 free(title_16);
192         }
193
194         m_user32 = ::LoadLibrary("user32.dll");
195
196         if (m_hWnd) {
197                 if (m_user32) {
198                         // Touch enabled screens with pen support by default have gestures
199                         // enabled, which results in a delay between the pointer down event
200                         // and the first move when using the stylus. RegisterTouchWindow
201                         // disables the new gesture architecture enabling the events to be
202                         // sent immediately to the application rather than being absorbed by
203                         // the gesture API.
204                         GHOST_WIN32_RegisterTouchWindow pRegisterTouchWindow =
205                                 (GHOST_WIN32_RegisterTouchWindow)GetProcAddress(m_user32, "RegisterTouchWindow");
206                         if (pRegisterTouchWindow) {
207                                 pRegisterTouchWindow(m_hWnd, 0);
208                         }
209                 }
210
211                 // Register this window as a droptarget. Requires m_hWnd to be valid.
212                 // Note that OleInitialize(0) has to be called prior to this. Done in GHOST_SystemWin32.
213                 m_dropTarget = new GHOST_DropTargetWin32(this, m_system);
214                 if (m_dropTarget) {
215                         ::RegisterDragDrop(m_hWnd, m_dropTarget);
216                 }
217
218                 // Store a pointer to this class in the window structure
219                 ::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR) this);
220
221                 if (!m_system->m_windowFocus) {
222                         // Lower to bottom and don't activate if we don't want focus
223                         ::SetWindowPos(m_hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
224                 }
225
226                 // Store the device context
227                 m_hDC = ::GetDC(m_hWnd);
228
229                 GHOST_TSuccess success = setDrawingContextType(type);
230
231                 if (success) {
232                         // Show the window
233                         int nCmdShow;
234                         switch (state) {
235                                 case GHOST_kWindowStateMaximized:
236                                         nCmdShow = SW_SHOWMAXIMIZED;
237                                         break;
238                                 case GHOST_kWindowStateMinimized:
239                                         nCmdShow = (m_system->m_windowFocus) ? SW_SHOWMINIMIZED : SW_SHOWMINNOACTIVE;
240                                         break;
241                                 case GHOST_kWindowStateNormal:
242                                 default:
243                                         nCmdShow = (m_system->m_windowFocus) ? SW_SHOWNORMAL : SW_SHOWNOACTIVATE;
244                                         break;
245                         }
246
247                         ::ShowWindow(m_hWnd, nCmdShow);
248 #ifdef WIN32_COMPOSITING
249                         if (alphaBackground && parentwindowhwnd == 0) {
250
251                                 HRESULT hr = S_OK;
252
253                                 // Create and populate the Blur Behind structure
254                                 DWM_BLURBEHIND bb = { 0 };
255
256                                 // Enable Blur Behind and apply to the entire client area
257                                 bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
258                                 bb.fEnable = true;
259                                 bb.hRgnBlur = CreateRectRgn(0, 0, -1, -1);
260
261                                 // Apply Blur Behind
262                                 hr = DwmEnableBlurBehindWindow(m_hWnd, &bb);
263                                 DeleteObject(bb.hRgnBlur);
264                         }
265 #endif
266                         // Force an initial paint of the window
267                         ::UpdateWindow(m_hWnd);
268                 }
269                 else {
270                         //invalidate the window
271                         ::DestroyWindow(m_hWnd);
272                         m_hWnd = NULL;
273                 }
274         }
275
276         if (parentwindowhwnd != 0) {
277                 RAWINPUTDEVICE device = {0};
278                 device.usUsagePage  = 0x01; /* usUsagePage & usUsage for keyboard*/
279                 device.usUsage      = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */
280                 device.dwFlags |= RIDEV_INPUTSINK; // makes WM_INPUT is visible for ghost when has parent window
281                 device.hwndTarget = m_hWnd;
282                 RegisterRawInputDevices(&device, 1, sizeof(device));
283         }
284
285         m_wintab = ::LoadLibrary("Wintab32.dll");
286         if (m_wintab) {
287                 GHOST_WIN32_WTInfo fpWTInfo = (GHOST_WIN32_WTInfo) ::GetProcAddress(m_wintab, "WTInfoA");
288                 GHOST_WIN32_WTOpen fpWTOpen = (GHOST_WIN32_WTOpen) ::GetProcAddress(m_wintab, "WTOpenA");
289
290                 // Let's see if we can initialize tablet here.
291                 // Check if WinTab available by getting system context info.
292                 LOGCONTEXT lc = { 0 };
293                 lc.lcOptions |= CXO_SYSTEM;
294                 if (fpWTInfo && fpWTInfo(WTI_DEFSYSCTX, 0, &lc)) {
295                         // Now init the tablet
296                         /* The maximum tablet size, pressure and orientation (tilt) */
297                         AXIS TabletX, TabletY, Pressure, Orientation[3];
298
299                         // Open a Wintab context
300
301                         // Open the context
302                         lc.lcPktData = PACKETDATA;
303                         lc.lcPktMode = PACKETMODE;
304                         lc.lcOptions |= CXO_MESSAGES;
305                         lc.lcMoveMask = PACKETDATA;
306
307                         /* Set the entire tablet as active */
308                         fpWTInfo(WTI_DEVICES, DVC_X, &TabletX);
309                         fpWTInfo(WTI_DEVICES, DVC_Y, &TabletY);
310
311                         /* get the max pressure, to divide into a float */
312                         BOOL pressureSupport = fpWTInfo(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
313                         if (pressureSupport)
314                                 m_maxPressure = Pressure.axMax;
315                         else
316                                 m_maxPressure = 0;
317
318                         /* get the max tilt axes, to divide into floats */
319                         BOOL tiltSupport = fpWTInfo(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
320                         if (tiltSupport) {
321                                 /* does the tablet support azimuth ([0]) and altitude ([1]) */
322                                 if (Orientation[0].axResolution && Orientation[1].axResolution) {
323                                         /* all this assumes the minimum is 0 */
324                                         m_maxAzimuth = Orientation[0].axMax;
325                                         m_maxAltitude = Orientation[1].axMax;
326                                 }
327                                 else {  /* no so dont do tilt stuff */
328                                         m_maxAzimuth = m_maxAltitude = 0;
329                                 }
330                         }
331
332                         if (fpWTOpen) {
333                                 // The Wintab spec says we must open the context disabled if we are using cursor masks.
334                                 m_tablet = fpWTOpen(m_hWnd, &lc, FALSE);
335                                 if (m_tablet) {
336                                         m_tabletData = new GHOST_TabletData();
337                                         m_tabletData->Active = GHOST_kTabletModeNone;
338                                 }
339
340                                 GHOST_WIN32_WTEnable fpWTEnable = (GHOST_WIN32_WTEnable) ::GetProcAddress(m_wintab, "WTEnable");
341                                 if (fpWTEnable) {
342                                         fpWTEnable(m_tablet, TRUE);
343                                 }
344                         }
345                 }
346         }
347         CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar);
348 }
349
350
351 GHOST_WindowWin32::~GHOST_WindowWin32()
352 {
353         if (m_Bar) {
354                 m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
355                 m_Bar->Release();
356         }
357
358         if (m_wintab) {
359                 GHOST_WIN32_WTClose fpWTClose = (GHOST_WIN32_WTClose) ::GetProcAddress(m_wintab, "WTClose");
360                 if (fpWTClose) {
361                         if (m_tablet)
362                                 fpWTClose(m_tablet);
363                         delete m_tabletData;
364                         m_tabletData = NULL;
365                 }
366         }
367
368         if (m_customCursor) {
369                 DestroyCursor(m_customCursor);
370                 m_customCursor = NULL;
371         }
372
373         if (m_hWnd != NULL && m_hDC != NULL && releaseNativeHandles()) {
374                 ::ReleaseDC(m_hWnd, m_hDC);
375         }
376
377         if (m_hWnd) {
378                 if (m_dropTarget) {
379                         // Disable DragDrop
380                         RevokeDragDrop(m_hWnd);
381                         // Release our reference of the DropTarget and it will delete itself eventually.
382                         m_dropTarget->Release();
383                 }
384                 ::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, NULL);
385                 ::DestroyWindow(m_hWnd);
386                 m_hWnd = 0;
387         }
388
389         if (m_user32) {
390                 FreeLibrary(m_user32);
391                 m_user32 = NULL;
392         }
393 }
394
395 bool GHOST_WindowWin32::getValid() const
396 {
397         return GHOST_Window::getValid() && m_hWnd != 0 && m_hDC != 0;
398 }
399
400 HWND GHOST_WindowWin32::getHWND() const
401 {
402         return m_hWnd;
403 }
404
405 void GHOST_WindowWin32::setTitle(const STR_String &title)
406 {
407         wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0);
408         ::SetWindowTextW(m_hWnd, (wchar_t *)title_16);
409         free(title_16);
410 }
411
412
413 void GHOST_WindowWin32::getTitle(STR_String &title) const
414 {
415         char buf[s_maxTitleLength]; /*CHANGE + never used yet*/
416         ::GetWindowText(m_hWnd, buf, s_maxTitleLength);
417         STR_String temp(buf);
418         title = buf;
419 }
420
421
422 void GHOST_WindowWin32::getWindowBounds(GHOST_Rect &bounds) const
423 {
424         RECT rect;
425         ::GetWindowRect(m_hWnd, &rect);
426         bounds.m_b = rect.bottom;
427         bounds.m_l = rect.left;
428         bounds.m_r = rect.right;
429         bounds.m_t = rect.top;
430 }
431
432
433 void GHOST_WindowWin32::getClientBounds(GHOST_Rect &bounds) const
434 {
435         RECT rect;
436         POINT coord;
437         if (!IsIconic(m_hWnd)) {
438                 ::GetClientRect(m_hWnd, &rect);
439
440                 coord.x = rect.left;
441                 coord.y = rect.top;
442                 ::ClientToScreen(m_hWnd, &coord);
443
444                 bounds.m_l = coord.x;
445                 bounds.m_t = coord.y;
446
447                 coord.x = rect.right;
448                 coord.y = rect.bottom;
449                 ::ClientToScreen(m_hWnd, &coord);
450
451                 bounds.m_r = coord.x;
452                 bounds.m_b = coord.y;
453         }
454         else {
455                 bounds.m_b = 0;
456                 bounds.m_l = 0;
457                 bounds.m_r = 0;
458                 bounds.m_t = 0;
459         }
460 }
461
462
463 GHOST_TSuccess GHOST_WindowWin32::setClientWidth(GHOST_TUns32 width)
464 {
465         GHOST_TSuccess success;
466         GHOST_Rect cBnds, wBnds;
467         getClientBounds(cBnds);
468         if (cBnds.getWidth() != (GHOST_TInt32)width) {
469                 getWindowBounds(wBnds);
470                 int cx = wBnds.getWidth() + width - cBnds.getWidth();
471                 int cy = wBnds.getHeight();
472                 success =  ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
473                           GHOST_kSuccess : GHOST_kFailure;
474         }
475         else {
476                 success = GHOST_kSuccess;
477         }
478         return success;
479 }
480
481
482 GHOST_TSuccess GHOST_WindowWin32::setClientHeight(GHOST_TUns32 height)
483 {
484         GHOST_TSuccess success;
485         GHOST_Rect cBnds, wBnds;
486         getClientBounds(cBnds);
487         if (cBnds.getHeight() != (GHOST_TInt32)height) {
488                 getWindowBounds(wBnds);
489                 int cx = wBnds.getWidth();
490                 int cy = wBnds.getHeight() + height - cBnds.getHeight();
491                 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
492                           GHOST_kSuccess : GHOST_kFailure;
493         }
494         else {
495                 success = GHOST_kSuccess;
496         }
497         return success;
498 }
499
500
501 GHOST_TSuccess GHOST_WindowWin32::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
502 {
503         GHOST_TSuccess success;
504         GHOST_Rect cBnds, wBnds;
505         getClientBounds(cBnds);
506         if ((cBnds.getWidth() != (GHOST_TInt32)width) || (cBnds.getHeight() != (GHOST_TInt32)height)) {
507                 getWindowBounds(wBnds);
508                 int cx = wBnds.getWidth() + width - cBnds.getWidth();
509                 int cy = wBnds.getHeight() + height - cBnds.getHeight();
510                 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
511                           GHOST_kSuccess : GHOST_kFailure;
512         }
513         else {
514                 success = GHOST_kSuccess;
515         }
516         return success;
517 }
518
519
520 GHOST_TWindowState GHOST_WindowWin32::getState() const
521 {
522         GHOST_TWindowState state;
523
524         // XXX 27.04.2011
525         // we need to find a way to combine parented windows + resizing if we simply set the
526         // state as GHOST_kWindowStateEmbedded we will need to check for them somewhere else.
527         // It's also strange that in Windows is the only platform we need to make this separation.
528         if (m_parentWindowHwnd != 0) {
529                 state = GHOST_kWindowStateEmbedded;
530                 return state;
531         }
532
533         if (::IsIconic(m_hWnd)) {
534                 state = GHOST_kWindowStateMinimized;
535         }
536         else if (::IsZoomed(m_hWnd)) {
537                 LONG_PTR result = ::GetWindowLongPtr(m_hWnd, GWL_STYLE);
538                 if ((result & (WS_POPUP | WS_MAXIMIZE)) != (WS_POPUP | WS_MAXIMIZE))
539                         state = GHOST_kWindowStateMaximized;
540                 else
541                         state = GHOST_kWindowStateFullScreen;
542         }
543         else {
544                 state = GHOST_kWindowStateNormal;
545         }
546         return state;
547 }
548
549
550 void GHOST_WindowWin32::screenToClient(
551         GHOST_TInt32 inX, GHOST_TInt32 inY,
552         GHOST_TInt32 &outX, GHOST_TInt32 &outY) const
553 {
554         POINT point = {inX, inY};
555         ::ScreenToClient(m_hWnd, &point);
556         outX = point.x;
557         outY = point.y;
558 }
559
560
561 void GHOST_WindowWin32::clientToScreen(
562         GHOST_TInt32 inX, GHOST_TInt32 inY,
563         GHOST_TInt32 &outX, GHOST_TInt32 &outY) const
564 {
565         POINT point = {inX, inY};
566         ::ClientToScreen(m_hWnd, &point);
567         outX = point.x;
568         outY = point.y;
569 }
570
571
572 GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
573 {
574         GHOST_TWindowState curstate = getState();
575         WINDOWPLACEMENT wp;
576         wp.length = sizeof(WINDOWPLACEMENT);
577         ::GetWindowPlacement(m_hWnd, &wp);
578
579         if (state == GHOST_kWindowStateNormal)
580                 state = m_normal_state;
581
582         switch (state) {
583                 case GHOST_kWindowStateMinimized:
584                         wp.showCmd = SW_SHOWMINIMIZED;
585                         break;
586                 case GHOST_kWindowStateMaximized:
587                         wp.showCmd = SW_SHOWMAXIMIZED;
588                         ::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
589                         break;
590                 case GHOST_kWindowStateFullScreen:
591                         if (curstate != state && curstate != GHOST_kWindowStateMinimized)
592                                 m_normal_state = curstate;
593                         wp.showCmd = SW_SHOWMAXIMIZED;
594                         wp.ptMaxPosition.x = 0;
595                         wp.ptMaxPosition.y = 0;
596                         ::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_POPUP | WS_MAXIMIZE);
597                         break;
598                 case GHOST_kWindowStateEmbedded:
599                         ::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_CHILD);
600                         break;
601                 case GHOST_kWindowStateNormal:
602                 default:
603                         wp.showCmd = SW_SHOWNORMAL;
604                         ::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
605                         break;
606         }
607         /* Clears window cache for SetWindowLongPtr */
608         ::SetWindowPos(m_hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
609
610         return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
611 }
612
613
614 GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order)
615 {
616         HWND hWndInsertAfter, hWndToRaise;
617
618         if (order == GHOST_kWindowOrderBottom) {
619                 hWndInsertAfter = HWND_BOTTOM;
620                 hWndToRaise = ::GetWindow(m_hWnd, GW_HWNDNEXT); /* the window to raise */
621         }
622         else {
623                 hWndInsertAfter = HWND_TOP;
624                 hWndToRaise = NULL;
625         }
626
627         if (::SetWindowPos(m_hWnd, hWndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == FALSE) {
628                 return GHOST_kFailure;
629         }
630
631         if (hWndToRaise && ::SetWindowPos(hWndToRaise, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == FALSE) {
632                 return GHOST_kFailure;
633         }
634         return GHOST_kSuccess;
635 }
636
637
638 GHOST_TSuccess GHOST_WindowWin32::invalidate()
639 {
640         GHOST_TSuccess success;
641         if (m_hWnd) {
642                 success = ::InvalidateRect(m_hWnd, 0, FALSE) != 0 ? GHOST_kSuccess : GHOST_kFailure;
643         }
644         else {
645                 success = GHOST_kFailure;
646         }
647         return success;
648 }
649
650
651 GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType type)
652 {
653         if (type == GHOST_kDrawingContextTypeOpenGL) {
654                 GHOST_Context *context;
655
656 #if defined(WITH_GL_PROFILE_CORE)
657                 /* - AMD and Intel give us exactly this version
658                  * - NVIDIA gives at least this version <-- desired behavior
659                  * So we ask for 4.5, 4.4 ... 3.3 in descending order to get the best version on the user's system. */
660                 for (int minor = 5; minor >= 0; --minor) {
661                         context = new GHOST_ContextWGL(
662                             m_wantStereoVisual,
663                             m_wantAlphaBackground,
664                             m_wantNumOfAASamples,
665                             m_hWnd,
666                             m_hDC,
667                             WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
668                             4, minor,
669                             (m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
670                             GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
671
672                         if (context->initializeDrawingContext()) {
673                                 return context;
674                         }
675                         else {
676                                 delete context;
677                         }
678                 }
679                 context = new GHOST_ContextWGL(
680                     m_wantStereoVisual,
681                     m_wantAlphaBackground,
682                     m_wantNumOfAASamples,
683                     m_hWnd,
684                     m_hDC,
685                     WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
686                     3, 3,
687                     (m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
688                     GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
689
690                 if (context->initializeDrawingContext()) {
691                         return context;
692                 }
693                 else {
694                         MessageBox(
695                                 m_hWnd,
696                                 "Blender requires a graphics driver with at least OpenGL 3.3 support.\n\n"
697                                 "The program will now close.",
698                                 "Blender - Unsupported Graphics Driver",
699                                 MB_OK | MB_ICONERROR);
700                         delete context;
701                         exit(0);
702                 }
703
704 #elif defined(WITH_GL_PROFILE_COMPAT)
705                 // ask for 2.1 context, driver gives any GL version >= 2.1 (hopefully the latest compatibility profile)
706                 // 2.1 ignores the profile bit & is incompatible with core profile
707                 context = new GHOST_ContextWGL(
708                         m_wantStereoVisual,
709                         m_wantAlphaBackground,
710                         m_wantNumOfAASamples,
711                         m_hWnd,
712                         m_hDC,
713                         0, // no profile bit
714                         2, 1,
715                         (m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
716                         GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
717
718                 if (context->initializeDrawingContext()) {
719                         return context;
720                 }
721                 else {
722                         delete context;
723                 }
724 #else
725 #  error // must specify either core or compat at build time
726 #endif
727         }
728
729         return NULL;
730 }
731
732 void GHOST_WindowWin32::lostMouseCapture()
733 {
734         if (m_hasMouseCaptured) {
735                 m_hasGrabMouse = false;
736                 m_nPressedButtons = 0;
737                 m_hasMouseCaptured = false;
738         }
739 }
740
741 void GHOST_WindowWin32::registerMouseClickEvent(int press)
742 {
743
744         switch (press) {
745                 case 0: m_nPressedButtons++;    break;
746                 case 1: if (m_nPressedButtons) m_nPressedButtons--; break;
747                 case 2: m_hasGrabMouse = true;    break;
748                 case 3: m_hasGrabMouse = false;   break;
749         }
750
751         if (!m_nPressedButtons && !m_hasGrabMouse && m_hasMouseCaptured) {
752                 ::ReleaseCapture();
753                 m_hasMouseCaptured = false;
754         }
755         else if ((m_nPressedButtons || m_hasGrabMouse) && !m_hasMouseCaptured) {
756                 ::SetCapture(m_hWnd);
757                 m_hasMouseCaptured = true;
758
759         }
760 }
761
762
763 void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
764 {
765         if (!visible) {
766                 while (::ShowCursor(FALSE) >= 0) ;
767         }
768         else {
769                 while (::ShowCursor(TRUE) < 0) ;
770         }
771
772         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
773                 ::SetCursor(m_customCursor);
774         }
775         else {
776                 // Convert GHOST cursor to Windows OEM cursor
777                 bool success = true;
778                 LPCSTR id;
779                 switch (cursor) {
780                         case GHOST_kStandardCursorDefault:              id = IDC_ARROW;     break;
781                         case GHOST_kStandardCursorRightArrow:           id = IDC_ARROW;     break;
782                         case GHOST_kStandardCursorLeftArrow:            id = IDC_ARROW;     break;
783                         case GHOST_kStandardCursorInfo:                 id = IDC_SIZEALL;   break;  // Four-pointed arrow pointing north, south, east, and west
784                         case GHOST_kStandardCursorDestroy:              id = IDC_NO;        break;  // Slashed circle
785                         case GHOST_kStandardCursorHelp:                 id = IDC_HELP;      break;  // Arrow and question mark
786                         case GHOST_kStandardCursorCycle:                id = IDC_NO;        break;  // Slashed circle
787                         case GHOST_kStandardCursorSpray:                id = IDC_SIZEALL;   break;  // Four-pointed arrow pointing north, south, east, and west
788                         case GHOST_kStandardCursorWait:                 id = IDC_WAIT;      break;  // Hourglass
789                         case GHOST_kStandardCursorText:                 id = IDC_IBEAM;     break;  // I-beam
790                         case GHOST_kStandardCursorCrosshair:            id = IDC_CROSS;     break;  // Crosshair
791                         case GHOST_kStandardCursorUpDown:               id = IDC_SIZENS;    break;  // Double-pointed arrow pointing north and south
792                         case GHOST_kStandardCursorLeftRight:            id = IDC_SIZEWE;    break;  // Double-pointed arrow pointing west and east
793                         case GHOST_kStandardCursorTopSide:              id = IDC_UPARROW;   break;  // Vertical arrow
794                         case GHOST_kStandardCursorBottomSide:           id = IDC_SIZENS;    break;
795                         case GHOST_kStandardCursorLeftSide:             id = IDC_SIZEWE;    break;
796                         case GHOST_kStandardCursorTopLeftCorner:        id = IDC_SIZENWSE;  break;
797                         case GHOST_kStandardCursorTopRightCorner:       id = IDC_SIZENESW;  break;
798                         case GHOST_kStandardCursorBottomRightCorner:    id = IDC_SIZENWSE;  break;
799                         case GHOST_kStandardCursorBottomLeftCorner:     id = IDC_SIZENESW;  break;
800                         case GHOST_kStandardCursorPencil:               id = IDC_ARROW;     break;
801                         case GHOST_kStandardCursorCopy:                 id = IDC_ARROW;     break;
802                         default:
803                                 success = false;
804                 }
805
806                 if (success) {
807                         ::SetCursor(::LoadCursor(0, id));
808                 }
809         }
810 }
811
812 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible)
813 {
814         if (::GetForegroundWindow() == m_hWnd) {
815                 loadCursor(visible, getCursorShape());
816         }
817
818         return GHOST_kSuccess;
819 }
820
821 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
822 {
823         if (mode != GHOST_kGrabDisable) {
824                 if (mode != GHOST_kGrabNormal) {
825                         m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
826                         setCursorGrabAccum(0, 0);
827
828                         if (mode == GHOST_kGrabHide)
829                                 setWindowCursorVisibility(false);
830                 }
831                 registerMouseClickEvent(2);
832         }
833         else {
834                 if (m_cursorGrab == GHOST_kGrabHide) {
835                         m_system->setCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
836                         setWindowCursorVisibility(true);
837                 }
838                 if (m_cursorGrab != GHOST_kGrabNormal) {
839                         /* use to generate a mouse move event, otherwise the last event
840                          * blender gets can be outside the screen causing menus not to show
841                          * properly unless the user moves the mouse */
842                         GHOST_TInt32 pos[2];
843                         m_system->getCursorPosition(pos[0], pos[1]);
844                         m_system->setCursorPosition(pos[0], pos[1]);
845                 }
846
847                 /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
848                 setCursorGrabAccum(0, 0);
849                 m_cursorGrabBounds.m_l = m_cursorGrabBounds.m_r = -1; /* disable */
850                 registerMouseClickEvent(3);
851         }
852
853         return GHOST_kSuccess;
854 }
855
856 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
857 {
858         if (m_customCursor) {
859                 DestroyCursor(m_customCursor);
860                 m_customCursor = NULL;
861         }
862
863         if (::GetForegroundWindow() == m_hWnd) {
864                 loadCursor(getCursorVisibility(), cursorShape);
865         }
866
867         return GHOST_kSuccess;
868 }
869
870 void GHOST_WindowWin32::processWin32TabletActivateEvent(WORD state)
871 {
872         if (!m_tablet) {
873                 return;
874         }
875
876         GHOST_WIN32_WTEnable fpWTEnable = (GHOST_WIN32_WTEnable) ::GetProcAddress(m_wintab, "WTEnable");
877         GHOST_WIN32_WTOverlap fpWTOverlap = (GHOST_WIN32_WTOverlap) ::GetProcAddress(m_wintab, "WTOverlap");
878
879         if (fpWTEnable) {
880                 fpWTEnable(m_tablet, state);
881                 if (fpWTOverlap && state) {
882                         fpWTOverlap(m_tablet, TRUE);
883                 }
884         }
885 }
886
887 void GHOST_WindowWin32::processWin32TabletInitEvent()
888 {
889         if (m_wintab && m_tabletData) {
890                 GHOST_WIN32_WTInfo fpWTInfo = (GHOST_WIN32_WTInfo) ::GetProcAddress(m_wintab, "WTInfoA");
891
892                 // let's see if we can initialize tablet here
893                 /* check if WinTab available. */
894                 if (fpWTInfo) {
895                         AXIS Pressure, Orientation[3]; /* The maximum tablet size */
896
897                         BOOL pressureSupport = fpWTInfo(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
898                         if (pressureSupport)
899                                 m_maxPressure = Pressure.axMax;
900                         else
901                                 m_maxPressure = 0;
902
903                         BOOL tiltSupport = fpWTInfo(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
904                         if (tiltSupport) {
905                                 /* does the tablet support azimuth ([0]) and altitude ([1]) */
906                                 if (Orientation[0].axResolution && Orientation[1].axResolution) {
907                                         m_maxAzimuth = Orientation[0].axMax;
908                                         m_maxAltitude = Orientation[1].axMax;
909                                 }
910                                 else {  /* no so dont do tilt stuff */
911                                         m_maxAzimuth = m_maxAltitude = 0;
912                                 }
913                         }
914
915                         m_tabletData->Active = GHOST_kTabletModeNone;
916                 }
917         }
918 }
919
920 void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam)
921 {
922         PACKET pkt;
923         if (m_wintab) {
924                 GHOST_WIN32_WTPacket fpWTPacket = (GHOST_WIN32_WTPacket) ::GetProcAddress(m_wintab, "WTPacket");
925                 if (fpWTPacket) {
926                         if (fpWTPacket((HCTX)lParam, wParam, &pkt)) {
927                                 if (m_tabletData) {
928                                         switch (pkt.pkCursor % 3) { /* % 3 for multiple devices ("DualTrack") */
929                                                 case 0:
930                                                         m_tabletData->Active = GHOST_kTabletModeNone; /* puck - not yet supported */
931                                                         break;
932                                                 case 1:
933                                                         m_tabletData->Active = GHOST_kTabletModeStylus; /* stylus */
934                                                         break;
935                                                 case 2:
936                                                         m_tabletData->Active = GHOST_kTabletModeEraser; /* eraser */
937                                                         break;
938                                         }
939                                         if (m_maxPressure > 0) {
940                                                 m_tabletData->Pressure = (float)pkt.pkNormalPressure / (float)m_maxPressure;
941                                         }
942                                         else {
943                                                 m_tabletData->Pressure = 1.0f;
944                                         }
945
946                                         if ((m_maxAzimuth > 0) && (m_maxAltitude > 0)) {
947                                                 ORIENTATION ort = pkt.pkOrientation;
948                                                 float vecLen;
949                                                 float altRad, azmRad;   /* in radians */
950
951                                                 /*
952                                                  * from the wintab spec:
953                                                  * orAzimuth    Specifies the clockwise rotation of the
954                                                  * cursor about the z axis through a full circular range.
955                                                  *
956                                                  * orAltitude   Specifies the angle with the x-y plane
957                                                  * through a signed, semicircular range.  Positive values
958                                                  * specify an angle upward toward the positive z axis;
959                                                  * negative values specify an angle downward toward the negative z axis.
960                                                  *
961                                                  * wintab.h defines .orAltitude as a UINT but documents .orAltitude
962                                                  * as positive for upward angles and negative for downward angles.
963                                                  * WACOM uses negative altitude values to show that the pen is inverted;
964                                                  * therefore we cast .orAltitude as an (int) and then use the absolute value.
965                                                  */
966
967                                                 /* convert raw fixed point data to radians */
968                                                 altRad = (float)((fabs((float)ort.orAltitude) / (float)m_maxAltitude) * M_PI / 2.0);
969                                                 azmRad = (float)(((float)ort.orAzimuth / (float)m_maxAzimuth) * M_PI * 2.0);
970
971                                                 /* find length of the stylus' projected vector on the XY plane */
972                                                 vecLen = cos(altRad);
973
974                                                 /* from there calculate X and Y components based on azimuth */
975                                                 m_tabletData->Xtilt = sin(azmRad) * vecLen;
976                                                 m_tabletData->Ytilt = (float)(sin(M_PI / 2.0 - azmRad) * vecLen);
977
978                                         }
979                                         else {
980                                                 m_tabletData->Xtilt = 0.0f;
981                                                 m_tabletData->Ytilt = 0.0f;
982                                         }
983                                 }
984                         }
985                 }
986         }
987 }
988
989 void GHOST_WindowWin32::bringTabletContextToFront()
990 {
991         if (m_wintab) {
992                 GHOST_WIN32_WTOverlap fpWTOverlap = (GHOST_WIN32_WTOverlap) ::GetProcAddress(m_wintab, "WTOverlap");
993                 if (fpWTOverlap) {
994                         fpWTOverlap(m_tablet, TRUE);
995                 }
996         }
997 }
998
999 GHOST_TUns16 GHOST_WindowWin32::getDPIHint()
1000 {
1001         if (m_user32) {
1002                 GHOST_WIN32_GetDpiForWindow fpGetDpiForWindow = (GHOST_WIN32_GetDpiForWindow) ::GetProcAddress(m_user32, "GetDpiForWindow");
1003
1004                 if (fpGetDpiForWindow) {
1005                         return fpGetDpiForWindow(this->m_hWnd);
1006                 }
1007         }
1008
1009         return USER_DEFAULT_SCREEN_DPI;
1010 }
1011
1012 /** Reverse the bits in a GHOST_TUns8 */
1013 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
1014 {
1015         ch = ((ch >> 1) & 0x55) | ((ch << 1) & 0xAA);
1016         ch = ((ch >> 2) & 0x33) | ((ch << 2) & 0xCC);
1017         ch = ((ch >> 4) & 0x0F) | ((ch << 4) & 0xF0);
1018         return ch;
1019 }
1020
1021 #if 0  /* UNUSED */
1022 /** Reverse the bits in a GHOST_TUns16 */
1023 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
1024 {
1025         shrt = ((shrt >> 1) & 0x5555) | ((shrt << 1) & 0xAAAA);
1026         shrt = ((shrt >> 2) & 0x3333) | ((shrt << 2) & 0xCCCC);
1027         shrt = ((shrt >> 4) & 0x0F0F) | ((shrt << 4) & 0xF0F0);
1028         shrt = ((shrt >> 8) & 0x00FF) | ((shrt << 8) & 0xFF00);
1029         return shrt;
1030 }
1031 #endif
1032 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(
1033         GHOST_TUns8 bitmap[16][2],
1034         GHOST_TUns8 mask[16][2],
1035         int hotX, int hotY)
1036 {
1037         return setWindowCustomCursorShape((GHOST_TUns8 *)bitmap, (GHOST_TUns8 *)mask,
1038                                           16, 16, hotX, hotY, 0, 1);
1039 }
1040
1041 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(
1042         GHOST_TUns8 *bitmap,
1043         GHOST_TUns8 *mask, int sizeX, int sizeY, int hotX, int hotY,
1044         int fg_color, int bg_color)
1045 {
1046         GHOST_TUns32 andData[32];
1047         GHOST_TUns32 xorData[32];
1048         GHOST_TUns32 fullBitRow, fullMaskRow;
1049         int x, y, cols;
1050
1051         cols = sizeX / 8; /* Num of whole bytes per row (width of bm/mask) */
1052         if (sizeX % 8) cols++;
1053
1054         if (m_customCursor) {
1055                 DestroyCursor(m_customCursor);
1056                 m_customCursor = NULL;
1057         }
1058
1059         memset(&andData, 0xFF, sizeof(andData));
1060         memset(&xorData, 0, sizeof(xorData));
1061
1062         for (y = 0; y < sizeY; y++) {
1063                 fullBitRow = 0;
1064                 fullMaskRow = 0;
1065                 for (x = cols - 1; x >= 0; x--) {
1066                         fullBitRow <<= 8;
1067                         fullMaskRow <<= 8;
1068                         fullBitRow  |= uns8ReverseBits(bitmap[cols * y + x]);
1069                         fullMaskRow |= uns8ReverseBits(mask[cols * y + x]);
1070                 }
1071                 xorData[y] = fullBitRow & fullMaskRow;
1072                 andData[y] = ~fullMaskRow;
1073         }
1074
1075         m_customCursor = ::CreateCursor(::GetModuleHandle(0), hotX, hotY, 32, 32, andData, xorData);
1076         if (!m_customCursor) {
1077                 return GHOST_kFailure;
1078         }
1079
1080         if (::GetForegroundWindow() == m_hWnd) {
1081                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
1082         }
1083
1084         return GHOST_kSuccess;
1085 }
1086
1087
1088 GHOST_TSuccess GHOST_WindowWin32::setProgressBar(float progress)
1089 {
1090         /*SetProgressValue sets state to TBPF_NORMAL automaticly*/
1091         if (m_Bar && S_OK == m_Bar->SetProgressValue(m_hWnd, 10000 * progress, 10000))
1092                 return GHOST_kSuccess;
1093
1094         return GHOST_kFailure;
1095 }
1096
1097 GHOST_TSuccess GHOST_WindowWin32::endProgressBar()
1098 {
1099         if (m_Bar && S_OK == m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS))
1100                 return GHOST_kSuccess;
1101
1102         return GHOST_kFailure;
1103 }
1104
1105
1106 #ifdef WITH_INPUT_IME
1107 void GHOST_WindowWin32::beginIME(GHOST_TInt32 x, GHOST_TInt32 y, GHOST_TInt32 w, GHOST_TInt32 h, int completed)
1108 {
1109         m_imeInput.BeginIME(m_hWnd, GHOST_Rect(x, y - h, x, y), (bool)completed);
1110 }
1111
1112
1113 void GHOST_WindowWin32::endIME()
1114 {
1115         m_imeInput.EndIME(m_hWnd);
1116 }
1117 #endif /* WITH_INPUT_IME */