1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup GHOST
22  */
23 
24 #define _USE_MATH_DEFINES
25 
26 #include "GHOST_WindowWin32.h"
27 #include "GHOST_ContextD3D.h"
28 #include "GHOST_ContextNone.h"
29 #include "GHOST_DropTargetWin32.h"
30 #include "GHOST_SystemWin32.h"
31 #include "GHOST_WindowManager.h"
32 #include "utf_winfunc.h"
33 #include "utfconv.h"
34 
35 #if defined(WITH_GL_EGL)
36 #  include "GHOST_ContextEGL.h"
37 #else
38 #  include "GHOST_ContextWGL.h"
39 #endif
40 #ifdef WIN32_COMPOSITING
41 #  include <Dwmapi.h>
42 #endif
43 
44 #include <assert.h>
45 #include <math.h>
46 #include <string.h>
47 #include <windowsx.h>
48 
49 #ifndef GET_POINTERID_WPARAM
50 #  define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam))
51 #endif  // GET_POINTERID_WPARAM
52 
53 const wchar_t *GHOST_WindowWin32::s_windowClassName = L"GHOST_WindowClass";
54 const int GHOST_WindowWin32::s_maxTitleLength = 128;
55 
56 /* force NVidia Optimus to used dedicated graphics */
57 extern "C" {
58 __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
59 }
60 
GHOST_WindowWin32(GHOST_SystemWin32 * system,const char * title,GHOST_TInt32 left,GHOST_TInt32 top,GHOST_TUns32 width,GHOST_TUns32 height,GHOST_TWindowState state,GHOST_TDrawingContextType type,bool wantStereoVisual,bool alphaBackground,GHOST_WindowWin32 * parentwindow,bool is_debug,bool dialog)61 GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
62                                      const char *title,
63                                      GHOST_TInt32 left,
64                                      GHOST_TInt32 top,
65                                      GHOST_TUns32 width,
66                                      GHOST_TUns32 height,
67                                      GHOST_TWindowState state,
68                                      GHOST_TDrawingContextType type,
69                                      bool wantStereoVisual,
70                                      bool alphaBackground,
71                                      GHOST_WindowWin32 *parentwindow,
72                                      bool is_debug,
73                                      bool dialog)
74     : GHOST_Window(width, height, state, wantStereoVisual, false),
75       m_tabletInRange(false),
76       m_inLiveResize(false),
77       m_system(system),
78       m_hDC(0),
79       m_hasMouseCaptured(false),
80       m_hasGrabMouse(false),
81       m_nPressedButtons(0),
82       m_customCursor(0),
83       m_wantAlphaBackground(alphaBackground),
84       m_normal_state(GHOST_kWindowStateNormal),
85       m_user32(NULL),
86       m_fpGetPointerInfoHistory(NULL),
87       m_fpGetPointerPenInfoHistory(NULL),
88       m_fpGetPointerTouchInfoHistory(NULL),
89       m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : NULL),
90       m_debug_context(is_debug)
91 {
92   // Initialize tablet variables
93   memset(&m_wintab, 0, sizeof(m_wintab));
94   m_tabletData = GHOST_TABLET_DATA_NONE;
95 
96   // Create window
97   if (state != GHOST_kWindowStateFullScreen) {
98     RECT rect;
99     MONITORINFO monitor;
100     GHOST_TUns32 tw, th;
101 
102 #ifndef _MSC_VER
103     int cxsizeframe = GetSystemMetrics(SM_CXSIZEFRAME);
104     int cysizeframe = GetSystemMetrics(SM_CYSIZEFRAME);
105 #else
106     // MSVC 2012+ returns bogus values from GetSystemMetrics, bug in Windows
107     // http://connect.microsoft.com/VisualStudio/feedback/details/753224/regression-getsystemmetrics-delivers-different-values
108     RECT cxrect = {0, 0, 0, 0};
109     AdjustWindowRectEx(
110         &cxrect, WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_THICKFRAME | WS_DLGFRAME, FALSE, 0);
111 
112     int cxsizeframe = abs(cxrect.bottom);
113     int cysizeframe = abs(cxrect.left);
114 #endif
115 
116     width += cxsizeframe * 2;
117     height += cysizeframe * 2 + GetSystemMetrics(SM_CYCAPTION);
118 
119     rect.left = left;
120     rect.right = left + width;
121     rect.top = top;
122     rect.bottom = top + height;
123 
124     monitor.cbSize = sizeof(monitor);
125     monitor.dwFlags = 0;
126 
127     // take taskbar into account
128     GetMonitorInfo(MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST), &monitor);
129 
130     th = monitor.rcWork.bottom - monitor.rcWork.top;
131     tw = monitor.rcWork.right - monitor.rcWork.left;
132 
133     if (tw < width) {
134       width = tw;
135       left = monitor.rcWork.left;
136     }
137     else if (monitor.rcWork.right < left + (int)width)
138       left = monitor.rcWork.right - width;
139     else if (left < monitor.rcWork.left)
140       left = monitor.rcWork.left;
141 
142     if (th < height) {
143       height = th;
144       top = monitor.rcWork.top;
145     }
146     else if (monitor.rcWork.bottom < top + (int)height)
147       top = monitor.rcWork.bottom - height;
148     else if (top < monitor.rcWork.top)
149       top = monitor.rcWork.top;
150 
151     int wintype = WS_OVERLAPPEDWINDOW;
152     if ((m_parentWindowHwnd != 0) && !dialog) {
153       wintype = WS_CHILD;
154       GetWindowRect(m_parentWindowHwnd, &rect);
155       left = 0;
156       top = 0;
157       width = rect.right - rect.left;
158       height = rect.bottom - rect.top;
159     }
160 
161     wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0);
162     m_hWnd = ::CreateWindowW(s_windowClassName,                // pointer to registered class name
163                              title_16,                         // pointer to window name
164                              wintype,                          // window style
165                              left,                             // horizontal position of window
166                              top,                              // vertical position of window
167                              width,                            // window width
168                              height,                           // window height
169                              dialog ? 0 : m_parentWindowHwnd,  // handle to parent or owner window
170                              0,                     // handle to menu or child-window identifier
171                              ::GetModuleHandle(0),  // handle to application instance
172                              0);                    // pointer to window-creation data
173     free(title_16);
174   }
175   else {
176     wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0);
177     m_hWnd = ::CreateWindowW(s_windowClassName,     // pointer to registered class name
178                              title_16,              // pointer to window name
179                              WS_MAXIMIZE,           // window style
180                              left,                  // horizontal position of window
181                              top,                   // vertical position of window
182                              width,                 // window width
183                              height,                // window height
184                              HWND_DESKTOP,          // handle to parent or owner window
185                              0,                     // handle to menu or child-window identifier
186                              ::GetModuleHandle(0),  // handle to application instance
187                              0);                    // pointer to window-creation data
188     free(title_16);
189   }
190 
191   m_user32 = ::LoadLibrary("user32.dll");
192 
193   if (m_hWnd) {
194     if (m_user32) {
195       // Touch enabled screens with pen support by default have gestures
196       // enabled, which results in a delay between the pointer down event
197       // and the first move when using the stylus. RegisterTouchWindow
198       // disables the new gesture architecture enabling the events to be
199       // sent immediately to the application rather than being absorbed by
200       // the gesture API.
201       GHOST_WIN32_RegisterTouchWindow pRegisterTouchWindow = (GHOST_WIN32_RegisterTouchWindow)
202           GetProcAddress(m_user32, "RegisterTouchWindow");
203       if (pRegisterTouchWindow) {
204         pRegisterTouchWindow(m_hWnd, 0);
205       }
206     }
207 
208     // Register this window as a droptarget. Requires m_hWnd to be valid.
209     // Note that OleInitialize(0) has to be called prior to this. Done in GHOST_SystemWin32.
210     m_dropTarget = new GHOST_DropTargetWin32(this, m_system);
211     if (m_dropTarget) {
212       ::RegisterDragDrop(m_hWnd, m_dropTarget);
213     }
214 
215     // Store a pointer to this class in the window structure
216     ::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR)this);
217 
218     if (!m_system->m_windowFocus) {
219       // Lower to bottom and don't activate if we don't want focus
220       ::SetWindowPos(m_hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
221     }
222 
223     // Store the device context
224     m_hDC = ::GetDC(m_hWnd);
225 
226     GHOST_TSuccess success = setDrawingContextType(type);
227 
228     if (success) {
229       // Show the window
230       int nCmdShow;
231       switch (state) {
232         case GHOST_kWindowStateMaximized:
233           nCmdShow = SW_SHOWMAXIMIZED;
234           break;
235         case GHOST_kWindowStateMinimized:
236           nCmdShow = (m_system->m_windowFocus) ? SW_SHOWMINIMIZED : SW_SHOWMINNOACTIVE;
237           break;
238         case GHOST_kWindowStateNormal:
239         default:
240           nCmdShow = (m_system->m_windowFocus) ? SW_SHOWNORMAL : SW_SHOWNOACTIVATE;
241           break;
242       }
243 
244       ::ShowWindow(m_hWnd, nCmdShow);
245 #ifdef WIN32_COMPOSITING
246       if (alphaBackground && parentwindowhwnd == 0) {
247 
248         HRESULT hr = S_OK;
249 
250         // Create and populate the Blur Behind structure
251         DWM_BLURBEHIND bb = {0};
252 
253         // Enable Blur Behind and apply to the entire client area
254         bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
255         bb.fEnable = true;
256         bb.hRgnBlur = CreateRectRgn(0, 0, -1, -1);
257 
258         // Apply Blur Behind
259         hr = DwmEnableBlurBehindWindow(m_hWnd, &bb);
260         DeleteObject(bb.hRgnBlur);
261       }
262 #endif
263       // Force an initial paint of the window
264       ::UpdateWindow(m_hWnd);
265     }
266     else {
267       // invalidate the window
268       ::DestroyWindow(m_hWnd);
269       m_hWnd = NULL;
270     }
271   }
272 
273   if (dialog && parentwindow) {
274     ::SetWindowLongPtr(
275         m_hWnd, GWL_STYLE, WS_VISIBLE | WS_POPUPWINDOW | WS_CAPTION | WS_MAXIMIZEBOX | WS_SIZEBOX);
276     ::SetWindowLongPtr(m_hWnd, GWLP_HWNDPARENT, (LONG_PTR)m_parentWindowHwnd);
277     ::SetWindowPos(
278         m_hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
279   }
280   else if (parentwindow) {
281     RAWINPUTDEVICE device = {0};
282     device.usUsagePage = 0x01; /* usUsagePage & usUsage for keyboard*/
283     device.usUsage = 0x06;     /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */
284     device.dwFlags |=
285         RIDEV_INPUTSINK;  // makes WM_INPUT is visible for ghost when has parent window
286     device.hwndTarget = m_hWnd;
287     RegisterRawInputDevices(&device, 1, sizeof(device));
288   }
289 
290   // Initialize Windows Ink
291   if (m_user32) {
292     m_fpGetPointerInfoHistory = (GHOST_WIN32_GetPointerInfoHistory)::GetProcAddress(
293         m_user32, "GetPointerInfoHistory");
294     m_fpGetPointerPenInfoHistory = (GHOST_WIN32_GetPointerPenInfoHistory)::GetProcAddress(
295         m_user32, "GetPointerPenInfoHistory");
296     m_fpGetPointerTouchInfoHistory = (GHOST_WIN32_GetPointerTouchInfoHistory)::GetProcAddress(
297         m_user32, "GetPointerTouchInfoHistory");
298   }
299 
300   // Initialize Wintab
301   m_wintab.handle = ::LoadLibrary("Wintab32.dll");
302   if (m_wintab.handle && m_system->getTabletAPI() != GHOST_kTabletNative) {
303     // Get API functions
304     m_wintab.info = (GHOST_WIN32_WTInfo)::GetProcAddress(m_wintab.handle, "WTInfoA");
305     m_wintab.open = (GHOST_WIN32_WTOpen)::GetProcAddress(m_wintab.handle, "WTOpenA");
306     m_wintab.close = (GHOST_WIN32_WTClose)::GetProcAddress(m_wintab.handle, "WTClose");
307     m_wintab.packet = (GHOST_WIN32_WTPacket)::GetProcAddress(m_wintab.handle, "WTPacket");
308     m_wintab.enable = (GHOST_WIN32_WTEnable)::GetProcAddress(m_wintab.handle, "WTEnable");
309     m_wintab.overlap = (GHOST_WIN32_WTOverlap)::GetProcAddress(m_wintab.handle, "WTOverlap");
310 
311     // Let's see if we can initialize tablet here.
312     // Check if WinTab available by getting system context info.
313     LOGCONTEXT lc = {0};
314     lc.lcOptions |= CXO_SYSTEM;
315     if (m_wintab.open && m_wintab.info && m_wintab.info(WTI_DEFSYSCTX, 0, &lc)) {
316       // Now init the tablet
317       /* The maximum tablet size, pressure and orientation (tilt) */
318       AXIS TabletX, TabletY, Pressure, Orientation[3];
319 
320       // Open a Wintab context
321 
322       // Open the context
323       lc.lcPktData = PACKETDATA;
324       lc.lcPktMode = PACKETMODE;
325       lc.lcOptions |= CXO_MESSAGES;
326       lc.lcMoveMask = PACKETDATA;
327 
328       /* Set the entire tablet as active */
329       m_wintab.info(WTI_DEVICES, DVC_X, &TabletX);
330       m_wintab.info(WTI_DEVICES, DVC_Y, &TabletY);
331 
332       /* get the max pressure, to divide into a float */
333       BOOL pressureSupport = m_wintab.info(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
334       if (pressureSupport)
335         m_wintab.maxPressure = Pressure.axMax;
336       else
337         m_wintab.maxPressure = 0;
338 
339       /* get the max tilt axes, to divide into floats */
340       BOOL tiltSupport = m_wintab.info(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
341       if (tiltSupport) {
342         /* does the tablet support azimuth ([0]) and altitude ([1]) */
343         if (Orientation[0].axResolution && Orientation[1].axResolution) {
344           /* all this assumes the minimum is 0 */
345           m_wintab.maxAzimuth = Orientation[0].axMax;
346           m_wintab.maxAltitude = Orientation[1].axMax;
347         }
348         else { /* no so dont do tilt stuff */
349           m_wintab.maxAzimuth = m_wintab.maxAltitude = 0;
350         }
351       }
352 
353       // The Wintab spec says we must open the context disabled if we are using cursor masks.
354       m_wintab.tablet = m_wintab.open(m_hWnd, &lc, FALSE);
355       if (m_wintab.enable && m_wintab.tablet) {
356         m_wintab.enable(m_wintab.tablet, TRUE);
357       }
358     }
359   }
360   CoCreateInstance(
361       CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar);
362 }
363 
~GHOST_WindowWin32()364 GHOST_WindowWin32::~GHOST_WindowWin32()
365 {
366   if (m_Bar) {
367     m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
368     m_Bar->Release();
369     m_Bar = NULL;
370   }
371 
372   if (m_wintab.handle) {
373     if (m_wintab.close && m_wintab.tablet) {
374       m_wintab.close(m_wintab.tablet);
375     }
376 
377     FreeLibrary(m_wintab.handle);
378     memset(&m_wintab, 0, sizeof(m_wintab));
379   }
380 
381   if (m_user32) {
382     FreeLibrary(m_user32);
383     m_user32 = NULL;
384     m_fpGetPointerInfoHistory = NULL;
385     m_fpGetPointerPenInfoHistory = NULL;
386     m_fpGetPointerTouchInfoHistory = NULL;
387   }
388 
389   if (m_customCursor) {
390     DestroyCursor(m_customCursor);
391     m_customCursor = NULL;
392   }
393 
394   if (m_hWnd != NULL && m_hDC != NULL && releaseNativeHandles()) {
395     ::ReleaseDC(m_hWnd, m_hDC);
396     m_hDC = NULL;
397   }
398 
399   if (m_hWnd) {
400     /* If this window is referenced by others as parent, clear that relation or windows will free
401      * the handle while we still reference it. */
402     for (GHOST_IWindow *iter_win : m_system->getWindowManager()->getWindows()) {
403       GHOST_WindowWin32 *iter_winwin = (GHOST_WindowWin32 *)iter_win;
404       if (iter_winwin->m_parentWindowHwnd == m_hWnd) {
405         ::SetWindowLongPtr(iter_winwin->m_hWnd, GWLP_HWNDPARENT, NULL);
406         iter_winwin->m_parentWindowHwnd = 0;
407       }
408     }
409 
410     if (m_dropTarget) {
411       // Disable DragDrop
412       RevokeDragDrop(m_hWnd);
413       // Release our reference of the DropTarget and it will delete itself eventually.
414       m_dropTarget->Release();
415       m_dropTarget = NULL;
416     }
417     ::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, NULL);
418     ::DestroyWindow(m_hWnd);
419     m_hWnd = 0;
420   }
421 }
422 
getValid() const423 bool GHOST_WindowWin32::getValid() const
424 {
425   return GHOST_Window::getValid() && m_hWnd != 0 && m_hDC != 0;
426 }
427 
getHWND() const428 HWND GHOST_WindowWin32::getHWND() const
429 {
430   return m_hWnd;
431 }
432 
setTitle(const char * title)433 void GHOST_WindowWin32::setTitle(const char *title)
434 {
435   wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0);
436   ::SetWindowTextW(m_hWnd, (wchar_t *)title_16);
437   free(title_16);
438 }
439 
getTitle() const440 std::string GHOST_WindowWin32::getTitle() const
441 {
442   char buf[s_maxTitleLength]; /*CHANGE + never used yet*/
443   ::GetWindowText(m_hWnd, buf, s_maxTitleLength);
444   return std::string(buf);
445 }
446 
getWindowBounds(GHOST_Rect & bounds) const447 void GHOST_WindowWin32::getWindowBounds(GHOST_Rect &bounds) const
448 {
449   RECT rect;
450   ::GetWindowRect(m_hWnd, &rect);
451   bounds.m_b = rect.bottom;
452   bounds.m_l = rect.left;
453   bounds.m_r = rect.right;
454   bounds.m_t = rect.top;
455 }
456 
getClientBounds(GHOST_Rect & bounds) const457 void GHOST_WindowWin32::getClientBounds(GHOST_Rect &bounds) const
458 {
459   RECT rect;
460   POINT coord;
461   if (!IsIconic(m_hWnd)) {
462     ::GetClientRect(m_hWnd, &rect);
463 
464     coord.x = rect.left;
465     coord.y = rect.top;
466     ::ClientToScreen(m_hWnd, &coord);
467 
468     bounds.m_l = coord.x;
469     bounds.m_t = coord.y;
470 
471     coord.x = rect.right;
472     coord.y = rect.bottom;
473     ::ClientToScreen(m_hWnd, &coord);
474 
475     bounds.m_r = coord.x;
476     bounds.m_b = coord.y;
477   }
478   else {
479     bounds.m_b = 0;
480     bounds.m_l = 0;
481     bounds.m_r = 0;
482     bounds.m_t = 0;
483   }
484 }
485 
setClientWidth(GHOST_TUns32 width)486 GHOST_TSuccess GHOST_WindowWin32::setClientWidth(GHOST_TUns32 width)
487 {
488   GHOST_TSuccess success;
489   GHOST_Rect cBnds, wBnds;
490   getClientBounds(cBnds);
491   if (cBnds.getWidth() != (GHOST_TInt32)width) {
492     getWindowBounds(wBnds);
493     int cx = wBnds.getWidth() + width - cBnds.getWidth();
494     int cy = wBnds.getHeight();
495     success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
496                   GHOST_kSuccess :
497                   GHOST_kFailure;
498   }
499   else {
500     success = GHOST_kSuccess;
501   }
502   return success;
503 }
504 
setClientHeight(GHOST_TUns32 height)505 GHOST_TSuccess GHOST_WindowWin32::setClientHeight(GHOST_TUns32 height)
506 {
507   GHOST_TSuccess success;
508   GHOST_Rect cBnds, wBnds;
509   getClientBounds(cBnds);
510   if (cBnds.getHeight() != (GHOST_TInt32)height) {
511     getWindowBounds(wBnds);
512     int cx = wBnds.getWidth();
513     int cy = wBnds.getHeight() + height - cBnds.getHeight();
514     success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
515                   GHOST_kSuccess :
516                   GHOST_kFailure;
517   }
518   else {
519     success = GHOST_kSuccess;
520   }
521   return success;
522 }
523 
setClientSize(GHOST_TUns32 width,GHOST_TUns32 height)524 GHOST_TSuccess GHOST_WindowWin32::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
525 {
526   GHOST_TSuccess success;
527   GHOST_Rect cBnds, wBnds;
528   getClientBounds(cBnds);
529   if ((cBnds.getWidth() != (GHOST_TInt32)width) || (cBnds.getHeight() != (GHOST_TInt32)height)) {
530     getWindowBounds(wBnds);
531     int cx = wBnds.getWidth() + width - cBnds.getWidth();
532     int cy = wBnds.getHeight() + height - cBnds.getHeight();
533     success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
534                   GHOST_kSuccess :
535                   GHOST_kFailure;
536   }
537   else {
538     success = GHOST_kSuccess;
539   }
540   return success;
541 }
542 
getState() const543 GHOST_TWindowState GHOST_WindowWin32::getState() const
544 {
545   GHOST_TWindowState state;
546 
547   // XXX 27.04.2011
548   // we need to find a way to combine parented windows + resizing if we simply set the
549   // state as GHOST_kWindowStateEmbedded we will need to check for them somewhere else.
550   // It's also strange that in Windows is the only platform we need to make this separation.
551   if ((m_parentWindowHwnd != 0) && !isDialog()) {
552     state = GHOST_kWindowStateEmbedded;
553     return state;
554   }
555 
556   if (::IsIconic(m_hWnd)) {
557     state = GHOST_kWindowStateMinimized;
558   }
559   else if (::IsZoomed(m_hWnd)) {
560     LONG_PTR result = ::GetWindowLongPtr(m_hWnd, GWL_STYLE);
561     if ((result & (WS_DLGFRAME | WS_MAXIMIZE)) == (WS_DLGFRAME | WS_MAXIMIZE))
562       state = GHOST_kWindowStateMaximized;
563     else
564       state = GHOST_kWindowStateFullScreen;
565   }
566   else {
567     state = GHOST_kWindowStateNormal;
568   }
569   return state;
570 }
571 
screenToClient(GHOST_TInt32 inX,GHOST_TInt32 inY,GHOST_TInt32 & outX,GHOST_TInt32 & outY) const572 void GHOST_WindowWin32::screenToClient(GHOST_TInt32 inX,
573                                        GHOST_TInt32 inY,
574                                        GHOST_TInt32 &outX,
575                                        GHOST_TInt32 &outY) const
576 {
577   POINT point = {inX, inY};
578   ::ScreenToClient(m_hWnd, &point);
579   outX = point.x;
580   outY = point.y;
581 }
582 
clientToScreen(GHOST_TInt32 inX,GHOST_TInt32 inY,GHOST_TInt32 & outX,GHOST_TInt32 & outY) const583 void GHOST_WindowWin32::clientToScreen(GHOST_TInt32 inX,
584                                        GHOST_TInt32 inY,
585                                        GHOST_TInt32 &outX,
586                                        GHOST_TInt32 &outY) const
587 {
588   POINT point = {inX, inY};
589   ::ClientToScreen(m_hWnd, &point);
590   outX = point.x;
591   outY = point.y;
592 }
593 
setState(GHOST_TWindowState state)594 GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
595 {
596   GHOST_TWindowState curstate = getState();
597   LONG_PTR newstyle = -1;
598   WINDOWPLACEMENT wp;
599   wp.length = sizeof(WINDOWPLACEMENT);
600   ::GetWindowPlacement(m_hWnd, &wp);
601 
602   if (state == GHOST_kWindowStateNormal)
603     state = m_normal_state;
604 
605   switch (state) {
606     case GHOST_kWindowStateMinimized:
607       wp.showCmd = SW_SHOWMINIMIZED;
608       break;
609     case GHOST_kWindowStateMaximized:
610       wp.showCmd = SW_SHOWMAXIMIZED;
611       newstyle = WS_OVERLAPPEDWINDOW;
612       break;
613     case GHOST_kWindowStateFullScreen:
614       if (curstate != state && curstate != GHOST_kWindowStateMinimized)
615         m_normal_state = curstate;
616       wp.showCmd = SW_SHOWMAXIMIZED;
617       wp.ptMaxPosition.x = 0;
618       wp.ptMaxPosition.y = 0;
619       newstyle = WS_MAXIMIZE;
620       break;
621     case GHOST_kWindowStateEmbedded:
622       newstyle = WS_CHILD;
623       break;
624     case GHOST_kWindowStateNormal:
625     default:
626       wp.showCmd = SW_SHOWNORMAL;
627       newstyle = WS_OVERLAPPEDWINDOW;
628       break;
629   }
630   if ((newstyle >= 0) && !isDialog()) {
631     ::SetWindowLongPtr(m_hWnd, GWL_STYLE, newstyle);
632   }
633 
634   /* Clears window cache for SetWindowLongPtr */
635   ::SetWindowPos(m_hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
636 
637   return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
638 }
639 
setOrder(GHOST_TWindowOrder order)640 GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order)
641 {
642   HWND hWndInsertAfter, hWndToRaise;
643 
644   if (order == GHOST_kWindowOrderBottom) {
645     hWndInsertAfter = HWND_BOTTOM;
646     hWndToRaise = ::GetWindow(m_hWnd, GW_HWNDNEXT); /* the window to raise */
647   }
648   else {
649     if (getState() == GHOST_kWindowStateMinimized) {
650       setState(GHOST_kWindowStateNormal);
651     }
652     hWndInsertAfter = HWND_TOP;
653     hWndToRaise = NULL;
654   }
655 
656   if (::SetWindowPos(m_hWnd, hWndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == FALSE) {
657     return GHOST_kFailure;
658   }
659 
660   if (hWndToRaise &&
661       ::SetWindowPos(hWndToRaise, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == FALSE) {
662     return GHOST_kFailure;
663   }
664   return GHOST_kSuccess;
665 }
666 
invalidate()667 GHOST_TSuccess GHOST_WindowWin32::invalidate()
668 {
669   GHOST_TSuccess success;
670   if (m_hWnd) {
671     success = ::InvalidateRect(m_hWnd, 0, FALSE) != 0 ? GHOST_kSuccess : GHOST_kFailure;
672   }
673   else {
674     success = GHOST_kFailure;
675   }
676   return success;
677 }
678 
newDrawingContext(GHOST_TDrawingContextType type)679 GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType type)
680 {
681   if (type == GHOST_kDrawingContextTypeOpenGL) {
682     GHOST_Context *context;
683 
684 #if defined(WITH_GL_PROFILE_CORE)
685     /* - AMD and Intel give us exactly this version
686      * - NVIDIA gives at least this version <-- desired behavior
687      * So we ask for 4.5, 4.4 ... 3.3 in descending order
688      * to get the best version on the user's system. */
689     for (int minor = 5; minor >= 0; --minor) {
690       context = new GHOST_ContextWGL(m_wantStereoVisual,
691                                      m_wantAlphaBackground,
692                                      m_hWnd,
693                                      m_hDC,
694                                      WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
695                                      4,
696                                      minor,
697                                      (m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
698                                      GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
699 
700       if (context->initializeDrawingContext()) {
701         return context;
702       }
703       else {
704         delete context;
705       }
706     }
707     context = new GHOST_ContextWGL(m_wantStereoVisual,
708                                    m_wantAlphaBackground,
709                                    m_hWnd,
710                                    m_hDC,
711                                    WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
712                                    3,
713                                    3,
714                                    (m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
715                                    GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
716 
717     if (context->initializeDrawingContext()) {
718       return context;
719     }
720     else {
721       MessageBox(m_hWnd,
722                  "A graphics card and driver with support for OpenGL 3.3 or higher is required.\n"
723                  "Installing the latest driver for your graphics card may resolve the issue.\n\n"
724                  "The program will now close.",
725                  "Blender - Unsupported Graphics Card or Driver",
726                  MB_OK | MB_ICONERROR);
727       delete context;
728       exit(0);
729     }
730 
731 #elif defined(WITH_GL_PROFILE_COMPAT)
732     // ask for 2.1 context, driver gives any GL version >= 2.1
733     // (hopefully the latest compatibility profile)
734     // 2.1 ignores the profile bit & is incompatible with core profile
735     context = new GHOST_ContextWGL(m_wantStereoVisual,
736                                    m_wantAlphaBackground,
737                                    m_hWnd,
738                                    m_hDC,
739                                    0,  // no profile bit
740                                    2,
741                                    1,
742                                    (m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
743                                    GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
744 
745     if (context->initializeDrawingContext()) {
746       return context;
747     }
748     else {
749       delete context;
750     }
751 #else
752 #  error  // must specify either core or compat at build time
753 #endif
754   }
755   else if (type == GHOST_kDrawingContextTypeD3D) {
756     GHOST_Context *context;
757 
758     context = new GHOST_ContextD3D(false, m_hWnd);
759     if (context->initializeDrawingContext()) {
760       return context;
761     }
762     else {
763       delete context;
764     }
765 
766     return context;
767   }
768 
769   return NULL;
770 }
771 
lostMouseCapture()772 void GHOST_WindowWin32::lostMouseCapture()
773 {
774   if (m_hasMouseCaptured) {
775     m_hasGrabMouse = false;
776     m_nPressedButtons = 0;
777     m_hasMouseCaptured = false;
778   }
779 }
780 
isDialog() const781 bool GHOST_WindowWin32::isDialog() const
782 {
783   HWND parent = (HWND)::GetWindowLongPtr(m_hWnd, GWLP_HWNDPARENT);
784   long int style = (long int)::GetWindowLongPtr(m_hWnd, GWL_STYLE);
785 
786   return (parent != 0) && (style & WS_POPUPWINDOW);
787 }
788 
updateMouseCapture(GHOST_MouseCaptureEventWin32 event)789 void GHOST_WindowWin32::updateMouseCapture(GHOST_MouseCaptureEventWin32 event)
790 {
791   switch (event) {
792     case MousePressed:
793       m_nPressedButtons++;
794       break;
795     case MouseReleased:
796       if (m_nPressedButtons)
797         m_nPressedButtons--;
798       break;
799     case OperatorGrab:
800       m_hasGrabMouse = true;
801       break;
802     case OperatorUngrab:
803       m_hasGrabMouse = false;
804       break;
805   }
806 
807   if (!m_nPressedButtons && !m_hasGrabMouse && m_hasMouseCaptured) {
808     ::ReleaseCapture();
809     m_hasMouseCaptured = false;
810   }
811   else if ((m_nPressedButtons || m_hasGrabMouse) && !m_hasMouseCaptured) {
812     ::SetCapture(m_hWnd);
813     m_hasMouseCaptured = true;
814   }
815 }
816 
getMousePressed() const817 bool GHOST_WindowWin32::getMousePressed() const
818 {
819   return m_nPressedButtons;
820 }
821 
getStandardCursor(GHOST_TStandardCursor shape) const822 HCURSOR GHOST_WindowWin32::getStandardCursor(GHOST_TStandardCursor shape) const
823 {
824   // Convert GHOST cursor to Windows OEM cursor
825   HANDLE cursor = NULL;
826   HMODULE module = ::GetModuleHandle(0);
827   GHOST_TUns32 flags = LR_SHARED | LR_DEFAULTSIZE;
828   int cx = 0, cy = 0;
829 
830   switch (shape) {
831     case GHOST_kStandardCursorCustom:
832       if (m_customCursor) {
833         return m_customCursor;
834       }
835       else {
836         return NULL;
837       }
838     case GHOST_kStandardCursorRightArrow:
839       cursor = ::LoadImage(module, "arrowright_cursor", IMAGE_CURSOR, cx, cy, flags);
840       break;
841     case GHOST_kStandardCursorLeftArrow:
842       cursor = ::LoadImage(module, "arrowleft_cursor", IMAGE_CURSOR, cx, cy, flags);
843       break;
844     case GHOST_kStandardCursorUpArrow:
845       cursor = ::LoadImage(module, "arrowup_cursor", IMAGE_CURSOR, cx, cy, flags);
846       break;
847     case GHOST_kStandardCursorDownArrow:
848       cursor = ::LoadImage(module, "arrowdown_cursor", IMAGE_CURSOR, cx, cy, flags);
849       break;
850     case GHOST_kStandardCursorVerticalSplit:
851       cursor = ::LoadImage(module, "splitv_cursor", IMAGE_CURSOR, cx, cy, flags);
852       break;
853     case GHOST_kStandardCursorHorizontalSplit:
854       cursor = ::LoadImage(module, "splith_cursor", IMAGE_CURSOR, cx, cy, flags);
855       break;
856     case GHOST_kStandardCursorKnife:
857       cursor = ::LoadImage(module, "knife_cursor", IMAGE_CURSOR, cx, cy, flags);
858       break;
859     case GHOST_kStandardCursorEyedropper:
860       cursor = ::LoadImage(module, "eyedropper_cursor", IMAGE_CURSOR, cx, cy, flags);
861       break;
862     case GHOST_kStandardCursorZoomIn:
863       cursor = ::LoadImage(module, "zoomin_cursor", IMAGE_CURSOR, cx, cy, flags);
864       break;
865     case GHOST_kStandardCursorZoomOut:
866       cursor = ::LoadImage(module, "zoomout_cursor", IMAGE_CURSOR, cx, cy, flags);
867       break;
868     case GHOST_kStandardCursorMove:
869       cursor = ::LoadImage(module, "handopen_cursor", IMAGE_CURSOR, cx, cy, flags);
870       break;
871     case GHOST_kStandardCursorNSEWScroll:
872       cursor = ::LoadImage(module, "scrollnsew_cursor", IMAGE_CURSOR, cx, cy, flags);
873       break;
874     case GHOST_kStandardCursorNSScroll:
875       cursor = ::LoadImage(module, "scrollns_cursor", IMAGE_CURSOR, cx, cy, flags);
876       break;
877     case GHOST_kStandardCursorEWScroll:
878       cursor = ::LoadImage(module, "scrollew_cursor", IMAGE_CURSOR, cx, cy, flags);
879       break;
880     case GHOST_kStandardCursorHelp:
881       cursor = ::LoadImage(NULL, IDC_HELP, IMAGE_CURSOR, cx, cy, flags);
882       break;  // Arrow and question mark
883     case GHOST_kStandardCursorWait:
884       cursor = ::LoadImage(NULL, IDC_WAIT, IMAGE_CURSOR, cx, cy, flags);
885       break;  // Hourglass
886     case GHOST_kStandardCursorText:
887       cursor = ::LoadImage(NULL, IDC_IBEAM, IMAGE_CURSOR, cx, cy, flags);
888       break;  // I-beam
889     case GHOST_kStandardCursorCrosshair:
890       cursor = ::LoadImage(module, "cross_cursor", IMAGE_CURSOR, cx, cy, flags);
891       break;  // Standard Cross
892     case GHOST_kStandardCursorCrosshairA:
893       cursor = ::LoadImage(module, "crossA_cursor", IMAGE_CURSOR, cx, cy, flags);
894       break;  // Crosshair A
895     case GHOST_kStandardCursorCrosshairB:
896       cursor = ::LoadImage(module, "crossB_cursor", IMAGE_CURSOR, cx, cy, flags);
897       break;  // Diagonal Crosshair B
898     case GHOST_kStandardCursorCrosshairC:
899       cursor = ::LoadImage(module, "crossC_cursor", IMAGE_CURSOR, cx, cy, flags);
900       break;  // Minimal Crosshair C
901     case GHOST_kStandardCursorBottomSide:
902     case GHOST_kStandardCursorUpDown:
903       cursor = ::LoadImage(module, "movens_cursor", IMAGE_CURSOR, cx, cy, flags);
904       break;  // Double-pointed arrow pointing north and south
905     case GHOST_kStandardCursorLeftSide:
906     case GHOST_kStandardCursorLeftRight:
907       cursor = ::LoadImage(module, "moveew_cursor", IMAGE_CURSOR, cx, cy, flags);
908       break;  // Double-pointed arrow pointing west and east
909     case GHOST_kStandardCursorTopSide:
910       cursor = ::LoadImage(NULL, IDC_UPARROW, IMAGE_CURSOR, cx, cy, flags);
911       break;  // Vertical arrow
912     case GHOST_kStandardCursorTopLeftCorner:
913       cursor = ::LoadImage(NULL, IDC_SIZENWSE, IMAGE_CURSOR, cx, cy, flags);
914       break;
915     case GHOST_kStandardCursorTopRightCorner:
916       cursor = ::LoadImage(NULL, IDC_SIZENESW, IMAGE_CURSOR, cx, cy, flags);
917       break;
918     case GHOST_kStandardCursorBottomRightCorner:
919       cursor = ::LoadImage(NULL, IDC_SIZENWSE, IMAGE_CURSOR, cx, cy, flags);
920       break;
921     case GHOST_kStandardCursorBottomLeftCorner:
922       cursor = ::LoadImage(NULL, IDC_SIZENESW, IMAGE_CURSOR, cx, cy, flags);
923       break;
924     case GHOST_kStandardCursorPencil:
925       cursor = ::LoadImage(module, "pencil_cursor", IMAGE_CURSOR, cx, cy, flags);
926       break;
927     case GHOST_kStandardCursorEraser:
928       cursor = ::LoadImage(module, "eraser_cursor", IMAGE_CURSOR, cx, cy, flags);
929       break;
930     case GHOST_kStandardCursorDestroy:
931     case GHOST_kStandardCursorStop:
932       cursor = ::LoadImage(module, "forbidden_cursor", IMAGE_CURSOR, cx, cy, flags);
933       break;  // Slashed circle
934     case GHOST_kStandardCursorDefault:
935       cursor = NULL;
936       break;
937     default:
938       return NULL;
939   }
940 
941   if (cursor == NULL) {
942     cursor = ::LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, cx, cy, flags);
943   }
944 
945   return (HCURSOR)cursor;
946 }
947 
loadCursor(bool visible,GHOST_TStandardCursor shape) const948 void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor shape) const
949 {
950   if (!visible) {
951     while (::ShowCursor(FALSE) >= 0)
952       ;
953   }
954   else {
955     while (::ShowCursor(TRUE) < 0)
956       ;
957   }
958 
959   HCURSOR cursor = getStandardCursor(shape);
960   if (cursor == NULL) {
961     cursor = getStandardCursor(GHOST_kStandardCursorDefault);
962   }
963   ::SetCursor(cursor);
964 }
965 
setWindowCursorVisibility(bool visible)966 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible)
967 {
968   if (::GetForegroundWindow() == m_hWnd) {
969     loadCursor(visible, getCursorShape());
970   }
971 
972   return GHOST_kSuccess;
973 }
974 
setWindowCursorGrab(GHOST_TGrabCursorMode mode)975 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
976 {
977   if (mode != GHOST_kGrabDisable) {
978     if (mode != GHOST_kGrabNormal) {
979       m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
980       setCursorGrabAccum(0, 0);
981 
982       if (mode == GHOST_kGrabHide)
983         setWindowCursorVisibility(false);
984     }
985     updateMouseCapture(OperatorGrab);
986   }
987   else {
988     if (m_cursorGrab == GHOST_kGrabHide) {
989       m_system->setCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
990       setWindowCursorVisibility(true);
991     }
992     if (m_cursorGrab != GHOST_kGrabNormal) {
993       /* use to generate a mouse move event, otherwise the last event
994        * blender gets can be outside the screen causing menus not to show
995        * properly unless the user moves the mouse */
996       GHOST_TInt32 pos[2];
997       m_system->getCursorPosition(pos[0], pos[1]);
998       m_system->setCursorPosition(pos[0], pos[1]);
999     }
1000 
1001     /* Almost works without but important otherwise the mouse GHOST location
1002      * can be incorrect on exit. */
1003     setCursorGrabAccum(0, 0);
1004     m_cursorGrabBounds.m_l = m_cursorGrabBounds.m_r = -1; /* disable */
1005     updateMouseCapture(OperatorUngrab);
1006   }
1007 
1008   return GHOST_kSuccess;
1009 }
1010 
setWindowCursorShape(GHOST_TStandardCursor cursorShape)1011 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
1012 {
1013   if (::GetForegroundWindow() == m_hWnd) {
1014     loadCursor(getCursorVisibility(), cursorShape);
1015   }
1016 
1017   return GHOST_kSuccess;
1018 }
1019 
hasCursorShape(GHOST_TStandardCursor cursorShape)1020 GHOST_TSuccess GHOST_WindowWin32::hasCursorShape(GHOST_TStandardCursor cursorShape)
1021 {
1022   return (getStandardCursor(cursorShape)) ? GHOST_kSuccess : GHOST_kFailure;
1023 }
1024 
getPointerInfo(std::vector<GHOST_PointerInfoWin32> & outPointerInfo,WPARAM wParam,LPARAM lParam)1025 GHOST_TSuccess GHOST_WindowWin32::getPointerInfo(
1026     std::vector<GHOST_PointerInfoWin32> &outPointerInfo, WPARAM wParam, LPARAM lParam)
1027 {
1028   if (!useTabletAPI(GHOST_kTabletNative)) {
1029     return GHOST_kFailure;
1030   }
1031 
1032   GHOST_TInt32 pointerId = GET_POINTERID_WPARAM(wParam);
1033   GHOST_TInt32 isPrimary = IS_POINTER_PRIMARY_WPARAM(wParam);
1034   GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem();
1035   GHOST_TUns32 outCount;
1036 
1037   if (!(m_fpGetPointerInfoHistory && m_fpGetPointerInfoHistory(pointerId, &outCount, NULL))) {
1038     return GHOST_kFailure;
1039   }
1040 
1041   auto pointerPenInfo = std::vector<POINTER_PEN_INFO>(outCount);
1042   outPointerInfo.resize(outCount);
1043 
1044   if (!(m_fpGetPointerPenInfoHistory &&
1045         m_fpGetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) {
1046     return GHOST_kFailure;
1047   }
1048 
1049   for (GHOST_TUns32 i = 0; i < outCount; i++) {
1050     POINTER_INFO pointerApiInfo = pointerPenInfo[i].pointerInfo;
1051     // Obtain the basic information from the event
1052     outPointerInfo[i].pointerId = pointerId;
1053     outPointerInfo[i].isPrimary = isPrimary;
1054 
1055     switch (pointerApiInfo.ButtonChangeType) {
1056       case POINTER_CHANGE_FIRSTBUTTON_DOWN:
1057       case POINTER_CHANGE_FIRSTBUTTON_UP:
1058         outPointerInfo[i].buttonMask = GHOST_kButtonMaskLeft;
1059         break;
1060       case POINTER_CHANGE_SECONDBUTTON_DOWN:
1061       case POINTER_CHANGE_SECONDBUTTON_UP:
1062         outPointerInfo[i].buttonMask = GHOST_kButtonMaskRight;
1063         break;
1064       case POINTER_CHANGE_THIRDBUTTON_DOWN:
1065       case POINTER_CHANGE_THIRDBUTTON_UP:
1066         outPointerInfo[i].buttonMask = GHOST_kButtonMaskMiddle;
1067         break;
1068       case POINTER_CHANGE_FOURTHBUTTON_DOWN:
1069       case POINTER_CHANGE_FOURTHBUTTON_UP:
1070         outPointerInfo[i].buttonMask = GHOST_kButtonMaskButton4;
1071         break;
1072       case POINTER_CHANGE_FIFTHBUTTON_DOWN:
1073       case POINTER_CHANGE_FIFTHBUTTON_UP:
1074         outPointerInfo[i].buttonMask = GHOST_kButtonMaskButton5;
1075         break;
1076       default:
1077         break;
1078     }
1079 
1080     outPointerInfo[i].pixelLocation = pointerApiInfo.ptPixelLocation;
1081     outPointerInfo[i].tabletData.Active = GHOST_kTabletModeStylus;
1082     outPointerInfo[i].tabletData.Pressure = 1.0f;
1083     outPointerInfo[i].tabletData.Xtilt = 0.0f;
1084     outPointerInfo[i].tabletData.Ytilt = 0.0f;
1085     outPointerInfo[i].time = system->performanceCounterToMillis(pointerApiInfo.PerformanceCount);
1086 
1087     if (pointerPenInfo[i].penMask & PEN_MASK_PRESSURE) {
1088       outPointerInfo[i].tabletData.Pressure = pointerPenInfo[i].pressure / 1024.0f;
1089     }
1090 
1091     if (pointerPenInfo[i].penFlags & PEN_FLAG_ERASER) {
1092       outPointerInfo[i].tabletData.Active = GHOST_kTabletModeEraser;
1093     }
1094 
1095     if (pointerPenInfo[i].penMask & PEN_MASK_TILT_X) {
1096       outPointerInfo[i].tabletData.Xtilt = fmin(fabs(pointerPenInfo[i].tiltX / 90.0f), 1.0f);
1097     }
1098 
1099     if (pointerPenInfo[i].penMask & PEN_MASK_TILT_Y) {
1100       outPointerInfo[i].tabletData.Ytilt = fmin(fabs(pointerPenInfo[i].tiltY / 90.0f), 1.0f);
1101     }
1102   }
1103 
1104   return GHOST_kSuccess;
1105 }
1106 
setTabletData(GHOST_TabletData * pTabletData)1107 void GHOST_WindowWin32::setTabletData(GHOST_TabletData *pTabletData)
1108 {
1109   if (pTabletData) {
1110     m_tabletData = *pTabletData;
1111   }
1112   else {
1113     m_tabletData = GHOST_TABLET_DATA_NONE;
1114   }
1115 }
1116 
processWin32TabletActivateEvent(WORD state)1117 void GHOST_WindowWin32::processWin32TabletActivateEvent(WORD state)
1118 {
1119   if (!useTabletAPI(GHOST_kTabletWintab)) {
1120     return;
1121   }
1122 
1123   if (m_wintab.enable && m_wintab.tablet) {
1124     m_wintab.enable(m_wintab.tablet, state);
1125 
1126     if (m_wintab.overlap && state) {
1127       m_wintab.overlap(m_wintab.tablet, TRUE);
1128     }
1129   }
1130 }
1131 
useTabletAPI(GHOST_TTabletAPI api) const1132 bool GHOST_WindowWin32::useTabletAPI(GHOST_TTabletAPI api) const
1133 {
1134   if (m_system->getTabletAPI() == api) {
1135     return true;
1136   }
1137   else if (m_system->getTabletAPI() == GHOST_kTabletAutomatic) {
1138     if (m_wintab.tablet)
1139       return api == GHOST_kTabletWintab;
1140     else
1141       return api == GHOST_kTabletNative;
1142   }
1143   else {
1144     return false;
1145   }
1146 }
1147 
processWin32TabletInitEvent()1148 void GHOST_WindowWin32::processWin32TabletInitEvent()
1149 {
1150   if (!useTabletAPI(GHOST_kTabletWintab)) {
1151     return;
1152   }
1153 
1154   // Let's see if we can initialize tablet here
1155   if (m_wintab.info && m_wintab.tablet) {
1156     AXIS Pressure, Orientation[3]; /* The maximum tablet size */
1157 
1158     BOOL pressureSupport = m_wintab.info(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
1159     if (pressureSupport)
1160       m_wintab.maxPressure = Pressure.axMax;
1161     else
1162       m_wintab.maxPressure = 0;
1163 
1164     BOOL tiltSupport = m_wintab.info(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
1165     if (tiltSupport) {
1166       /* does the tablet support azimuth ([0]) and altitude ([1]) */
1167       if (Orientation[0].axResolution && Orientation[1].axResolution) {
1168         m_wintab.maxAzimuth = Orientation[0].axMax;
1169         m_wintab.maxAltitude = Orientation[1].axMax;
1170       }
1171       else { /* no so dont do tilt stuff */
1172         m_wintab.maxAzimuth = m_wintab.maxAltitude = 0;
1173       }
1174     }
1175   }
1176 
1177   m_tabletData.Active = GHOST_kTabletModeNone;
1178 }
1179 
processWin32TabletEvent(WPARAM wParam,LPARAM lParam)1180 void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam)
1181 {
1182   if (!useTabletAPI(GHOST_kTabletWintab)) {
1183     return;
1184   }
1185 
1186   if (m_wintab.packet && m_wintab.tablet) {
1187     PACKET pkt;
1188     if (m_wintab.packet((HCTX)lParam, wParam, &pkt)) {
1189       switch (pkt.pkCursor % 3) { /* % 3 for multiple devices ("DualTrack") */
1190         case 0:
1191           m_tabletData.Active = GHOST_kTabletModeNone; /* puck - not yet supported */
1192           break;
1193         case 1:
1194           m_tabletData.Active = GHOST_kTabletModeStylus; /* stylus */
1195           break;
1196         case 2:
1197           m_tabletData.Active = GHOST_kTabletModeEraser; /* eraser */
1198           break;
1199       }
1200 
1201       if (m_wintab.maxPressure > 0) {
1202         m_tabletData.Pressure = (float)pkt.pkNormalPressure / (float)m_wintab.maxPressure;
1203       }
1204       else {
1205         m_tabletData.Pressure = 1.0f;
1206       }
1207 
1208       if ((m_wintab.maxAzimuth > 0) && (m_wintab.maxAltitude > 0)) {
1209         ORIENTATION ort = pkt.pkOrientation;
1210         float vecLen;
1211         float altRad, azmRad; /* in radians */
1212 
1213         /*
1214          * from the wintab spec:
1215          * orAzimuth    Specifies the clockwise rotation of the
1216          * cursor about the z axis through a full circular range.
1217          *
1218          * orAltitude   Specifies the angle with the x-y plane
1219          * through a signed, semicircular range.  Positive values
1220          * specify an angle upward toward the positive z axis;
1221          * negative values specify an angle downward toward the negative z axis.
1222          *
1223          * wintab.h defines .orAltitude as a UINT but documents .orAltitude
1224          * as positive for upward angles and negative for downward angles.
1225          * WACOM uses negative altitude values to show that the pen is inverted;
1226          * therefore we cast .orAltitude as an (int) and then use the absolute value.
1227          */
1228 
1229         /* convert raw fixed point data to radians */
1230         altRad = (float)((fabs((float)ort.orAltitude) / (float)m_wintab.maxAltitude) * M_PI / 2.0);
1231         azmRad = (float)(((float)ort.orAzimuth / (float)m_wintab.maxAzimuth) * M_PI * 2.0);
1232 
1233         /* find length of the stylus' projected vector on the XY plane */
1234         vecLen = cos(altRad);
1235 
1236         /* from there calculate X and Y components based on azimuth */
1237         m_tabletData.Xtilt = sin(azmRad) * vecLen;
1238         m_tabletData.Ytilt = (float)(sin(M_PI / 2.0 - azmRad) * vecLen);
1239       }
1240       else {
1241         m_tabletData.Xtilt = 0.0f;
1242         m_tabletData.Ytilt = 0.0f;
1243       }
1244     }
1245   }
1246 }
1247 
bringTabletContextToFront()1248 void GHOST_WindowWin32::bringTabletContextToFront()
1249 {
1250   if (!useTabletAPI(GHOST_kTabletWintab)) {
1251     return;
1252   }
1253 
1254   if (m_wintab.overlap && m_wintab.tablet) {
1255     m_wintab.overlap(m_wintab.tablet, TRUE);
1256   }
1257 }
1258 
getDPIHint()1259 GHOST_TUns16 GHOST_WindowWin32::getDPIHint()
1260 {
1261   if (m_user32) {
1262     GHOST_WIN32_GetDpiForWindow fpGetDpiForWindow = (GHOST_WIN32_GetDpiForWindow)::GetProcAddress(
1263         m_user32, "GetDpiForWindow");
1264 
1265     if (fpGetDpiForWindow) {
1266       return fpGetDpiForWindow(this->m_hWnd);
1267     }
1268   }
1269 
1270   return USER_DEFAULT_SCREEN_DPI;
1271 }
1272 
1273 /** Reverse the bits in a GHOST_TUns8 */
uns8ReverseBits(GHOST_TUns8 ch)1274 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
1275 {
1276   ch = ((ch >> 1) & 0x55) | ((ch << 1) & 0xAA);
1277   ch = ((ch >> 2) & 0x33) | ((ch << 2) & 0xCC);
1278   ch = ((ch >> 4) & 0x0F) | ((ch << 4) & 0xF0);
1279   return ch;
1280 }
1281 
1282 #if 0 /* UNUSED */
1283 /** Reverse the bits in a GHOST_TUns16 */
1284 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
1285 {
1286   shrt = ((shrt >> 1) & 0x5555) | ((shrt << 1) & 0xAAAA);
1287   shrt = ((shrt >> 2) & 0x3333) | ((shrt << 2) & 0xCCCC);
1288   shrt = ((shrt >> 4) & 0x0F0F) | ((shrt << 4) & 0xF0F0);
1289   shrt = ((shrt >> 8) & 0x00FF) | ((shrt << 8) & 0xFF00);
1290   return shrt;
1291 }
1292 #endif
1293 
setWindowCustomCursorShape(GHOST_TUns8 * bitmap,GHOST_TUns8 * mask,int sizeX,int sizeY,int hotX,int hotY,bool canInvertColor)1294 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 *bitmap,
1295                                                              GHOST_TUns8 *mask,
1296                                                              int sizeX,
1297                                                              int sizeY,
1298                                                              int hotX,
1299                                                              int hotY,
1300                                                              bool canInvertColor)
1301 {
1302   GHOST_TUns32 andData[32];
1303   GHOST_TUns32 xorData[32];
1304   GHOST_TUns32 fullBitRow, fullMaskRow;
1305   int x, y, cols;
1306 
1307   cols = sizeX / 8; /* Num of whole bytes per row (width of bm/mask) */
1308   if (sizeX % 8)
1309     cols++;
1310 
1311   if (m_customCursor) {
1312     DestroyCursor(m_customCursor);
1313     m_customCursor = NULL;
1314   }
1315 
1316   memset(&andData, 0xFF, sizeof(andData));
1317   memset(&xorData, 0, sizeof(xorData));
1318 
1319   for (y = 0; y < sizeY; y++) {
1320     fullBitRow = 0;
1321     fullMaskRow = 0;
1322     for (x = cols - 1; x >= 0; x--) {
1323       fullBitRow <<= 8;
1324       fullMaskRow <<= 8;
1325       fullBitRow |= uns8ReverseBits(bitmap[cols * y + x]);
1326       fullMaskRow |= uns8ReverseBits(mask[cols * y + x]);
1327     }
1328     xorData[y] = fullBitRow & fullMaskRow;
1329     andData[y] = ~fullMaskRow;
1330   }
1331 
1332   m_customCursor = ::CreateCursor(::GetModuleHandle(0), hotX, hotY, 32, 32, andData, xorData);
1333   if (!m_customCursor) {
1334     return GHOST_kFailure;
1335   }
1336 
1337   if (::GetForegroundWindow() == m_hWnd) {
1338     loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
1339   }
1340 
1341   return GHOST_kSuccess;
1342 }
1343 
setProgressBar(float progress)1344 GHOST_TSuccess GHOST_WindowWin32::setProgressBar(float progress)
1345 {
1346   /*SetProgressValue sets state to TBPF_NORMAL automaticly*/
1347   if (m_Bar && S_OK == m_Bar->SetProgressValue(m_hWnd, 10000 * progress, 10000))
1348     return GHOST_kSuccess;
1349 
1350   return GHOST_kFailure;
1351 }
1352 
endProgressBar()1353 GHOST_TSuccess GHOST_WindowWin32::endProgressBar()
1354 {
1355   if (m_Bar && S_OK == m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS))
1356     return GHOST_kSuccess;
1357 
1358   return GHOST_kFailure;
1359 }
1360 
1361 #ifdef WITH_INPUT_IME
beginIME(GHOST_TInt32 x,GHOST_TInt32 y,GHOST_TInt32 w,GHOST_TInt32 h,int completed)1362 void GHOST_WindowWin32::beginIME(
1363     GHOST_TInt32 x, GHOST_TInt32 y, GHOST_TInt32 w, GHOST_TInt32 h, int completed)
1364 {
1365   m_imeInput.BeginIME(m_hWnd, GHOST_Rect(x, y - h, x, y), (bool)completed);
1366 }
1367 
endIME()1368 void GHOST_WindowWin32::endIME()
1369 {
1370   m_imeInput.EndIME(m_hWnd);
1371 }
1372 #endif /* WITH_INPUT_IME */
1373