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