6e89299e1ca1156a4a7363c13013e8d66764f218
[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         if (m_hWnd) {
194                 // Register this window as a droptarget. Requires m_hWnd to be valid.
195                 // Note that OleInitialize(0) has to be called prior to this. Done in GHOST_SystemWin32.
196                 m_dropTarget = new GHOST_DropTargetWin32(this, m_system);
197                 if (m_dropTarget) {
198                         ::RegisterDragDrop(m_hWnd, m_dropTarget);
199                 }
200
201                 // Store a pointer to this class in the window structure
202                 ::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR) this);
203
204                 // Store the device context
205                 m_hDC = ::GetDC(m_hWnd);
206
207                 GHOST_TSuccess success = setDrawingContextType(type);
208
209                 if (success) {
210                         // Show the window
211                         int nCmdShow;
212                         switch (state) {
213                                 case GHOST_kWindowStateMaximized:
214                                         nCmdShow = SW_SHOWMAXIMIZED;
215                                         break;
216                                 case GHOST_kWindowStateMinimized:
217                                         nCmdShow = SW_SHOWMINIMIZED;
218                                         break;
219                                 case GHOST_kWindowStateNormal:
220                                 default:
221                                         nCmdShow = SW_SHOWNORMAL;
222                                         break;
223                         }
224
225                         ::ShowWindow(m_hWnd, nCmdShow);
226 #ifdef WIN32_COMPOSITING
227                         if (alphaBackground && parentwindowhwnd == 0) {
228
229                                 HRESULT hr = S_OK;
230
231                                 // Create and populate the Blur Behind structure
232                                 DWM_BLURBEHIND bb = { 0 };
233
234                                 // Enable Blur Behind and apply to the entire client area
235                                 bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
236                                 bb.fEnable = true;
237                                 bb.hRgnBlur = CreateRectRgn(0, 0, -1, -1);
238
239                                 // Apply Blur Behind
240                                 hr = DwmEnableBlurBehindWindow(m_hWnd, &bb);
241                                 DeleteObject(bb.hRgnBlur);
242                         }
243 #endif
244                         // Force an initial paint of the window
245                         ::UpdateWindow(m_hWnd);
246                 }
247                 else {
248                         //invalidate the window
249                         ::DestroyWindow(m_hWnd);
250                         m_hWnd = NULL;
251                 }
252         }
253
254         if (parentwindowhwnd != 0) {
255                 RAWINPUTDEVICE device = {0};
256                 device.usUsagePage  = 0x01; /* usUsagePage & usUsage for keyboard*/
257                 device.usUsage      = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */
258                 device.dwFlags |= RIDEV_INPUTSINK; // makes WM_INPUT is visible for ghost when has parent window
259                 device.hwndTarget = m_hWnd;
260                 RegisterRawInputDevices(&device, 1, sizeof(device));
261         }
262
263         m_wintab = ::LoadLibrary("Wintab32.dll");
264         if (m_wintab) {
265                 GHOST_WIN32_WTInfo fpWTInfo = (GHOST_WIN32_WTInfo) ::GetProcAddress(m_wintab, "WTInfoA");
266                 GHOST_WIN32_WTOpen fpWTOpen = (GHOST_WIN32_WTOpen) ::GetProcAddress(m_wintab, "WTOpenA");
267
268                 // let's see if we can initialize tablet here
269                 /* check if WinTab available. */
270                 if (fpWTInfo && fpWTInfo(0, 0, NULL)) {
271                         // Now init the tablet
272                         LOGCONTEXT lc;
273                         /* The maximum tablet size, pressure and orientation (tilt) */
274                         AXIS TabletX, TabletY, Pressure, Orientation[3];
275
276                         // Open a Wintab context
277
278                         // Get default context information
279                         fpWTInfo(WTI_DEFCONTEXT, 0, &lc);
280
281                         // Open the context
282                         lc.lcPktData = PACKETDATA;
283                         lc.lcPktMode = PACKETMODE;
284                         lc.lcOptions |= CXO_MESSAGES | CXO_SYSTEM;
285
286                         /* Set the entire tablet as active */
287                         fpWTInfo(WTI_DEVICES, DVC_X, &TabletX);
288                         fpWTInfo(WTI_DEVICES, DVC_Y, &TabletY);
289
290                         /* get the max pressure, to divide into a float */
291                         BOOL pressureSupport = fpWTInfo(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
292                         if (pressureSupport)
293                                 m_maxPressure = Pressure.axMax;
294                         else
295                                 m_maxPressure = 0;
296
297                         /* get the max tilt axes, to divide into floats */
298                         BOOL tiltSupport = fpWTInfo(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
299                         if (tiltSupport) {
300                                 /* does the tablet support azimuth ([0]) and altitude ([1]) */
301                                 if (Orientation[0].axResolution && Orientation[1].axResolution) {
302                                         /* all this assumes the minimum is 0 */
303                                         m_maxAzimuth = Orientation[0].axMax;
304                                         m_maxAltitude = Orientation[1].axMax;
305                                 }
306                                 else {  /* no so dont do tilt stuff */
307                                         m_maxAzimuth = m_maxAltitude = 0;
308                                 }
309                         }
310
311                         if (fpWTOpen) {
312                                 m_tablet = fpWTOpen(m_hWnd, &lc, TRUE);
313                                 if (m_tablet) {
314                                         m_tabletData = new GHOST_TabletData();
315                                         m_tabletData->Active = GHOST_kTabletModeNone;
316                                 }
317                         }
318                 }
319         }
320         CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar);
321 }
322
323
324 GHOST_WindowWin32::~GHOST_WindowWin32()
325 {
326         if (m_Bar) {
327                 m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
328                 m_Bar->Release();
329         }
330
331         if (m_wintab) {
332                 GHOST_WIN32_WTClose fpWTClose = (GHOST_WIN32_WTClose) ::GetProcAddress(m_wintab, "WTClose");
333                 if (fpWTClose) {
334                         if (m_tablet)
335                                 fpWTClose(m_tablet);
336                         delete m_tabletData;
337                         m_tabletData = NULL;
338                 }
339         }
340
341         if (m_customCursor) {
342                 DestroyCursor(m_customCursor);
343                 m_customCursor = NULL;
344         }
345
346         if (m_hWnd != NULL && m_hDC != NULL && releaseNativeHandles()) {
347                 ::ReleaseDC(m_hWnd, m_hDC);
348         }
349
350         if (m_hWnd) {
351                 if (m_dropTarget) {
352                         // Disable DragDrop
353                         RevokeDragDrop(m_hWnd);
354                         // Release our reference of the DropTarget and it will delete itself eventually.
355                         m_dropTarget->Release();
356                 }
357                 ::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, NULL);
358                 ::DestroyWindow(m_hWnd);
359                 m_hWnd = 0;
360         }
361 }
362
363 bool GHOST_WindowWin32::getValid() const
364 {
365         return GHOST_Window::getValid() && m_hWnd != 0 && m_hDC != 0;
366 }
367
368 HWND GHOST_WindowWin32::getHWND() const
369 {
370         return m_hWnd;
371 }
372
373 void GHOST_WindowWin32::setTitle(const STR_String &title)
374 {
375         wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0);
376         ::SetWindowTextW(m_hWnd, (wchar_t *)title_16);
377         free(title_16);
378 }
379
380
381 void GHOST_WindowWin32::getTitle(STR_String &title) const
382 {
383         char buf[s_maxTitleLength]; /*CHANGE + never used yet*/
384         ::GetWindowText(m_hWnd, buf, s_maxTitleLength);
385         STR_String temp(buf);
386         title = buf;
387 }
388
389
390 void GHOST_WindowWin32::getWindowBounds(GHOST_Rect &bounds) const
391 {
392         RECT rect;
393         ::GetWindowRect(m_hWnd, &rect);
394         bounds.m_b = rect.bottom;
395         bounds.m_l = rect.left;
396         bounds.m_r = rect.right;
397         bounds.m_t = rect.top;
398 }
399
400
401 void GHOST_WindowWin32::getClientBounds(GHOST_Rect &bounds) const
402 {
403         RECT rect;
404         POINT coord;
405         if (!IsIconic(m_hWnd)) {
406                 ::GetClientRect(m_hWnd, &rect);
407
408                 coord.x = rect.left;
409                 coord.y = rect.top;
410                 ::ClientToScreen(m_hWnd, &coord);
411
412                 bounds.m_l = coord.x;
413                 bounds.m_t = coord.y;
414
415                 coord.x = rect.right;
416                 coord.y = rect.bottom;
417                 ::ClientToScreen(m_hWnd, &coord);
418
419                 bounds.m_r = coord.x;
420                 bounds.m_b = coord.y;
421         }
422         else {
423                 bounds.m_b = 0;
424                 bounds.m_l = 0;
425                 bounds.m_r = 0;
426                 bounds.m_t = 0;
427         }
428 }
429
430
431 GHOST_TSuccess GHOST_WindowWin32::setClientWidth(GHOST_TUns32 width)
432 {
433         GHOST_TSuccess success;
434         GHOST_Rect cBnds, wBnds;
435         getClientBounds(cBnds);
436         if (cBnds.getWidth() != (GHOST_TInt32)width) {
437                 getWindowBounds(wBnds);
438                 int cx = wBnds.getWidth() + width - cBnds.getWidth();
439                 int cy = wBnds.getHeight();
440                 success =  ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
441                           GHOST_kSuccess : GHOST_kFailure;
442         }
443         else {
444                 success = GHOST_kSuccess;
445         }
446         return success;
447 }
448
449
450 GHOST_TSuccess GHOST_WindowWin32::setClientHeight(GHOST_TUns32 height)
451 {
452         GHOST_TSuccess success;
453         GHOST_Rect cBnds, wBnds;
454         getClientBounds(cBnds);
455         if (cBnds.getHeight() != (GHOST_TInt32)height) {
456                 getWindowBounds(wBnds);
457                 int cx = wBnds.getWidth();
458                 int cy = wBnds.getHeight() + height - cBnds.getHeight();
459                 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
460                           GHOST_kSuccess : GHOST_kFailure;
461         }
462         else {
463                 success = GHOST_kSuccess;
464         }
465         return success;
466 }
467
468
469 GHOST_TSuccess GHOST_WindowWin32::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
470 {
471         GHOST_TSuccess success;
472         GHOST_Rect cBnds, wBnds;
473         getClientBounds(cBnds);
474         if ((cBnds.getWidth() != (GHOST_TInt32)width) || (cBnds.getHeight() != (GHOST_TInt32)height)) {
475                 getWindowBounds(wBnds);
476                 int cx = wBnds.getWidth() + width - cBnds.getWidth();
477                 int cy = wBnds.getHeight() + height - cBnds.getHeight();
478                 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
479                           GHOST_kSuccess : GHOST_kFailure;
480         }
481         else {
482                 success = GHOST_kSuccess;
483         }
484         return success;
485 }
486
487
488 GHOST_TWindowState GHOST_WindowWin32::getState() const
489 {
490         GHOST_TWindowState state;
491
492         // XXX 27.04.2011
493         // we need to find a way to combine parented windows + resizing if we simply set the
494         // state as GHOST_kWindowStateEmbedded we will need to check for them somewhere else.
495         // It's also strange that in Windows is the only platform we need to make this separation.
496         if (m_parentWindowHwnd != 0) {
497                 state = GHOST_kWindowStateEmbedded;
498                 return state;
499         }
500
501         if (::IsIconic(m_hWnd)) {
502                 state = GHOST_kWindowStateMinimized;
503         }
504         else if (::IsZoomed(m_hWnd)) {
505                 LONG_PTR result = ::GetWindowLongPtr(m_hWnd, GWL_STYLE);
506                 if ((result & (WS_POPUP | WS_MAXIMIZE)) != (WS_POPUP | WS_MAXIMIZE))
507                         state = GHOST_kWindowStateMaximized;
508                 else
509                         state = GHOST_kWindowStateFullScreen;
510         }
511         else {
512                 state = GHOST_kWindowStateNormal;
513         }
514         return state;
515 }
516
517
518 void GHOST_WindowWin32::screenToClient(
519         GHOST_TInt32 inX, GHOST_TInt32 inY,
520         GHOST_TInt32 &outX, GHOST_TInt32 &outY) const
521 {
522         POINT point = {inX, inY};
523         ::ScreenToClient(m_hWnd, &point);
524         outX = point.x;
525         outY = point.y;
526 }
527
528
529 void GHOST_WindowWin32::clientToScreen(
530         GHOST_TInt32 inX, GHOST_TInt32 inY,
531         GHOST_TInt32 &outX, GHOST_TInt32 &outY) const
532 {
533         POINT point = {inX, inY};
534         ::ClientToScreen(m_hWnd, &point);
535         outX = point.x;
536         outY = point.y;
537 }
538
539
540 GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
541 {
542         GHOST_TWindowState curstate = getState();
543         WINDOWPLACEMENT wp;
544         wp.length = sizeof(WINDOWPLACEMENT);
545         ::GetWindowPlacement(m_hWnd, &wp);
546
547         if (state == GHOST_kWindowStateNormal)
548                 state = m_normal_state;
549
550         switch (state) {
551                 case GHOST_kWindowStateMinimized:
552                         wp.showCmd = SW_SHOWMINIMIZED;
553                         break;
554                 case GHOST_kWindowStateMaximized:
555                         wp.showCmd = SW_SHOWMAXIMIZED;
556                         ::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
557                         break;
558                 case GHOST_kWindowStateFullScreen:
559                         if (curstate != state && curstate != GHOST_kWindowStateMinimized)
560                                 m_normal_state = curstate;
561                         wp.showCmd = SW_SHOWMAXIMIZED;
562                         wp.ptMaxPosition.x = 0;
563                         wp.ptMaxPosition.y = 0;
564                         ::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_POPUP | WS_MAXIMIZE);
565                         break;
566                 case GHOST_kWindowStateEmbedded:
567                         ::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_CHILD);
568                         break;
569                 case GHOST_kWindowStateNormal:
570                 default:
571                         wp.showCmd = SW_SHOWNORMAL;
572                         ::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
573                         break;
574         }
575         /* Clears window cache for SetWindowLongPtr */
576         ::SetWindowPos(m_hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
577
578         return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
579 }
580
581
582 GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order)
583 {
584         HWND hWndInsertAfter, hWndToRaise;
585
586         if (order == GHOST_kWindowOrderBottom) {
587                 hWndInsertAfter = HWND_BOTTOM;
588                 hWndToRaise = ::GetWindow(m_hWnd, GW_HWNDNEXT); /* the window to raise */
589         }
590         else {
591                 hWndInsertAfter = HWND_TOP;
592                 hWndToRaise = NULL;
593         }
594
595         if (::SetWindowPos(m_hWnd, hWndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == FALSE) {
596                 return GHOST_kFailure;
597         }
598
599         if (hWndToRaise && ::SetWindowPos(hWndToRaise, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == FALSE) {
600                 return GHOST_kFailure;
601         }
602         return GHOST_kSuccess;
603 }
604
605
606 GHOST_TSuccess GHOST_WindowWin32::invalidate()
607 {
608         GHOST_TSuccess success;
609         if (m_hWnd) {
610                 success = ::InvalidateRect(m_hWnd, 0, FALSE) != 0 ? GHOST_kSuccess : GHOST_kFailure;
611         }
612         else {
613                 success = GHOST_kFailure;
614         }
615         return success;
616 }
617
618
619 GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType type)
620 {
621         if (type == GHOST_kDrawingContextTypeOpenGL) {
622 #if !defined(WITH_GL_EGL)
623
624 #if defined(WITH_GL_PROFILE_CORE)
625                 GHOST_Context *context = new GHOST_ContextWGL(
626                         m_wantStereoVisual,
627                         m_wantAlphaBackground,
628                         m_wantNumOfAASamples,
629                         m_hWnd,
630                         m_hDC,
631                         WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
632                         3, 2,
633                         GHOST_OPENGL_WGL_CONTEXT_FLAGS,
634                         GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
635 #elif defined(WITH_GL_PROFILE_ES20)
636                 GHOST_Context *context = new GHOST_ContextWGL(
637                         m_wantStereoVisual,
638                         m_wantAlphaBackground,
639                         m_wantNumOfAASamples,
640                         m_hWnd,
641                         m_hDC,
642                         WGL_CONTEXT_ES2_PROFILE_BIT_EXT,
643                         2, 0,
644                         GHOST_OPENGL_WGL_CONTEXT_FLAGS,
645                         GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
646 #elif defined(WITH_GL_PROFILE_COMPAT)
647                 GHOST_Context *context = new GHOST_ContextWGL(
648                         m_wantStereoVisual,
649                         m_wantAlphaBackground,
650                         m_wantNumOfAASamples,
651                         m_hWnd,
652                         m_hDC,
653 #if 1
654                         0, // profile bit
655                         2, 1, // GL version requested
656 #else
657                         // switch to this for Blender 2.8 development
658                         WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
659                         3, 2,
660 #endif
661                         (m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
662                         GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
663 #else
664 #  error
665 #endif
666
667 #else
668
669 #if defined(WITH_GL_PROFILE_CORE)
670                 GHOST_Context *context = new GHOST_ContextEGL(
671                         m_wantStereoVisual,
672                         m_wantNumOfAASamples,
673                         m_hWnd,
674                         m_hDC,
675                         EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
676                         3, 2,
677                         GHOST_OPENGL_EGL_CONTEXT_FLAGS,
678                         GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
679                         EGL_OPENGL_API);
680 #elif defined(WITH_GL_PROFILE_ES20)
681                 GHOST_Context *context = new GHOST_ContextEGL(
682                         m_wantStereoVisual,
683                         m_wantNumOfAASamples,
684                         m_hWnd,
685                         m_hDC,
686                         0, // profile bit
687                         2, 0,
688                         GHOST_OPENGL_EGL_CONTEXT_FLAGS,
689                         GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
690                         EGL_OPENGL_ES_API);
691 #elif defined(WITH_GL_PROFILE_COMPAT)
692                 GHOST_Context *context = new GHOST_ContextEGL(
693                         m_wantStereoVisual,
694                         m_wantNumOfAASamples,
695                         m_hWnd,
696                         m_hDC,
697 #if 1
698                         0, // profile bit
699                         2, 1, // GL version requested
700 #else
701                         // switch to this for Blender 2.8 development
702                         EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT,
703                         3, 2,
704 #endif
705                         GHOST_OPENGL_EGL_CONTEXT_FLAGS,
706                         GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
707                         EGL_OPENGL_API);
708 #else
709 #  error
710 #endif
711
712 #endif
713                 if (context->initializeDrawingContext())
714                         return context;
715                 else
716                         delete context;
717         }
718
719         return NULL;
720 }
721
722 void GHOST_WindowWin32::lostMouseCapture()
723 {
724         if (m_hasMouseCaptured) {
725                 m_hasGrabMouse = false;
726                 m_nPressedButtons = 0;
727                 m_hasMouseCaptured = false;
728         }
729 }
730
731 void GHOST_WindowWin32::registerMouseClickEvent(int press)
732 {
733
734         switch (press) {
735                 case 0: m_nPressedButtons++;    break;
736                 case 1: if (m_nPressedButtons) m_nPressedButtons--; break;
737                 case 2: m_hasGrabMouse = true;    break;
738                 case 3: m_hasGrabMouse = false;   break;
739         }
740
741         if (!m_nPressedButtons && !m_hasGrabMouse && m_hasMouseCaptured) {
742                 ::ReleaseCapture();
743                 m_hasMouseCaptured = false;
744         }
745         else if ((m_nPressedButtons || m_hasGrabMouse) && !m_hasMouseCaptured) {
746                 ::SetCapture(m_hWnd);
747                 m_hasMouseCaptured = true;
748
749         }
750 }
751
752
753 void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
754 {
755         if (!visible) {
756                 while (::ShowCursor(FALSE) >= 0) ;
757         }
758         else {
759                 while (::ShowCursor(TRUE) < 0) ;
760         }
761
762         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
763                 ::SetCursor(m_customCursor);
764         }
765         else {
766                 // Convert GHOST cursor to Windows OEM cursor
767                 bool success = true;
768                 LPCSTR id;
769                 switch (cursor) {
770                         case GHOST_kStandardCursorDefault:              id = IDC_ARROW;     break;
771                         case GHOST_kStandardCursorRightArrow:           id = IDC_ARROW;     break;
772                         case GHOST_kStandardCursorLeftArrow:            id = IDC_ARROW;     break;
773                         case GHOST_kStandardCursorInfo:                 id = IDC_SIZEALL;   break;  // Four-pointed arrow pointing north, south, east, and west
774                         case GHOST_kStandardCursorDestroy:              id = IDC_NO;        break;  // Slashed circle
775                         case GHOST_kStandardCursorHelp:                 id = IDC_HELP;      break;  // Arrow and question mark
776                         case GHOST_kStandardCursorCycle:                id = IDC_NO;        break;  // Slashed circle
777                         case GHOST_kStandardCursorSpray:                id = IDC_SIZEALL;   break;  // Four-pointed arrow pointing north, south, east, and west
778                         case GHOST_kStandardCursorWait:                 id = IDC_WAIT;      break;  // Hourglass
779                         case GHOST_kStandardCursorText:                 id = IDC_IBEAM;     break;  // I-beam
780                         case GHOST_kStandardCursorCrosshair:            id = IDC_CROSS;     break;  // Crosshair
781                         case GHOST_kStandardCursorUpDown:               id = IDC_SIZENS;    break;  // Double-pointed arrow pointing north and south
782                         case GHOST_kStandardCursorLeftRight:            id = IDC_SIZEWE;    break;  // Double-pointed arrow pointing west and east
783                         case GHOST_kStandardCursorTopSide:              id = IDC_UPARROW;   break;  // Vertical arrow
784                         case GHOST_kStandardCursorBottomSide:           id = IDC_SIZENS;    break;
785                         case GHOST_kStandardCursorLeftSide:             id = IDC_SIZEWE;    break;
786                         case GHOST_kStandardCursorTopLeftCorner:        id = IDC_SIZENWSE;  break;
787                         case GHOST_kStandardCursorTopRightCorner:       id = IDC_SIZENESW;  break;
788                         case GHOST_kStandardCursorBottomRightCorner:    id = IDC_SIZENWSE;  break;
789                         case GHOST_kStandardCursorBottomLeftCorner:     id = IDC_SIZENESW;  break;
790                         case GHOST_kStandardCursorPencil:               id = IDC_ARROW;     break;
791                         case GHOST_kStandardCursorCopy:                 id = IDC_ARROW;     break;
792                         default:
793                                 success = false;
794                 }
795
796                 if (success) {
797                         ::SetCursor(::LoadCursor(0, id));
798                 }
799         }
800 }
801
802 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible)
803 {
804         if (::GetForegroundWindow() == m_hWnd) {
805                 loadCursor(visible, getCursorShape());
806         }
807
808         return GHOST_kSuccess;
809 }
810
811 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
812 {
813         if (mode != GHOST_kGrabDisable) {
814                 if (mode != GHOST_kGrabNormal) {
815                         m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
816                         setCursorGrabAccum(0, 0);
817
818                         if (mode == GHOST_kGrabHide)
819                                 setWindowCursorVisibility(false);
820                 }
821                 registerMouseClickEvent(2);
822         }
823         else {
824                 if (m_cursorGrab == GHOST_kGrabHide) {
825                         m_system->setCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
826                         setWindowCursorVisibility(true);
827                 }
828                 if (m_cursorGrab != GHOST_kGrabNormal) {
829                         /* use to generate a mouse move event, otherwise the last event
830                          * blender gets can be outside the screen causing menus not to show
831                          * properly unless the user moves the mouse */
832                         GHOST_TInt32 pos[2];
833                         m_system->getCursorPosition(pos[0], pos[1]);
834                         m_system->setCursorPosition(pos[0], pos[1]);
835                 }
836
837                 /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
838                 setCursorGrabAccum(0, 0);
839                 m_cursorGrabBounds.m_l = m_cursorGrabBounds.m_r = -1; /* disable */
840                 registerMouseClickEvent(3);
841         }
842
843         return GHOST_kSuccess;
844 }
845
846 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
847 {
848         if (m_customCursor) {
849                 DestroyCursor(m_customCursor);
850                 m_customCursor = NULL;
851         }
852
853         if (::GetForegroundWindow() == m_hWnd) {
854                 loadCursor(getCursorVisibility(), cursorShape);
855         }
856
857         return GHOST_kSuccess;
858 }
859
860 void GHOST_WindowWin32::processWin32TabletInitEvent()
861 {
862         if (m_wintab && m_tabletData) {
863                 GHOST_WIN32_WTInfo fpWTInfo = (GHOST_WIN32_WTInfo) ::GetProcAddress(m_wintab, "WTInfoA");
864
865                 // let's see if we can initialize tablet here
866                 /* check if WinTab available. */
867                 if (fpWTInfo) {
868                         AXIS Pressure, Orientation[3]; /* The maximum tablet size */
869
870                         BOOL pressureSupport = fpWTInfo(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
871                         if (pressureSupport)
872                                 m_maxPressure = Pressure.axMax;
873                         else
874                                 m_maxPressure = 0;
875
876                         BOOL tiltSupport = fpWTInfo(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
877                         if (tiltSupport) {
878                                 /* does the tablet support azimuth ([0]) and altitude ([1]) */
879                                 if (Orientation[0].axResolution && Orientation[1].axResolution) {
880                                         m_maxAzimuth = Orientation[0].axMax;
881                                         m_maxAltitude = Orientation[1].axMax;
882                                 }
883                                 else {  /* no so dont do tilt stuff */
884                                         m_maxAzimuth = m_maxAltitude = 0;
885                                 }
886                         }
887
888                         m_tabletData->Active = GHOST_kTabletModeNone;
889                 }
890         }
891 }
892
893 void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam)
894 {
895         PACKET pkt;
896         if (m_wintab) {
897                 GHOST_WIN32_WTPacket fpWTPacket = (GHOST_WIN32_WTPacket) ::GetProcAddress(m_wintab, "WTPacket");
898                 if (fpWTPacket) {
899                         if (fpWTPacket((HCTX)lParam, wParam, &pkt)) {
900                                 if (m_tabletData) {
901                                         switch (pkt.pkCursor % 3) { /* % 3 for multiple devices ("DualTrack") */
902                                                 case 0:
903                                                         m_tabletData->Active = GHOST_kTabletModeNone; /* puck - not yet supported */
904                                                         break;
905                                                 case 1:
906                                                         m_tabletData->Active = GHOST_kTabletModeStylus; /* stylus */
907                                                         break;
908                                                 case 2:
909                                                         m_tabletData->Active = GHOST_kTabletModeEraser; /* eraser */
910                                                         break;
911                                         }
912                                         if (m_maxPressure > 0) {
913                                                 m_tabletData->Pressure = (float)pkt.pkNormalPressure / (float)m_maxPressure;
914                                         }
915                                         else {
916                                                 m_tabletData->Pressure = 1.0f;
917                                         }
918
919                                         if ((m_maxAzimuth > 0) && (m_maxAltitude > 0)) {
920                                                 ORIENTATION ort = pkt.pkOrientation;
921                                                 float vecLen;
922                                                 float altRad, azmRad;   /* in radians */
923
924                                                 /*
925                                                  * from the wintab spec:
926                                                  * orAzimuth    Specifies the clockwise rotation of the
927                                                  * cursor about the z axis through a full circular range.
928                                                  *
929                                                  * orAltitude   Specifies the angle with the x-y plane
930                                                  * through a signed, semicircular range.  Positive values
931                                                  * specify an angle upward toward the positive z axis;
932                                                  * negative values specify an angle downward toward the negative z axis.
933                                                  *
934                                                  * wintab.h defines .orAltitude as a UINT but documents .orAltitude
935                                                  * as positive for upward angles and negative for downward angles.
936                                                  * WACOM uses negative altitude values to show that the pen is inverted;
937                                                  * therefore we cast .orAltitude as an (int) and then use the absolute value.
938                                                  */
939
940                                                 /* convert raw fixed point data to radians */
941                                                 altRad = (float)((fabs((float)ort.orAltitude) / (float)m_maxAltitude) * M_PI / 2.0);
942                                                 azmRad = (float)(((float)ort.orAzimuth / (float)m_maxAzimuth) * M_PI * 2.0);
943
944                                                 /* find length of the stylus' projected vector on the XY plane */
945                                                 vecLen = cos(altRad);
946
947                                                 /* from there calculate X and Y components based on azimuth */
948                                                 m_tabletData->Xtilt = sin(azmRad) * vecLen;
949                                                 m_tabletData->Ytilt = (float)(sin(M_PI / 2.0 - azmRad) * vecLen);
950
951                                         }
952                                         else {
953                                                 m_tabletData->Xtilt = 0.0f;
954                                                 m_tabletData->Ytilt = 0.0f;
955                                         }
956                                 }
957                         }
958                 }
959         }
960 }
961
962 void GHOST_WindowWin32::bringTabletContextToFront()
963 {
964         if (m_wintab) {
965                 GHOST_WIN32_WTOverlap fpWTOverlap = (GHOST_WIN32_WTOverlap) ::GetProcAddress(m_wintab, "WTOverlap");
966                 if (fpWTOverlap) {
967                         fpWTOverlap(m_tablet, TRUE);
968                 }
969         }
970 }
971
972 GHOST_TUns16 GHOST_WindowWin32::getDPIHint()
973 {
974         if (!m_user32) {
975                 m_user32 = ::LoadLibrary("user32.dll");
976         }
977
978         if (m_user32) {
979                 GHOST_WIN32_GetDpiForWindow fpGetDpiForWindow = (GHOST_WIN32_GetDpiForWindow) ::GetProcAddress(m_user32, "GetDpiForWindow");
980
981                 if (fpGetDpiForWindow) {
982                         return fpGetDpiForWindow(this->m_hWnd);
983                 }
984         }
985
986         return USER_DEFAULT_SCREEN_DPI;
987 }
988
989 /** Reverse the bits in a GHOST_TUns8 */
990 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
991 {
992         ch = ((ch >> 1) & 0x55) | ((ch << 1) & 0xAA);
993         ch = ((ch >> 2) & 0x33) | ((ch << 2) & 0xCC);
994         ch = ((ch >> 4) & 0x0F) | ((ch << 4) & 0xF0);
995         return ch;
996 }
997
998 #if 0  /* UNUSED */
999 /** Reverse the bits in a GHOST_TUns16 */
1000 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
1001 {
1002         shrt = ((shrt >> 1) & 0x5555) | ((shrt << 1) & 0xAAAA);
1003         shrt = ((shrt >> 2) & 0x3333) | ((shrt << 2) & 0xCCCC);
1004         shrt = ((shrt >> 4) & 0x0F0F) | ((shrt << 4) & 0xF0F0);
1005         shrt = ((shrt >> 8) & 0x00FF) | ((shrt << 8) & 0xFF00);
1006         return shrt;
1007 }
1008 #endif
1009 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(
1010         GHOST_TUns8 bitmap[16][2],
1011         GHOST_TUns8 mask[16][2],
1012         int hotX, int hotY)
1013 {
1014         return setWindowCustomCursorShape((GHOST_TUns8 *)bitmap, (GHOST_TUns8 *)mask,
1015                                           16, 16, hotX, hotY, 0, 1);
1016 }
1017
1018 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(
1019         GHOST_TUns8 *bitmap,
1020         GHOST_TUns8 *mask, int sizeX, int sizeY, int hotX, int hotY,
1021         int fg_color, int bg_color)
1022 {
1023         GHOST_TUns32 andData[32];
1024         GHOST_TUns32 xorData[32];
1025         GHOST_TUns32 fullBitRow, fullMaskRow;
1026         int x, y, cols;
1027
1028         cols = sizeX / 8; /* Num of whole bytes per row (width of bm/mask) */
1029         if (sizeX % 8) cols++;
1030
1031         if (m_customCursor) {
1032                 DestroyCursor(m_customCursor);
1033                 m_customCursor = NULL;
1034         }
1035
1036         memset(&andData, 0xFF, sizeof(andData));
1037         memset(&xorData, 0, sizeof(xorData));
1038
1039         for (y = 0; y < sizeY; y++) {
1040                 fullBitRow = 0;
1041                 fullMaskRow = 0;
1042                 for (x = cols - 1; x >= 0; x--) {
1043                         fullBitRow <<= 8;
1044                         fullMaskRow <<= 8;
1045                         fullBitRow  |= uns8ReverseBits(bitmap[cols * y + x]);
1046                         fullMaskRow |= uns8ReverseBits(mask[cols * y + x]);
1047                 }
1048                 xorData[y] = fullBitRow & fullMaskRow;
1049                 andData[y] = ~fullMaskRow;
1050         }
1051
1052         m_customCursor = ::CreateCursor(::GetModuleHandle(0), hotX, hotY, 32, 32, andData, xorData);
1053         if (!m_customCursor) {
1054                 return GHOST_kFailure;
1055         }
1056
1057         if (::GetForegroundWindow() == m_hWnd) {
1058                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
1059         }
1060
1061         return GHOST_kSuccess;
1062 }
1063
1064
1065 GHOST_TSuccess GHOST_WindowWin32::setProgressBar(float progress)
1066 {
1067         /*SetProgressValue sets state to TBPF_NORMAL automaticly*/
1068         if (m_Bar && S_OK == m_Bar->SetProgressValue(m_hWnd, 10000 * progress, 10000))
1069                 return GHOST_kSuccess;
1070
1071         return GHOST_kFailure;
1072 }
1073
1074 GHOST_TSuccess GHOST_WindowWin32::endProgressBar()
1075 {
1076         if (m_Bar && S_OK == m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS))
1077                 return GHOST_kSuccess;
1078
1079         return GHOST_kFailure;
1080 }
1081
1082
1083 #ifdef WITH_INPUT_IME
1084 void GHOST_WindowWin32::beginIME(GHOST_TInt32 x, GHOST_TInt32 y, GHOST_TInt32 w, GHOST_TInt32 h, int completed)
1085 {
1086         m_imeImput.BeginIME(m_hWnd, GHOST_Rect(x, y - h, x, y), (bool)completed);
1087 }
1088
1089
1090 void GHOST_WindowWin32::endIME()
1091 {
1092         m_imeImput.EndIME(m_hWnd);
1093 }
1094 #endif /* WITH_INPUT_IME */