81c08f4fc062d3f6669a6519f1bad4e977f66bb0
[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
47
48 #include <math.h>
49 #include <string.h>
50 #include <assert.h>
51
52
53
54 const wchar_t *GHOST_WindowWin32::s_windowClassName = L"GHOST_WindowClass";
55 const int GHOST_WindowWin32::s_maxTitleLength = 128;
56
57
58
59
60 /* force NVidia Optimus to used dedicated graphics */
61 extern "C" {
62         __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
63 }
64
65 GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
66         const STR_String &title,
67         GHOST_TInt32 left,
68         GHOST_TInt32 top,
69         GHOST_TUns32 width,
70         GHOST_TUns32 height,
71         GHOST_TWindowState state,
72         GHOST_TDrawingContextType type,
73         bool wantStereoVisual,
74         GHOST_TUns16 wantNumOfAASamples,
75         GHOST_TEmbedderWindowID parentwindowhwnd,
76         bool is_debug)
77     : GHOST_Window(width, height, state,
78                    wantStereoVisual, false, wantNumOfAASamples),
79       m_inLiveResize(false),
80       m_system(system),
81       m_hDC(0),
82       m_hasMouseCaptured(false),
83       m_hasGrabMouse(false),
84       m_nPressedButtons(0),
85       m_customCursor(0),
86       m_wintab(NULL),
87       m_tabletData(NULL),
88       m_tablet(0),
89       m_maxPressure(0),
90       m_normal_state(GHOST_kWindowStateNormal),
91       m_parentWindowHwnd(parentwindowhwnd),
92       m_debug_context(is_debug)
93 {
94         OSVERSIONINFOEX versionInfo;
95         bool hasMinVersionForTaskbar = false;
96         
97         ZeroMemory(&versionInfo, sizeof(OSVERSIONINFOEX));
98         
99         versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
100         
101         if (!GetVersionEx((OSVERSIONINFO *)&versionInfo)) {
102                 versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
103                 if (GetVersionEx((OSVERSIONINFO *)&versionInfo)) {
104                         if ((versionInfo.dwMajorVersion == 6 && versionInfo.dwMinorVersion >= 1) ||
105                             (versionInfo.dwMajorVersion >= 7))
106                         {
107                                 hasMinVersionForTaskbar = true;
108                         }
109                 }
110         }
111         else {
112                 if ((versionInfo.dwMajorVersion == 6 && versionInfo.dwMinorVersion >= 1) ||
113                     (versionInfo.dwMajorVersion >= 7))
114                 {
115                         hasMinVersionForTaskbar = true;
116                 }
117         }
118
119         if (state != GHOST_kWindowStateFullScreen) {
120                 RECT rect;
121                 MONITORINFO monitor;
122                 GHOST_TUns32 tw, th; 
123
124 #ifndef _MSC_VER
125                 int cxsizeframe = GetSystemMetrics(SM_CXSIZEFRAME);
126                 int cysizeframe = GetSystemMetrics(SM_CYSIZEFRAME);
127 #else
128                 // MSVC 2012+ returns bogus values from GetSystemMetrics, bug in Windows
129                 // http://connect.microsoft.com/VisualStudio/feedback/details/753224/regression-getsystemmetrics-delivers-different-values
130                 RECT cxrect = {0, 0, 0, 0};
131                 AdjustWindowRectEx(&cxrect, WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_THICKFRAME | WS_DLGFRAME, FALSE, 0);
132
133                 int cxsizeframe = abs(cxrect.bottom);
134                 int cysizeframe = abs(cxrect.left);
135 #endif
136
137                 width += cxsizeframe * 2;
138                 height += cysizeframe * 2 + GetSystemMetrics(SM_CYCAPTION);
139
140                 rect.left = left;
141                 rect.right = left + width;
142                 rect.top = top;
143                 rect.bottom = top + height;
144
145                 monitor.cbSize = sizeof(monitor);
146                 monitor.dwFlags = 0;
147
148                 // take taskbar into account
149                 GetMonitorInfo(MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST), &monitor);
150
151                 th = monitor.rcWork.bottom - monitor.rcWork.top;
152                 tw = monitor.rcWork.right - monitor.rcWork.left;
153
154                 if (tw < width) {
155                         width = tw;
156                         left = monitor.rcWork.left;
157                 }
158                 else if (monitor.rcWork.right < left + (int)width)
159                         left = monitor.rcWork.right - width;
160                 else if (left < monitor.rcWork.left)
161                         left = monitor.rcWork.left;
162
163                 if (th < height) {
164                         height = th;
165                         top = monitor.rcWork.top;
166                 }
167                 else if (monitor.rcWork.bottom < top + (int)height)
168                         top = monitor.rcWork.bottom - height;
169                 else if (top < monitor.rcWork.top)
170                         top = monitor.rcWork.top;
171
172                 int wintype = WS_OVERLAPPEDWINDOW;
173                 if (m_parentWindowHwnd != 0) {
174                         wintype = WS_CHILD;
175                         GetWindowRect((HWND)m_parentWindowHwnd, &rect);
176                         left = 0;
177                         top = 0;
178                         width = rect.right - rect.left;
179                         height = rect.bottom - rect.top;
180                 }
181                 
182                 wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0);
183                 m_hWnd = ::CreateWindowW(
184                     s_windowClassName,          // pointer to registered class name
185                     title_16,                   // pointer to window name
186                     wintype,                    // window style
187                     left,                       // horizontal position of window
188                     top,                        // vertical position of window
189                     width,                      // window width
190                     height,                     // window height
191                     (HWND) m_parentWindowHwnd,  // handle to parent or owner window
192                     0,                          // handle to menu or child-window identifier
193                     ::GetModuleHandle(0),       // handle to application instance
194                     0);                         // pointer to window-creation data
195                 free(title_16);
196         }
197         else {
198                 wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0);
199                 m_hWnd = ::CreateWindowW(
200                     s_windowClassName,          // pointer to registered class name
201                     title_16,                   // pointer to window name
202                     WS_POPUP | WS_MAXIMIZE,     // window style
203                     left,                       // horizontal position of window
204                     top,                        // vertical position of window
205                     width,                      // window width
206                     height,                     // window height
207                     HWND_DESKTOP,               // handle to parent or owner window
208                     0,                          // handle to menu or child-window identifier
209                     ::GetModuleHandle(0),       // handle to application instance
210                     0);                         // pointer to window-creation data
211                 free(title_16);
212         }
213         if (m_hWnd) {
214                 // Register this window as a droptarget. Requires m_hWnd to be valid.
215                 // Note that OleInitialize(0) has to be called prior to this. Done in GHOST_SystemWin32.
216                 m_dropTarget = new GHOST_DropTargetWin32(this, m_system);
217                 if (m_dropTarget) {
218                         ::RegisterDragDrop(m_hWnd, m_dropTarget);
219                 }
220
221                 // Store a pointer to this class in the window structure
222                 ::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR) this);
223
224                 // Store the device context
225                 m_hDC = ::GetDC(m_hWnd);
226
227                 GHOST_TSuccess success = setDrawingContextType(type);
228
229                 if (success) {
230                         // Show the window
231                         int nCmdShow;
232                         switch (state) {
233                                 case GHOST_kWindowStateMaximized:
234                                         nCmdShow = SW_SHOWMAXIMIZED;
235                                         break;
236                                 case GHOST_kWindowStateMinimized:
237                                         nCmdShow = SW_SHOWMINIMIZED;
238                                         break;
239                                 case GHOST_kWindowStateNormal:
240                                 default:
241                                         nCmdShow = SW_SHOWNORMAL;
242                                         break;
243                         }
244
245                         ::ShowWindow(m_hWnd, nCmdShow);
246                         // Force an initial paint of the window
247                         ::UpdateWindow(m_hWnd);
248                 }
249                 else {
250                         //invalidate the window
251                         ::DestroyWindow(m_hWnd);
252                         m_hWnd = NULL;
253                 }
254         }
255
256         if (parentwindowhwnd != 0) {
257                 RAWINPUTDEVICE device = {0};
258                 device.usUsagePage  = 0x01; /* usUsagePage & usUsage for keyboard*/
259                 device.usUsage      = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */
260                 device.dwFlags |= RIDEV_INPUTSINK; // makes WM_INPUT is visible for ghost when has parent window
261                 device.hwndTarget = m_hWnd;
262                 RegisterRawInputDevices(&device, 1, sizeof(device));
263         }
264
265         m_wintab = ::LoadLibrary("Wintab32.dll");
266         if (m_wintab) {
267                 GHOST_WIN32_WTInfo fpWTInfo = (GHOST_WIN32_WTInfo) ::GetProcAddress(m_wintab, "WTInfoA");
268                 GHOST_WIN32_WTOpen fpWTOpen = (GHOST_WIN32_WTOpen) ::GetProcAddress(m_wintab, "WTOpenA");
269
270                 // let's see if we can initialize tablet here
271                 /* check if WinTab available. */
272                 if (fpWTInfo && fpWTInfo(0, 0, NULL)) {
273                         // Now init the tablet
274                         LOGCONTEXT lc;
275                         /* The maximum tablet size, pressure and orientation (tilt) */
276                         AXIS TabletX, TabletY, Pressure, Orientation[3];
277
278                         // Open a Wintab context
279
280                         // Get default context information
281                         fpWTInfo(WTI_DEFCONTEXT, 0, &lc);
282
283                         // Open the context
284                         lc.lcPktData = PACKETDATA;
285                         lc.lcPktMode = PACKETMODE;
286                         lc.lcOptions |= CXO_MESSAGES | CXO_SYSTEM;
287
288                         /* Set the entire tablet as active */
289                         fpWTInfo(WTI_DEVICES, DVC_X, &TabletX);
290                         fpWTInfo(WTI_DEVICES, DVC_Y, &TabletY);
291
292                         /* get the max pressure, to divide into a float */
293                         BOOL pressureSupport = fpWTInfo(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
294                         if (pressureSupport)
295                                 m_maxPressure = Pressure.axMax;
296                         else
297                                 m_maxPressure = 0;
298
299                         /* get the max tilt axes, to divide into floats */
300                         BOOL tiltSupport = fpWTInfo(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
301                         if (tiltSupport) {
302                                 /* does the tablet support azimuth ([0]) and altitude ([1]) */
303                                 if (Orientation[0].axResolution && Orientation[1].axResolution) {
304                                         /* all this assumes the minimum is 0 */
305                                         m_maxAzimuth = Orientation[0].axMax;
306                                         m_maxAltitude = Orientation[1].axMax;
307                                 }
308                                 else {  /* no so dont do tilt stuff */
309                                         m_maxAzimuth = m_maxAltitude = 0;
310                                 }
311                         }
312
313                         if (fpWTOpen) {
314                                 m_tablet = fpWTOpen(m_hWnd, &lc, TRUE);
315                                 if (m_tablet) {
316                                         m_tabletData = new GHOST_TabletData();
317                                         m_tabletData->Active = GHOST_kTabletModeNone;
318                                 }
319                         }
320                 }
321         }
322
323         if (hasMinVersionForTaskbar)
324                 CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList, (LPVOID *)&m_Bar);
325         else
326                 m_Bar = NULL;
327 }
328
329
330 GHOST_WindowWin32::~GHOST_WindowWin32()
331 {
332         if (m_Bar) {
333                 m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
334                 m_Bar->Release();
335         }
336
337         if (m_wintab) {
338                 GHOST_WIN32_WTClose fpWTClose = (GHOST_WIN32_WTClose) ::GetProcAddress(m_wintab, "WTClose");
339                 if (fpWTClose) {
340                         if (m_tablet)
341                                 fpWTClose(m_tablet);
342                         delete m_tabletData;
343                         m_tabletData = NULL;
344                 }
345         }
346
347         if (m_customCursor) {
348                 DestroyCursor(m_customCursor);
349                 m_customCursor = NULL;
350         }
351
352         if (m_hWnd != NULL && m_hDC != NULL && releaseNativeHandles()) {
353                 ::ReleaseDC(m_hWnd, m_hDC);
354         }
355
356         if (m_hWnd) {
357                 if (m_dropTarget) {
358                         // Disable DragDrop
359                         RevokeDragDrop(m_hWnd);
360                         // Release our reference of the DropTarget and it will delete itself eventually.
361                         m_dropTarget->Release();
362                 }
363
364                 ::DestroyWindow(m_hWnd);
365                 m_hWnd = 0;
366         }
367 }
368
369 bool GHOST_WindowWin32::getValid() const
370 {
371         return GHOST_Window::getValid() && m_hWnd != 0 && m_hDC != 0;
372 }
373
374 HWND GHOST_WindowWin32::getHWND() const
375 {
376         return m_hWnd;
377 }
378
379 void GHOST_WindowWin32::setTitle(const STR_String &title)
380 {
381         wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0);
382         ::SetWindowTextW(m_hWnd, (wchar_t *)title_16);
383         free(title_16);
384 }
385
386
387 void GHOST_WindowWin32::getTitle(STR_String &title) const
388 {
389         char buf[s_maxTitleLength]; /*CHANGE + never used yet*/
390         ::GetWindowText(m_hWnd, buf, s_maxTitleLength);
391         STR_String temp(buf);
392         title = buf;
393 }
394
395
396 void GHOST_WindowWin32::getWindowBounds(GHOST_Rect &bounds) const
397 {
398         RECT rect;
399         ::GetWindowRect(m_hWnd, &rect);
400         bounds.m_b = rect.bottom;
401         bounds.m_l = rect.left;
402         bounds.m_r = rect.right;
403         bounds.m_t = rect.top;
404 }
405
406
407 void GHOST_WindowWin32::getClientBounds(GHOST_Rect &bounds) const
408 {
409         RECT rect;
410         POINT coord;
411         ::GetClientRect(m_hWnd, &rect);
412
413         coord.x = rect.left;
414         coord.y = rect.top;
415         ::ClientToScreen(m_hWnd, &coord);
416
417         bounds.m_l = coord.x;
418         bounds.m_t = coord.y;
419
420         coord.x = rect.right;
421         coord.y = rect.bottom;
422         ::ClientToScreen(m_hWnd, &coord);
423
424         bounds.m_r = coord.x;
425         bounds.m_b = coord.y;
426 }
427
428
429 GHOST_TSuccess GHOST_WindowWin32::setClientWidth(GHOST_TUns32 width)
430 {
431         GHOST_TSuccess success;
432         GHOST_Rect cBnds, wBnds;
433         getClientBounds(cBnds);
434         if (cBnds.getWidth() != (GHOST_TInt32)width) {
435                 getWindowBounds(wBnds);
436                 int cx = wBnds.getWidth() + width - cBnds.getWidth();
437                 int cy = wBnds.getHeight();
438                 success =  ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
439                           GHOST_kSuccess : GHOST_kFailure;
440         }
441         else {
442                 success = GHOST_kSuccess;
443         }
444         return success;
445 }
446
447
448 GHOST_TSuccess GHOST_WindowWin32::setClientHeight(GHOST_TUns32 height)
449 {
450         GHOST_TSuccess success;
451         GHOST_Rect cBnds, wBnds;
452         getClientBounds(cBnds);
453         if (cBnds.getHeight() != (GHOST_TInt32)height) {
454                 getWindowBounds(wBnds);
455                 int cx = wBnds.getWidth();
456                 int cy = wBnds.getHeight() + height - cBnds.getHeight();
457                 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
458                           GHOST_kSuccess : GHOST_kFailure;
459         }
460         else {
461                 success = GHOST_kSuccess;
462         }
463         return success;
464 }
465
466
467 GHOST_TSuccess GHOST_WindowWin32::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
468 {
469         GHOST_TSuccess success;
470         GHOST_Rect cBnds, wBnds;
471         getClientBounds(cBnds);
472         if ((cBnds.getWidth() != (GHOST_TInt32)width) || (cBnds.getHeight() != (GHOST_TInt32)height)) {
473                 getWindowBounds(wBnds);
474                 int cx = wBnds.getWidth() + width - cBnds.getWidth();
475                 int cy = wBnds.getHeight() + height - cBnds.getHeight();
476                 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
477                           GHOST_kSuccess : GHOST_kFailure;
478         }
479         else {
480                 success = GHOST_kSuccess;
481         }
482         return success;
483 }
484
485
486 GHOST_TWindowState GHOST_WindowWin32::getState() const
487 {
488         GHOST_TWindowState state;
489
490         // XXX 27.04.2011
491         // we need to find a way to combine parented windows + resizing if we simply set the
492         // state as GHOST_kWindowStateEmbedded we will need to check for them somewhere else.
493         // It's also strange that in Windows is the only platform we need to make this separation.
494         if (m_parentWindowHwnd != 0) {
495                 state = GHOST_kWindowStateEmbedded;
496                 return state;
497         }
498
499         if (::IsIconic(m_hWnd)) {
500                 state = GHOST_kWindowStateMinimized;
501         }
502         else if (::IsZoomed(m_hWnd)) {
503                 LONG_PTR result = ::GetWindowLongPtr(m_hWnd, GWL_STYLE);
504                 if ((result & (WS_POPUP | WS_MAXIMIZE)) != (WS_POPUP | WS_MAXIMIZE))
505                         state = GHOST_kWindowStateMaximized;
506                 else
507                         state = GHOST_kWindowStateFullScreen;
508         }
509         else {
510                 state = GHOST_kWindowStateNormal;
511         }
512         return state;
513 }
514
515
516 void GHOST_WindowWin32::screenToClient(
517         GHOST_TInt32 inX, GHOST_TInt32 inY,
518         GHOST_TInt32 &outX, GHOST_TInt32 &outY) const
519 {
520         POINT point = {inX, inY};
521         ::ScreenToClient(m_hWnd, &point);
522         outX = point.x;
523         outY = point.y;
524 }
525
526
527 void GHOST_WindowWin32::clientToScreen(
528         GHOST_TInt32 inX, GHOST_TInt32 inY,
529         GHOST_TInt32 &outX, GHOST_TInt32 &outY) const
530 {
531         POINT point = {inX, inY};
532         ::ClientToScreen(m_hWnd, &point);
533         outX = point.x;
534         outY = point.y;
535 }
536
537
538 GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
539 {
540         GHOST_TWindowState curstate = getState();
541         WINDOWPLACEMENT wp;
542         wp.length = sizeof(WINDOWPLACEMENT);
543         ::GetWindowPlacement(m_hWnd, &wp);
544
545         if (state == GHOST_kWindowStateNormal)
546                 state = m_normal_state;
547
548         switch (state) {
549                 case GHOST_kWindowStateMinimized:
550                         wp.showCmd = SW_SHOWMINIMIZED;
551                         break;
552                 case GHOST_kWindowStateMaximized:
553                         wp.showCmd = SW_SHOWMAXIMIZED;
554                         ::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
555                         break;
556                 case GHOST_kWindowStateFullScreen:
557                         if (curstate != state && curstate != GHOST_kWindowStateMinimized)
558                                 m_normal_state = curstate;
559                         wp.showCmd = SW_SHOWMAXIMIZED;
560                         wp.ptMaxPosition.x = 0;
561                         wp.ptMaxPosition.y = 0;
562                         ::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_POPUP | WS_MAXIMIZE);
563                         break;
564                 case GHOST_kWindowStateEmbedded:
565                         ::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_CHILD);
566                         break;
567                 case GHOST_kWindowStateNormal:
568                 default:
569                         wp.showCmd = SW_SHOWNORMAL;
570                         ::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
571                         break;
572         }
573         /* Clears window cache for SetWindowLongPtr */
574         ::SetWindowPos(m_hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
575
576         return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
577 }
578
579
580 GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order)
581 {
582         HWND hWndInsertAfter, hWndToRaise;
583
584         if (order == GHOST_kWindowOrderBottom) {
585                 hWndInsertAfter = HWND_BOTTOM;
586                 hWndToRaise = ::GetWindow(m_hWnd, GW_HWNDNEXT); /* the window to raise */
587         }
588         else {
589                 hWndInsertAfter = HWND_TOP;
590                 hWndToRaise = NULL;
591         }
592
593         if (::SetWindowPos(m_hWnd, hWndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == FALSE) {
594                 return GHOST_kFailure;
595         }
596
597         if (hWndToRaise && ::SetWindowPos(hWndToRaise, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == FALSE) {
598                 return GHOST_kFailure;
599         }
600         return GHOST_kSuccess;
601 }
602
603
604 GHOST_TSuccess GHOST_WindowWin32::invalidate()
605 {
606         GHOST_TSuccess success;
607         if (m_hWnd) {
608                 success = ::InvalidateRect(m_hWnd, 0, FALSE) != 0 ? GHOST_kSuccess : GHOST_kFailure;
609         }
610         else {
611                 success = GHOST_kFailure;
612         }
613         return success;
614 }
615
616
617 GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType type)
618 {
619         if (type == GHOST_kDrawingContextTypeOpenGL) {
620 #if !defined(WITH_GL_EGL)
621
622 #if defined(WITH_GL_PROFILE_CORE)
623                 GHOST_Context *context = new GHOST_ContextWGL(
624                         m_wantStereoVisual,
625                         m_wantNumOfAASamples,
626                         m_hWnd,
627                         m_hDC,
628                         WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
629                         3, 2,
630                         GHOST_OPENGL_WGL_CONTEXT_FLAGS,
631                         GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
632 #elif defined(WITH_GL_PROFILE_ES20)
633                 GHOST_Context *context = new GHOST_ContextWGL(
634                         m_wantStereoVisual,
635                         m_wantNumOfAASamples,
636                         m_hWnd,
637                         m_hDC,
638                         WGL_CONTEXT_ES2_PROFILE_BIT_EXT,
639                         2, 0,
640                         GHOST_OPENGL_WGL_CONTEXT_FLAGS,
641                         GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
642 #elif defined(WITH_GL_PROFILE_COMPAT)
643                 GHOST_Context *context = new GHOST_ContextWGL(
644                         m_wantStereoVisual,
645                         m_wantNumOfAASamples,
646                         m_hWnd,
647                         m_hDC,
648 #if 1
649                         0, // profile bit
650                         2, 1, // GL version requested
651 #else
652                         // switch to this for Blender 2.8 development
653                         WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
654                         3, 2,
655 #endif
656                         GHOST_OPENGL_WGL_CONTEXT_FLAGS,
657                         GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
658 #else
659 #  error
660 #endif
661
662 #else
663
664 #if defined(WITH_GL_PROFILE_CORE)
665                 GHOST_Context *context = new GHOST_ContextEGL(
666                         m_wantStereoVisual,
667                         m_wantNumOfAASamples,
668                         m_hWnd,
669                         m_hDC,
670                         EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
671                         3, 2,
672                         GHOST_OPENGL_EGL_CONTEXT_FLAGS,
673                         GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
674                         EGL_OPENGL_API);
675 #elif defined(WITH_GL_PROFILE_ES20)
676                 GHOST_Context *context = new GHOST_ContextEGL(
677                         m_wantStereoVisual,
678                         m_wantNumOfAASamples,
679                         m_hWnd,
680                         m_hDC,
681                         0, // profile bit
682                         2, 0,
683                         GHOST_OPENGL_EGL_CONTEXT_FLAGS,
684                         GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
685                         EGL_OPENGL_ES_API);
686 #elif defined(WITH_GL_PROFILE_COMPAT)
687                 GHOST_Context *context = new GHOST_ContextEGL(
688                         m_wantStereoVisual,
689                         m_wantNumOfAASamples,
690                         m_hWnd,
691                         m_hDC,
692 #if 1
693                         0, // profile bit
694                         2, 1, // GL version requested
695 #else
696                         // switch to this for Blender 2.8 development
697                         EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT,
698                         3, 2,
699 #endif
700                         GHOST_OPENGL_EGL_CONTEXT_FLAGS,
701                         GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
702                         EGL_OPENGL_API);
703 #else
704 #  error
705 #endif
706
707 #endif
708                 if (context->initializeDrawingContext())
709                         return context;
710                 else
711                         delete context;
712         }
713
714         return NULL;
715 }
716
717 void GHOST_WindowWin32::lostMouseCapture()
718 {
719         if (m_hasMouseCaptured) {
720                 m_hasGrabMouse = false;
721                 m_nPressedButtons = 0;
722                 m_hasMouseCaptured = false;
723         }
724 }
725
726 void GHOST_WindowWin32::registerMouseClickEvent(int press)
727 {
728
729         switch (press) {
730                 case 0: m_nPressedButtons++;    break;
731                 case 1: if (m_nPressedButtons) m_nPressedButtons--; break;
732                 case 2: m_hasGrabMouse = true;    break;
733                 case 3: m_hasGrabMouse = false;   break;
734         }
735
736         if (!m_nPressedButtons && !m_hasGrabMouse && m_hasMouseCaptured) {
737                 ::ReleaseCapture();
738                 m_hasMouseCaptured = false;
739         }
740         else if ((m_nPressedButtons || m_hasGrabMouse) && !m_hasMouseCaptured) {
741                 ::SetCapture(m_hWnd);
742                 m_hasMouseCaptured = true;
743
744         }
745 }
746
747
748 void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
749 {
750         if (!visible) {
751                 while (::ShowCursor(FALSE) >= 0) ;
752         }
753         else {
754                 while (::ShowCursor(TRUE) < 0) ;
755         }
756
757         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
758                 ::SetCursor(m_customCursor);
759         }
760         else {
761                 // Convert GHOST cursor to Windows OEM cursor
762                 bool success = true;
763                 LPCSTR id;
764                 switch (cursor) {
765                         case GHOST_kStandardCursorDefault:              id = IDC_ARROW;     break;
766                         case GHOST_kStandardCursorRightArrow:           id = IDC_ARROW;     break;
767                         case GHOST_kStandardCursorLeftArrow:            id = IDC_ARROW;     break;
768                         case GHOST_kStandardCursorInfo:                 id = IDC_SIZEALL;   break;  // Four-pointed arrow pointing north, south, east, and west
769                         case GHOST_kStandardCursorDestroy:              id = IDC_NO;        break;  // Slashed circle
770                         case GHOST_kStandardCursorHelp:                 id = IDC_HELP;      break;  // Arrow and question mark
771                         case GHOST_kStandardCursorCycle:                id = IDC_NO;        break;  // Slashed circle
772                         case GHOST_kStandardCursorSpray:                id = IDC_SIZEALL;   break;  // Four-pointed arrow pointing north, south, east, and west
773                         case GHOST_kStandardCursorWait:                 id = IDC_WAIT;      break;  // Hourglass
774                         case GHOST_kStandardCursorText:                 id = IDC_IBEAM;     break;  // I-beam
775                         case GHOST_kStandardCursorCrosshair:            id = IDC_CROSS;     break;  // Crosshair
776                         case GHOST_kStandardCursorUpDown:               id = IDC_SIZENS;    break;  // Double-pointed arrow pointing north and south
777                         case GHOST_kStandardCursorLeftRight:            id = IDC_SIZEWE;    break;  // Double-pointed arrow pointing west and east
778                         case GHOST_kStandardCursorTopSide:              id = IDC_UPARROW;   break;  // Vertical arrow
779                         case GHOST_kStandardCursorBottomSide:           id = IDC_SIZENS;    break;
780                         case GHOST_kStandardCursorLeftSide:             id = IDC_SIZEWE;    break;
781                         case GHOST_kStandardCursorTopLeftCorner:        id = IDC_SIZENWSE;  break;
782                         case GHOST_kStandardCursorTopRightCorner:       id = IDC_SIZENESW;  break;
783                         case GHOST_kStandardCursorBottomRightCorner:    id = IDC_SIZENWSE;  break;
784                         case GHOST_kStandardCursorBottomLeftCorner:     id = IDC_SIZENESW;  break;
785                         case GHOST_kStandardCursorPencil:               id = IDC_ARROW;     break;
786                         case GHOST_kStandardCursorCopy:                 id = IDC_ARROW;     break;
787                         default:
788                                 success = false;
789                 }
790
791                 if (success) {
792                         ::SetCursor(::LoadCursor(0, id));
793                 }
794         }
795 }
796
797 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible)
798 {
799         if (::GetForegroundWindow() == m_hWnd) {
800                 loadCursor(visible, getCursorShape());
801         }
802
803         return GHOST_kSuccess;
804 }
805
806 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
807 {
808         if (mode != GHOST_kGrabDisable) {
809                 if (mode != GHOST_kGrabNormal) {
810                         m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
811                         setCursorGrabAccum(0, 0);
812
813                         if (mode == GHOST_kGrabHide)
814                                 setWindowCursorVisibility(false);
815                 }
816                 registerMouseClickEvent(2);
817         }
818         else {
819                 if (m_cursorGrab == GHOST_kGrabHide) {
820                         m_system->setCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
821                         setWindowCursorVisibility(true);
822                 }
823                 if (m_cursorGrab != GHOST_kGrabNormal) {
824                         /* use to generate a mouse move event, otherwise the last event
825                          * blender gets can be outside the screen causing menus not to show
826                          * properly unless the user moves the mouse */
827                         GHOST_TInt32 pos[2];
828                         m_system->getCursorPosition(pos[0], pos[1]);
829                         m_system->setCursorPosition(pos[0], pos[1]);
830                 }
831
832                 /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
833                 setCursorGrabAccum(0, 0);
834                 m_cursorGrabBounds.m_l = m_cursorGrabBounds.m_r = -1; /* disable */
835                 registerMouseClickEvent(3);
836         }
837         
838         return GHOST_kSuccess;
839 }
840
841 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
842 {
843         if (m_customCursor) {
844                 DestroyCursor(m_customCursor);
845                 m_customCursor = NULL;
846         }
847
848         if (::GetForegroundWindow() == m_hWnd) {
849                 loadCursor(getCursorVisibility(), cursorShape);
850         }
851
852         return GHOST_kSuccess;
853 }
854
855 void GHOST_WindowWin32::processWin32TabletInitEvent()
856 {
857         if (m_wintab && m_tabletData) {
858                 GHOST_WIN32_WTInfo fpWTInfo = (GHOST_WIN32_WTInfo) ::GetProcAddress(m_wintab, "WTInfoA");
859
860                 // let's see if we can initialize tablet here
861                 /* check if WinTab available. */
862                 if (fpWTInfo) {
863                         AXIS Pressure, Orientation[3]; /* The maximum tablet size */
864
865                         BOOL pressureSupport = fpWTInfo(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
866                         if (pressureSupport)
867                                 m_maxPressure = Pressure.axMax;
868                         else
869                                 m_maxPressure = 0;
870
871                         BOOL tiltSupport = fpWTInfo(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
872                         if (tiltSupport) {
873                                 /* does the tablet support azimuth ([0]) and altitude ([1]) */
874                                 if (Orientation[0].axResolution && Orientation[1].axResolution) {
875                                         m_maxAzimuth = Orientation[0].axMax;
876                                         m_maxAltitude = Orientation[1].axMax;
877                                 }
878                                 else {  /* no so dont do tilt stuff */
879                                         m_maxAzimuth = m_maxAltitude = 0;
880                                 }
881                         }
882
883                         m_tabletData->Active = GHOST_kTabletModeNone;
884                 }
885         }
886 }
887
888 void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam)
889 {
890         PACKET pkt;
891         if (m_wintab) {
892                 GHOST_WIN32_WTPacket fpWTPacket = (GHOST_WIN32_WTPacket) ::GetProcAddress(m_wintab, "WTPacket");
893                 if (fpWTPacket) {
894                         if (fpWTPacket((HCTX)lParam, wParam, &pkt)) {
895                                 if (m_tabletData) {
896                                         switch (pkt.pkCursor) {
897                                                 case 0: /* first device */
898                                                 case 3: /* second device */
899                                                         m_tabletData->Active = GHOST_kTabletModeNone; /* puck - not yet supported */
900                                                         break;
901                                                 case 1:
902                                                 case 4:
903                                                 case 7:
904                                                         m_tabletData->Active = GHOST_kTabletModeStylus; /* stylus */
905                                                         break;
906                                                 case 2:
907                                                 case 5:
908                                                 case 8:
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 /** Reverse the bits in a GHOST_TUns8 */
973 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
974 {
975         ch = ((ch >> 1) & 0x55) | ((ch << 1) & 0xAA);
976         ch = ((ch >> 2) & 0x33) | ((ch << 2) & 0xCC);
977         ch = ((ch >> 4) & 0x0F) | ((ch << 4) & 0xF0);
978         return ch;
979 }
980
981 #if 0  /* UNUSED */
982 /** Reverse the bits in a GHOST_TUns16 */
983 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
984 {
985         shrt = ((shrt >> 1) & 0x5555) | ((shrt << 1) & 0xAAAA);
986         shrt = ((shrt >> 2) & 0x3333) | ((shrt << 2) & 0xCCCC);
987         shrt = ((shrt >> 4) & 0x0F0F) | ((shrt << 4) & 0xF0F0);
988         shrt = ((shrt >> 8) & 0x00FF) | ((shrt << 8) & 0xFF00);
989         return shrt;
990 }
991 #endif
992 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(
993         GHOST_TUns8 bitmap[16][2],
994         GHOST_TUns8 mask[16][2],
995         int hotX, int hotY)
996 {
997         return setWindowCustomCursorShape((GHOST_TUns8 *)bitmap, (GHOST_TUns8 *)mask,
998                                           16, 16, hotX, hotY, 0, 1);
999 }
1000
1001 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(
1002         GHOST_TUns8 *bitmap,
1003         GHOST_TUns8 *mask, int sizeX, int sizeY, int hotX, int hotY,
1004         int fg_color, int bg_color)
1005 {
1006         GHOST_TUns32 andData[32];
1007         GHOST_TUns32 xorData[32];
1008         GHOST_TUns32 fullBitRow, fullMaskRow;
1009         int x, y, cols;
1010
1011         cols = sizeX / 8; /* Num of whole bytes per row (width of bm/mask) */
1012         if (sizeX % 8) cols++;
1013
1014         if (m_customCursor) {
1015                 DestroyCursor(m_customCursor);
1016                 m_customCursor = NULL;
1017         }
1018
1019         memset(&andData, 0xFF, sizeof(andData));
1020         memset(&xorData, 0, sizeof(xorData));
1021
1022         for (y = 0; y < sizeY; y++) {
1023                 fullBitRow = 0;
1024                 fullMaskRow = 0;
1025                 for (x = cols - 1; x >= 0; x--) {
1026                         fullBitRow <<= 8;
1027                         fullMaskRow <<= 8;
1028                         fullBitRow  |= uns8ReverseBits(bitmap[cols * y + x]);
1029                         fullMaskRow |= uns8ReverseBits(mask[cols * y + x]);
1030                 }
1031                 xorData[y] = fullBitRow & fullMaskRow;
1032                 andData[y] = ~fullMaskRow;
1033         }
1034
1035         m_customCursor = ::CreateCursor(::GetModuleHandle(0), hotX, hotY, 32, 32, andData, xorData);
1036         if (!m_customCursor) {
1037                 return GHOST_kFailure;
1038         }
1039
1040         if (::GetForegroundWindow() == m_hWnd) {
1041                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
1042         }
1043
1044         return GHOST_kSuccess;
1045 }
1046
1047
1048 GHOST_TSuccess GHOST_WindowWin32::setProgressBar(float progress)
1049 {       
1050         /*SetProgressValue sets state to TBPF_NORMAL automaticly*/
1051         if (m_Bar && S_OK == m_Bar->SetProgressValue(m_hWnd, 10000 * progress, 10000))
1052                 return GHOST_kSuccess;
1053
1054         return GHOST_kFailure;
1055 }
1056
1057 GHOST_TSuccess GHOST_WindowWin32::endProgressBar()
1058 {
1059         if (m_Bar && S_OK == m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS))
1060                 return GHOST_kSuccess;
1061
1062         return GHOST_kFailure;
1063 }
1064
1065
1066 #ifdef WITH_INPUT_IME
1067 void GHOST_WindowWin32::beginIME(GHOST_TInt32 x, GHOST_TInt32 y, GHOST_TInt32 w, GHOST_TInt32 h, int completed)
1068 {
1069         m_imeImput.BeginIME(m_hWnd, GHOST_Rect(x, y - h, x, y), (bool)completed);
1070 }
1071
1072
1073 void GHOST_WindowWin32::endIME()
1074 {
1075         m_imeImput.EndIME(m_hWnd);
1076 }
1077 #endif /* WITH_INPUT_IME */