Blender  V3.3
GHOST_SystemWin32.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
3 
8 #include "GHOST_SystemWin32.h"
9 #include "GHOST_ContextD3D.h"
10 #include "GHOST_EventDragnDrop.h"
11 #include "GHOST_EventTrackpad.h"
12 
13 #ifndef _WIN32_IE
14 # define _WIN32_IE 0x0501 /* shipped before XP, so doesn't impose additional requirements */
15 #endif
16 
17 #include <commctrl.h>
18 #include <dwmapi.h>
19 #include <psapi.h>
20 #include <shellapi.h>
21 #include <shellscalingapi.h>
22 #include <shlobj.h>
23 #include <tlhelp32.h>
24 #include <windowsx.h>
25 
26 #include "utf_winfunc.h"
27 #include "utfconv.h"
28 
30 #include "GHOST_EventButton.h"
31 #include "GHOST_EventCursor.h"
32 #include "GHOST_EventKey.h"
33 #include "GHOST_EventWheel.h"
34 #include "GHOST_TimerManager.h"
35 #include "GHOST_TimerTask.h"
36 #include "GHOST_WindowManager.h"
37 #include "GHOST_WindowWin32.h"
38 
39 #include "GHOST_ContextWGL.h"
40 
41 #ifdef WITH_INPUT_NDOF
42 # include "GHOST_NDOFManagerWin32.h"
43 #endif
44 
45 // Key code values not found in winuser.h
46 #ifndef VK_MINUS
47 # define VK_MINUS 0xBD
48 #endif // VK_MINUS
49 #ifndef VK_SEMICOLON
50 # define VK_SEMICOLON 0xBA
51 #endif // VK_SEMICOLON
52 #ifndef VK_PERIOD
53 # define VK_PERIOD 0xBE
54 #endif // VK_PERIOD
55 #ifndef VK_COMMA
56 # define VK_COMMA 0xBC
57 #endif // VK_COMMA
58 #ifndef VK_BACK_QUOTE
59 # define VK_BACK_QUOTE 0xC0
60 #endif // VK_BACK_QUOTE
61 #ifndef VK_SLASH
62 # define VK_SLASH 0xBF
63 #endif // VK_SLASH
64 #ifndef VK_BACK_SLASH
65 # define VK_BACK_SLASH 0xDC
66 #endif // VK_BACK_SLASH
67 #ifndef VK_EQUALS
68 # define VK_EQUALS 0xBB
69 #endif // VK_EQUALS
70 #ifndef VK_OPEN_BRACKET
71 # define VK_OPEN_BRACKET 0xDB
72 #endif // VK_OPEN_BRACKET
73 #ifndef VK_CLOSE_BRACKET
74 # define VK_CLOSE_BRACKET 0xDD
75 #endif // VK_CLOSE_BRACKET
76 #ifndef VK_GR_LESS
77 # define VK_GR_LESS 0xE2
78 #endif // VK_GR_LESS
79 
80 /* Workaround for some laptop touchpads, some of which seems to
81  * have driver issues which makes it so window function receives
82  * the message, but PeekMessage doesn't pick those messages for
83  * some reason.
84  *
85  * We send a dummy WM_USER message to force PeekMessage to receive
86  * something, making it so blender's window manager sees the new
87  * messages coming in.
88  */
89 #define BROKEN_PEEK_TOUCHPAD
90 
91 static bool isStartedFromCommandPrompt();
92 
93 static void initRawInput()
94 {
95 #ifdef WITH_INPUT_NDOF
96 # define DEVICE_COUNT 2
97 #else
98 # define DEVICE_COUNT 1
99 #endif
100 
101  RAWINPUTDEVICE devices[DEVICE_COUNT];
102  memset(devices, 0, DEVICE_COUNT * sizeof(RAWINPUTDEVICE));
103 
104  // Initiates WM_INPUT messages from keyboard
105  // That way GHOST can retrieve true keys
106  devices[0].usUsagePage = 0x01;
107  devices[0].usUsage = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */
108 
109 #ifdef WITH_INPUT_NDOF
110  // multi-axis mouse (SpaceNavigator, etc.)
111  devices[1].usUsagePage = 0x01;
112  devices[1].usUsage = 0x08;
113 #endif
114 
115  if (RegisterRawInputDevices(devices, DEVICE_COUNT, sizeof(RAWINPUTDEVICE)))
116  ; // yay!
117  else
118  GHOST_PRINTF("could not register for RawInput: %d\n", (int)GetLastError());
119 
120 #undef DEVICE_COUNT
121 }
122 
123 typedef BOOL(API *GHOST_WIN32_EnableNonClientDpiScaling)(HWND);
124 
126  : m_hasPerformanceCounter(false), m_freq(0), m_start(0), m_lfstart(0)
127 {
129  GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::GHOST_SystemWin32(): m_displayManager==0\n");
131 
132  m_consoleStatus = 1;
133 
134  // Tell Windows we are per monitor DPI aware. This disables the default
135  // blurry scaling and enables WM_DPICHANGED to allow us to draw at proper DPI.
136  SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
137 
138  // Check if current keyboard layout uses AltGr and save keylayout ID for
139  // specialized handling if keys like VK_OEM_*. I.e. french keylayout
140  // generates VK_OEM_8 for their exclamation key (key left of right shift)
141  this->handleKeyboardChange();
142  // Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32.
143  OleInitialize(0);
144 
145 #ifdef WITH_INPUT_NDOF
146  m_ndofManager = new GHOST_NDOFManagerWin32(*this);
147 #endif
148 }
149 
151 {
152  // Shutdown COM
153  OleUninitialize();
154 
157  }
158 }
159 
161 {
162  // Calculate the time passed since system initialization.
163  __int64 delta = (perf_ticks - m_start) * 1000;
164 
165  uint64_t t = (uint64_t)(delta / m_freq);
166  return t;
167 }
168 
170 {
171  return ticks - m_lfstart;
172 }
173 
175 {
176  // Hardware does not support high resolution timers. We will use GetTickCount instead then.
178  return tickCountToMillis(::GetTickCount());
179  }
180 
181  // Retrieve current count
182  __int64 count = 0;
183  ::QueryPerformanceCounter((LARGE_INTEGER *)&count);
184 
186 }
187 
189 {
190  GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::getNumDisplays(): m_displayManager==0\n");
191  uint8_t numDisplays;
192  m_displayManager->getNumDisplays(numDisplays);
193  return numDisplays;
194 }
195 
197 {
198  width = ::GetSystemMetrics(SM_CXSCREEN);
199  height = ::GetSystemMetrics(SM_CYSCREEN);
200 }
201 
203 {
204  width = ::GetSystemMetrics(SM_CXVIRTUALSCREEN);
205  height = ::GetSystemMetrics(SM_CYVIRTUALSCREEN);
206 }
207 
209  int32_t left,
210  int32_t top,
211  uint32_t width,
215  GHOST_GLSettings glSettings,
216  const bool exclusive,
217  const bool is_dialog,
218  const GHOST_IWindow *parentWindow)
219 {
220  GHOST_WindowWin32 *window = new GHOST_WindowWin32(
221  this,
222  title,
223  left,
224  top,
225  width,
226  height,
227  state,
228  type,
229  ((glSettings.flags & GHOST_glStereoVisual) != 0),
230  ((glSettings.flags & GHOST_glAlphaBackground) != 0),
231  (GHOST_WindowWin32 *)parentWindow,
232  ((glSettings.flags & GHOST_glDebugContext) != 0),
233  is_dialog);
234 
235  if (window->getValid()) {
236  // Store the pointer to the window
237  m_windowManager->addWindow(window);
239  }
240  else {
241  GHOST_PRINT("GHOST_SystemWin32::createWindow(): window invalid\n");
242  delete window;
243  window = NULL;
244  }
245 
246  return window;
247 }
248 
255 {
256  const bool debug_context = (glSettings.flags & GHOST_glDebugContext) != 0;
257 
259 
260  HWND wnd = CreateWindowA("STATIC",
261  "BlenderGLEW",
262  WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
263  0,
264  0,
265  64,
266  64,
267  NULL,
268  NULL,
269  GetModuleHandle(NULL),
270  NULL);
271 
272  HDC mHDC = GetDC(wnd);
273  HDC prev_hdc = wglGetCurrentDC();
274  HGLRC prev_context = wglGetCurrentContext();
275 #if defined(WITH_GL_PROFILE_CORE)
276  for (int minor = 5; minor >= 0; --minor) {
277  context = new GHOST_ContextWGL(false,
278  true,
279  wnd,
280  mHDC,
281  WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
282  4,
283  minor,
284  (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
286 
287  if (context->initializeDrawingContext()) {
288  goto finished;
289  }
290  else {
291  delete context;
292  }
293  }
294 
295  context = new GHOST_ContextWGL(false,
296  true,
297  wnd,
298  mHDC,
299  WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
300  3,
301  3,
302  (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
304 
305  if (context->initializeDrawingContext()) {
306  goto finished;
307  }
308  else {
309  delete context;
310  return NULL;
311  }
312 
313 #elif defined(WITH_GL_PROFILE_COMPAT)
314  // ask for 2.1 context, driver gives any GL version >= 2.1
315  // (hopefully the latest compatibility profile)
316  // 2.1 ignores the profile bit & is incompatible with core profile
317  context = new GHOST_ContextWGL(false,
318  true,
319  NULL,
320  NULL,
321  0, // no profile bit
322  2,
323  1,
324  (debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
326 
327  if (context->initializeDrawingContext()) {
328  return context;
329  }
330  else {
331  delete context;
332  }
333 #else
334 # error // must specify either core or compat at build time
335 #endif
336 finished:
337  wglMakeCurrent(prev_hdc, prev_context);
338  return context;
339 }
340 
347 {
348  delete context;
349 
350  return GHOST_kSuccess;
351 }
352 
359 {
361 
362  HWND wnd = CreateWindowA("STATIC",
363  "Blender XR",
364  WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
365  0,
366  0,
367  64,
368  64,
369  NULL,
370  NULL,
371  GetModuleHandle(NULL),
372  NULL);
373 
374  context = new GHOST_ContextD3D(false, wnd);
375  if (context->initializeDrawingContext() == GHOST_kFailure) {
376  delete context;
377  }
378 
379  return context;
380 }
381 
383 {
384  delete context;
385 
386  return GHOST_kSuccess;
387 }
388 
389 bool GHOST_SystemWin32::processEvents(bool waitForEvent)
390 {
391  MSG msg;
392  bool hasEventHandled = false;
393 
394  do {
395  GHOST_TimerManager *timerMgr = getTimerManager();
396 
397  if (waitForEvent && !::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
398 #if 1
399  ::Sleep(1);
400 #else
401  uint64_t next = timerMgr->nextFireTime();
402  int64_t maxSleep = next - getMilliSeconds();
403 
404  if (next == GHOST_kFireTimeNever) {
405  ::WaitMessage();
406  }
407  else if (maxSleep >= 0.0) {
408  ::SetTimer(NULL, 0, maxSleep, NULL);
409  ::WaitMessage();
410  ::KillTimer(NULL, 0);
411  }
412 #endif
413  }
414 
415  if (timerMgr->fireTimers(getMilliSeconds())) {
416  hasEventHandled = true;
417  }
418 
419  driveTrackpad();
420 
421  // Process all the events waiting for us
422  while (::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) != 0) {
423  // TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data.
424  // Needed for MapVirtualKey or if we ever need to get chars from wm_ime_char or similar.
425  ::TranslateMessage(&msg);
426  ::DispatchMessageW(&msg);
427  hasEventHandled = true;
428  }
429 
430  processTrackpad();
431 
432  /* PeekMessage above is allowed to dispatch messages to the wndproc without us
433  * noticing, so we need to check the event manager here to see if there are
434  * events waiting in the queue.
435  */
436  hasEventHandled |= this->m_eventManager->getNumEvents() > 0;
437 
438  } while (waitForEvent && !hasEventHandled);
439 
440  return hasEventHandled;
441 }
442 
444 {
445  POINT point;
446  if (::GetCursorPos(&point)) {
447  x = point.x;
448  y = point.y;
449  return GHOST_kSuccess;
450  }
451  return GHOST_kFailure;
452 }
453 
455 {
456  if (!::GetActiveWindow())
457  return GHOST_kFailure;
458  return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
459 }
460 
462 {
463  bool down = HIBYTE(::GetKeyState(VK_LSHIFT)) != 0;
464  keys.set(GHOST_kModifierKeyLeftShift, down);
465  down = HIBYTE(::GetKeyState(VK_RSHIFT)) != 0;
466  keys.set(GHOST_kModifierKeyRightShift, down);
467 
468  down = HIBYTE(::GetKeyState(VK_LMENU)) != 0;
469  keys.set(GHOST_kModifierKeyLeftAlt, down);
470  down = HIBYTE(::GetKeyState(VK_RMENU)) != 0;
471  keys.set(GHOST_kModifierKeyRightAlt, down);
472 
473  down = HIBYTE(::GetKeyState(VK_LCONTROL)) != 0;
475  down = HIBYTE(::GetKeyState(VK_RCONTROL)) != 0;
477 
478  bool lwindown = HIBYTE(::GetKeyState(VK_LWIN)) != 0;
479  bool rwindown = HIBYTE(::GetKeyState(VK_RWIN)) != 0;
480  if (lwindown || rwindown)
481  keys.set(GHOST_kModifierKeyOS, true);
482  else
483  keys.set(GHOST_kModifierKeyOS, false);
484  return GHOST_kSuccess;
485 }
486 
488 {
489  /* Check for swapped buttons (left-handed mouse buttons)
490  * GetAsyncKeyState() will give back the state of the physical mouse buttons.
491  */
492  bool swapped = ::GetSystemMetrics(SM_SWAPBUTTON) == TRUE;
493 
494  bool down = HIBYTE(::GetAsyncKeyState(VK_LBUTTON)) != 0;
495  buttons.set(swapped ? GHOST_kButtonMaskRight : GHOST_kButtonMaskLeft, down);
496 
497  down = HIBYTE(::GetAsyncKeyState(VK_MBUTTON)) != 0;
498  buttons.set(GHOST_kButtonMaskMiddle, down);
499 
500  down = HIBYTE(::GetAsyncKeyState(VK_RBUTTON)) != 0;
501  buttons.set(swapped ? GHOST_kButtonMaskLeft : GHOST_kButtonMaskRight, down);
502  return GHOST_kSuccess;
503 }
504 
506 {
508  InitCommonControls();
509 
510  /* Disable scaling on high DPI displays on Vista */
511  SetProcessDPIAware();
512  initRawInput();
513 
514  m_lfstart = ::GetTickCount();
515  // Determine whether this system has a high frequency performance counter. */
516  m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER *)&m_freq) == TRUE;
518  GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer available\n");
519  ::QueryPerformanceCounter((LARGE_INTEGER *)&m_start);
520  }
521  else {
522  GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer not available\n");
523  }
524 
525  if (success) {
526  WNDCLASSW wc = {0};
527  wc.style = CS_HREDRAW | CS_VREDRAW;
528  wc.lpfnWndProc = s_wndProc;
529  wc.cbClsExtra = 0;
530  wc.cbWndExtra = 0;
531  wc.hInstance = ::GetModuleHandle(0);
532  wc.hIcon = ::LoadIcon(wc.hInstance, "APPICON");
533 
534  if (!wc.hIcon) {
535  ::LoadIcon(NULL, IDI_APPLICATION);
536  }
537  wc.hCursor = ::LoadCursor(0, IDC_ARROW);
538  wc.hbrBackground =
539 #ifdef INW32_COMPISITING
540  (HBRUSH)CreateSolidBrush
541 #endif
542  (0x00000000);
543  wc.lpszMenuName = 0;
544  wc.lpszClassName = L"GHOST_WindowClass";
545 
546  // Use RegisterClassEx for setting small icon
547  if (::RegisterClassW(&wc) == 0) {
548  success = GHOST_kFailure;
549  }
550  }
551 
552  return success;
553 }
554 
556 {
557  return GHOST_System::exit();
558 }
559 
561  bool *r_keyDown,
562  bool *r_is_repeated_modifier)
563 {
564  bool is_repeated_modifier = false;
565 
568  GHOST_ModifierKeys modifiers;
569  system->retrieveModifierKeys(modifiers);
570 
571  // RI_KEY_BREAK doesn't work for sticky keys release, so we also
572  // check for the up message
573  unsigned int msg = raw.data.keyboard.Message;
574  *r_keyDown = !(raw.data.keyboard.Flags & RI_KEY_BREAK) && msg != WM_KEYUP && msg != WM_SYSKEYUP;
575 
576  key = this->convertKey(raw.data.keyboard.VKey,
577  raw.data.keyboard.MakeCode,
578  (raw.data.keyboard.Flags & (RI_KEY_E1 | RI_KEY_E0)));
579 
580  // extra handling of modifier keys: don't send repeats out from GHOST
581  if (key >= GHOST_kKeyLeftShift && key <= GHOST_kKeyRightAlt) {
582  bool changed = false;
583  GHOST_TModifierKey modifier;
584  switch (key) {
585  case GHOST_kKeyLeftShift: {
586  changed = (modifiers.get(GHOST_kModifierKeyLeftShift) != *r_keyDown);
587  modifier = GHOST_kModifierKeyLeftShift;
588  break;
589  }
590  case GHOST_kKeyRightShift: {
591  changed = (modifiers.get(GHOST_kModifierKeyRightShift) != *r_keyDown);
592  modifier = GHOST_kModifierKeyRightShift;
593  break;
594  }
595  case GHOST_kKeyLeftControl: {
596  changed = (modifiers.get(GHOST_kModifierKeyLeftControl) != *r_keyDown);
598  break;
599  }
600  case GHOST_kKeyRightControl: {
601  changed = (modifiers.get(GHOST_kModifierKeyRightControl) != *r_keyDown);
603  break;
604  }
605  case GHOST_kKeyLeftAlt: {
606  changed = (modifiers.get(GHOST_kModifierKeyLeftAlt) != *r_keyDown);
607  modifier = GHOST_kModifierKeyLeftAlt;
608  break;
609  }
610  case GHOST_kKeyRightAlt: {
611  changed = (modifiers.get(GHOST_kModifierKeyRightAlt) != *r_keyDown);
612  modifier = GHOST_kModifierKeyRightAlt;
613  break;
614  }
615  default:
616  break;
617  }
618 
619  if (changed) {
620  modifiers.set(modifier, *r_keyDown);
621  system->storeModifierKeys(modifiers);
622  }
623  else {
624  is_repeated_modifier = true;
625  }
626  }
627 
628  *r_is_repeated_modifier = is_repeated_modifier;
629  return key;
630 }
631 
638 GHOST_TKey GHOST_SystemWin32::processSpecialKey(short vKey, short scanCode) const
639 {
641  if (vKey == 0xFF) {
642  /* 0xFF is not a valid virtual key code. */
643  return key;
644  }
645 
646  char ch = (char)MapVirtualKeyA(vKey, MAPVK_VK_TO_CHAR);
647  switch (ch) {
648  case u'\"':
649  case u'\'':
650  key = GHOST_kKeyQuote;
651  break;
652  case u'.':
654  break;
655  case u'/':
656  key = GHOST_kKeySlash;
657  break;
658  case u'`':
659  case u'²':
660  key = GHOST_kKeyAccentGrave;
661  break;
662  default:
663  if (vKey == VK_OEM_7) {
664  key = GHOST_kKeyQuote;
665  }
666  else if (vKey == VK_OEM_8) {
667  if (PRIMARYLANGID(m_langId) == LANG_FRENCH) {
668  /* OEM key; used purely for shortcuts. */
669  key = GHOST_kKeyF13;
670  }
671  }
672  break;
673  }
674 
675  return key;
676 }
677 
678 GHOST_TKey GHOST_SystemWin32::convertKey(short vKey, short scanCode, short extend) const
679 {
680  GHOST_TKey key;
681 
682  if ((vKey >= '0') && (vKey <= '9')) {
683  // VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39)
684  key = (GHOST_TKey)(vKey - '0' + GHOST_kKey0);
685  }
686  else if ((vKey >= 'A') && (vKey <= 'Z')) {
687  // VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A)
688  key = (GHOST_TKey)(vKey - 'A' + GHOST_kKeyA);
689  }
690  else if ((vKey >= VK_F1) && (vKey <= VK_F24)) {
691  key = (GHOST_TKey)(vKey - VK_F1 + GHOST_kKeyF1);
692  }
693  else {
694  switch (vKey) {
695  case VK_RETURN:
696  key = (extend) ? GHOST_kKeyNumpadEnter : GHOST_kKeyEnter;
697  break;
698 
699  case VK_BACK:
700  key = GHOST_kKeyBackSpace;
701  break;
702  case VK_TAB:
703  key = GHOST_kKeyTab;
704  break;
705  case VK_ESCAPE:
706  key = GHOST_kKeyEsc;
707  break;
708  case VK_SPACE:
709  key = GHOST_kKeySpace;
710  break;
711 
712  case VK_INSERT:
713  case VK_NUMPAD0:
714  key = (extend) ? GHOST_kKeyInsert : GHOST_kKeyNumpad0;
715  break;
716  case VK_END:
717  case VK_NUMPAD1:
718  key = (extend) ? GHOST_kKeyEnd : GHOST_kKeyNumpad1;
719  break;
720  case VK_DOWN:
721  case VK_NUMPAD2:
722  key = (extend) ? GHOST_kKeyDownArrow : GHOST_kKeyNumpad2;
723  break;
724  case VK_NEXT:
725  case VK_NUMPAD3:
726  key = (extend) ? GHOST_kKeyDownPage : GHOST_kKeyNumpad3;
727  break;
728  case VK_LEFT:
729  case VK_NUMPAD4:
730  key = (extend) ? GHOST_kKeyLeftArrow : GHOST_kKeyNumpad4;
731  break;
732  case VK_CLEAR:
733  case VK_NUMPAD5:
734  key = (extend) ? GHOST_kKeyUnknown : GHOST_kKeyNumpad5;
735  break;
736  case VK_RIGHT:
737  case VK_NUMPAD6:
738  key = (extend) ? GHOST_kKeyRightArrow : GHOST_kKeyNumpad6;
739  break;
740  case VK_HOME:
741  case VK_NUMPAD7:
742  key = (extend) ? GHOST_kKeyHome : GHOST_kKeyNumpad7;
743  break;
744  case VK_UP:
745  case VK_NUMPAD8:
746  key = (extend) ? GHOST_kKeyUpArrow : GHOST_kKeyNumpad8;
747  break;
748  case VK_PRIOR:
749  case VK_NUMPAD9:
750  key = (extend) ? GHOST_kKeyUpPage : GHOST_kKeyNumpad9;
751  break;
752  case VK_DECIMAL:
753  case VK_DELETE:
754  key = (extend) ? GHOST_kKeyDelete : GHOST_kKeyNumpadPeriod;
755  break;
756 
757  case VK_SNAPSHOT:
758  key = GHOST_kKeyPrintScreen;
759  break;
760  case VK_PAUSE:
761  key = GHOST_kKeyPause;
762  break;
763  case VK_MULTIPLY:
765  break;
766  case VK_SUBTRACT:
767  key = GHOST_kKeyNumpadMinus;
768  break;
769  case VK_DIVIDE:
770  key = GHOST_kKeyNumpadSlash;
771  break;
772  case VK_ADD:
773  key = GHOST_kKeyNumpadPlus;
774  break;
775 
776  case VK_SEMICOLON:
777  key = GHOST_kKeySemicolon;
778  break;
779  case VK_EQUALS:
780  key = GHOST_kKeyEqual;
781  break;
782  case VK_COMMA:
783  key = GHOST_kKeyComma;
784  break;
785  case VK_MINUS:
786  key = GHOST_kKeyMinus;
787  break;
788  case VK_PERIOD:
789  key = GHOST_kKeyPeriod;
790  break;
791  case VK_SLASH:
792  key = GHOST_kKeySlash;
793  break;
794  case VK_BACK_QUOTE:
795  key = GHOST_kKeyAccentGrave;
796  break;
797  case VK_OPEN_BRACKET:
798  key = GHOST_kKeyLeftBracket;
799  break;
800  case VK_BACK_SLASH:
801  key = GHOST_kKeyBackslash;
802  break;
803  case VK_CLOSE_BRACKET:
805  break;
806  case VK_GR_LESS:
807  key = GHOST_kKeyGrLess;
808  break;
809 
810  case VK_SHIFT:
811  /* Check single shift presses */
812  if (scanCode == 0x36) {
813  key = GHOST_kKeyRightShift;
814  }
815  else if (scanCode == 0x2a) {
816  key = GHOST_kKeyLeftShift;
817  }
818  else {
819  /* Must be a combination SHIFT (Left or Right) + a Key
820  * Ignore this as the next message will contain
821  * the desired "Key" */
822  key = GHOST_kKeyUnknown;
823  }
824  break;
825  case VK_CONTROL:
827  break;
828  case VK_MENU:
829  key = (extend) ? GHOST_kKeyRightAlt : GHOST_kKeyLeftAlt;
830  break;
831  case VK_LWIN:
832  case VK_RWIN:
833  key = GHOST_kKeyOS;
834  break;
835  case VK_APPS:
836  key = GHOST_kKeyApp;
837  break;
838  case VK_NUMLOCK:
839  key = GHOST_kKeyNumLock;
840  break;
841  case VK_SCROLL:
842  key = GHOST_kKeyScrollLock;
843  break;
844  case VK_CAPITAL:
845  key = GHOST_kKeyCapsLock;
846  break;
847  case VK_MEDIA_PLAY_PAUSE:
848  key = GHOST_kKeyMediaPlay;
849  break;
850  case VK_MEDIA_STOP:
851  key = GHOST_kKeyMediaStop;
852  break;
853  case VK_MEDIA_PREV_TRACK:
854  key = GHOST_kKeyMediaFirst;
855  break;
856  case VK_MEDIA_NEXT_TRACK:
857  key = GHOST_kKeyMediaLast;
858  break;
859  case VK_OEM_7:
860  case VK_OEM_8:
861  default:
862  key = ((GHOST_SystemWin32 *)getSystem())->processSpecialKey(vKey, scanCode);
863  break;
864  }
865  }
866 
867  return key;
868 }
869 
871  GHOST_WindowWin32 *window,
873 {
875 
876  GHOST_TabletData td = window->getTabletData();
877 
878  /* Move mouse to button event position. */
879  if (window->getTabletData().Active != GHOST_kTabletModeNone) {
880  /* Tablet should be handling in between mouse moves, only move to event position. */
881  DWORD msgPos = ::GetMessagePos();
882  int msgPosX = GET_X_LPARAM(msgPos);
883  int msgPosY = GET_Y_LPARAM(msgPos);
884  system->pushEvent(new GHOST_EventCursor(
885  ::GetMessageTime(), GHOST_kEventCursorMove, window, msgPosX, msgPosY, td));
886 
887  if (type == GHOST_kEventButtonDown) {
888  WINTAB_PRINTF("HWND %p OS button down\n", window->getHWND());
889  }
890  else if (type == GHOST_kEventButtonUp) {
891  WINTAB_PRINTF("HWND %p OS button up\n", window->getHWND());
892  }
893  }
894 
896  return new GHOST_EventButton(system->getMilliSeconds(), type, window, mask, td);
897 }
898 
900 {
901  GHOST_Wintab *wt = window->getWintab();
902  if (!wt) {
903  return;
904  }
905 
907 
908  std::vector<GHOST_WintabInfoWin32> wintabInfo;
909  wt->getInput(wintabInfo);
910 
911  /* Wintab provided coordinates are untrusted until a Wintab and Win32 button down event match.
912  * This is checked on every button down event, and revoked if there is a mismatch. This can
913  * happen when Wintab incorrectly scales cursor position or is in mouse mode.
914  *
915  * If Wintab was never trusted while processing this Win32 event, a fallback Ghost cursor move
916  * event is created at the position of the Win32 WT_PACKET event. */
917  bool mouseMoveHandled;
918  bool useWintabPos;
919  mouseMoveHandled = useWintabPos = wt->trustCoordinates();
920 
921  for (GHOST_WintabInfoWin32 &info : wintabInfo) {
922  switch (info.type) {
923  case GHOST_kEventCursorMove: {
924  if (!useWintabPos) {
925  continue;
926  }
927 
928  wt->mapWintabToSysCoordinates(info.x, info.y, info.x, info.y);
929  system->pushEvent(new GHOST_EventCursor(
930  info.time, GHOST_kEventCursorMove, window, info.x, info.y, info.tabletData));
931 
932  break;
933  }
934  case GHOST_kEventButtonDown: {
935  WINTAB_PRINTF("HWND %p Wintab button down", window->getHWND());
936 
937  UINT message;
938  switch (info.button) {
940  message = WM_LBUTTONDOWN;
941  break;
943  message = WM_RBUTTONDOWN;
944  break;
946  message = WM_MBUTTONDOWN;
947  break;
948  default:
949  continue;
950  }
951 
952  /* Wintab buttons are modal, but the API does not inform us what mode a pressed button is
953  * in. Only issue button events if we can steal an equivalent Win32 button event from the
954  * event queue. */
955  MSG msg;
956  if (PeekMessage(&msg, window->getHWND(), message, message, PM_NOYIELD) &&
957  msg.message != WM_QUIT) {
958 
959  /* Test for Win32/Wintab button down match. */
960  useWintabPos = wt->testCoordinates(msg.pt.x, msg.pt.y, info.x, info.y);
961  if (!useWintabPos) {
962  WINTAB_PRINTF(" ... but associated system button mismatched position\n");
963  continue;
964  }
965 
966  WINTAB_PRINTF(" ... associated to system button\n");
967 
968  /* Steal the Win32 event which was previously peeked. */
969  PeekMessage(&msg, window->getHWND(), message, message, PM_REMOVE | PM_NOYIELD);
970 
971  /* Move cursor to button location, to prevent incorrect cursor position when
972  * transitioning from unsynchronized Win32 to Wintab cursor control. */
973  wt->mapWintabToSysCoordinates(info.x, info.y, info.x, info.y);
974  system->pushEvent(new GHOST_EventCursor(
975  info.time, GHOST_kEventCursorMove, window, info.x, info.y, info.tabletData));
976 
978  system->pushEvent(
979  new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData));
980 
981  mouseMoveHandled = true;
982  break;
983  }
984  else {
985  WINTAB_PRINTF(" ... but no system button\n");
986  }
987  }
988  case GHOST_kEventButtonUp: {
989  WINTAB_PRINTF("HWND %p Wintab button up", window->getHWND());
990  if (!useWintabPos) {
991  WINTAB_PRINTF(" ... but Wintab position isn't trusted\n");
992  continue;
993  }
994 
995  UINT message;
996  switch (info.button) {
998  message = WM_LBUTTONUP;
999  break;
1001  message = WM_RBUTTONUP;
1002  break;
1004  message = WM_MBUTTONUP;
1005  break;
1006  default:
1007  continue;
1008  }
1009 
1010  /* Wintab buttons are modal, but the API does not inform us what mode a pressed button is
1011  * in. Only issue button events if we can steal an equivalent Win32 button event from the
1012  * event queue. */
1013  MSG msg;
1014  if (PeekMessage(&msg, window->getHWND(), message, message, PM_REMOVE | PM_NOYIELD) &&
1015  msg.message != WM_QUIT) {
1016 
1017  WINTAB_PRINTF(" ... associated to system button\n");
1019  system->pushEvent(
1020  new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData));
1021  }
1022  else {
1023  WINTAB_PRINTF(" ... but no system button\n");
1024  }
1025  break;
1026  }
1027  default:
1028  break;
1029  }
1030  }
1031 
1032  /* Fallback cursor movement if Wintab position were never trusted while processing this event. */
1033  if (!mouseMoveHandled) {
1034  DWORD pos = GetMessagePos();
1035  int x = GET_X_LPARAM(pos);
1036  int y = GET_Y_LPARAM(pos);
1038 
1039  system->pushEvent(new GHOST_EventCursor(
1040  system->getMilliSeconds(), GHOST_kEventCursorMove, window, x, y, td));
1041  }
1042 }
1043 
1045  UINT type, GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam, bool &eventHandled)
1046 {
1047  /* Pointer events might fire when changing windows for a device which is set to use Wintab,
1048  * even when Wintab is left enabled but set to the bottom of Wintab overlap order. */
1049  if (!window->usingTabletAPI(GHOST_kTabletWinPointer)) {
1050  return;
1051  }
1052 
1054  std::vector<GHOST_PointerInfoWin32> pointerInfo;
1055 
1056  if (window->getPointerInfo(pointerInfo, wParam, lParam) != GHOST_kSuccess) {
1057  return;
1058  }
1059 
1060  switch (type) {
1061  case WM_POINTERUPDATE:
1062  /* Coalesced pointer events are reverse chronological order, reorder chronologically.
1063  * Only contiguous move events are coalesced. */
1064  for (uint32_t i = pointerInfo.size(); i-- > 0;) {
1065  system->pushEvent(new GHOST_EventCursor(pointerInfo[i].time,
1067  window,
1068  pointerInfo[i].pixelLocation.x,
1069  pointerInfo[i].pixelLocation.y,
1070  pointerInfo[i].tabletData));
1071  }
1072 
1073  /* Leave event unhandled so that system cursor is moved. */
1074 
1075  break;
1076  case WM_POINTERDOWN:
1077  /* Move cursor to point of contact because GHOST_EventButton does not include position. */
1078  system->pushEvent(new GHOST_EventCursor(pointerInfo[0].time,
1080  window,
1081  pointerInfo[0].pixelLocation.x,
1082  pointerInfo[0].pixelLocation.y,
1083  pointerInfo[0].tabletData));
1084  system->pushEvent(new GHOST_EventButton(pointerInfo[0].time,
1086  window,
1087  pointerInfo[0].buttonMask,
1088  pointerInfo[0].tabletData));
1090 
1091  /* Mark event handled so that mouse button events are not generated. */
1092  eventHandled = true;
1093 
1094  break;
1095  case WM_POINTERUP:
1096  system->pushEvent(new GHOST_EventButton(pointerInfo[0].time,
1098  window,
1099  pointerInfo[0].buttonMask,
1100  pointerInfo[0].tabletData));
1102 
1103  /* Mark event handled so that mouse button events are not generated. */
1104  eventHandled = true;
1105 
1106  break;
1107  default:
1108  break;
1109  }
1110 }
1111 
1113 {
1114  int32_t x_screen, y_screen;
1116 
1117  if (window->getTabletData().Active != GHOST_kTabletModeNone) {
1118  /* While pen devices are in range, cursor movement is handled by tablet input processing. */
1119  return NULL;
1120  }
1121 
1122  system->getCursorPosition(x_screen, y_screen);
1123 
1124  if (window->getCursorGrabModeIsWarp()) {
1125  /* WORKAROUND:
1126  * Sometimes Windows ignores `SetCursorPos()` or `SendInput()` calls or the mouse event is
1127  * outdate. Identify these cases by checking if the cursor is not yet within bounds. */
1128  static bool is_warping_x = false;
1129  static bool is_warping_y = false;
1130 
1131  int32_t x_new = x_screen;
1132  int32_t y_new = y_screen;
1133  int32_t x_accum, y_accum;
1135 
1136  /* Fallback to window bounds. */
1137  if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) {
1138  window->getClientBounds(bounds);
1139  }
1140 
1141  /* Could also clamp to screen bounds wrap with a window outside the view will
1142  * fail at the moment. Use inset in case the window is at screen bounds. */
1143  bounds.wrapPoint(x_new, y_new, 2, window->getCursorGrabAxis());
1144 
1145  window->getCursorGrabAccum(x_accum, y_accum);
1146  if (x_new != x_screen || y_new != y_screen) {
1147  system->setCursorPosition(x_new, y_new); /* wrap */
1148 
1149  /* Do not update the accum values if we are an outdated or failed pos-warp event. */
1150  if (!is_warping_x) {
1151  is_warping_x = x_new != x_screen;
1152  if (is_warping_x) {
1153  x_accum += (x_screen - x_new);
1154  }
1155  }
1156 
1157  if (!is_warping_y) {
1158  is_warping_y = y_new != y_screen;
1159  if (is_warping_y) {
1160  y_accum += (y_screen - y_new);
1161  }
1162  }
1163  window->setCursorGrabAccum(x_accum, y_accum);
1164 
1165  /* When wrapping we don't need to add an event because the setCursorPosition call will cause
1166  * a new event after. */
1167  return NULL;
1168  }
1169 
1170  is_warping_x = false;
1171  is_warping_y = false;
1172  x_screen += x_accum;
1173  y_screen += y_accum;
1174  }
1175 
1176  return new GHOST_EventCursor(system->getMilliSeconds(),
1178  window,
1179  x_screen,
1180  y_screen,
1182 }
1183 
1184 void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam)
1185 {
1187 
1188  int acc = system->m_wheelDeltaAccum;
1189  int delta = GET_WHEEL_DELTA_WPARAM(wParam);
1190 
1191  if (acc * delta < 0) {
1192  // scroll direction reversed.
1193  acc = 0;
1194  }
1195  acc += delta;
1196  int direction = (acc >= 0) ? 1 : -1;
1197  acc = abs(acc);
1198 
1199  while (acc >= WHEEL_DELTA) {
1200  system->pushEvent(new GHOST_EventWheel(system->getMilliSeconds(), window, direction));
1201  acc -= WHEEL_DELTA;
1202  }
1203  system->m_wheelDeltaAccum = acc * direction;
1204 }
1205 
1207 {
1208  bool keyDown = false;
1209  bool is_repeated_modifier = false;
1211  GHOST_TKey key = system->hardKey(raw, &keyDown, &is_repeated_modifier);
1212  GHOST_EventKey *event;
1213 
1214  /* We used to check `if (key != GHOST_kKeyUnknown)`, but since the message
1215  * values `WM_SYSKEYUP`, `WM_KEYUP` and `WM_CHAR` are ignored, we capture
1216  * those events here as well. */
1217  if (!is_repeated_modifier) {
1218  char vk = raw.data.keyboard.VKey;
1219  char utf8_char[6] = {0};
1220  char ascii = 0;
1221  bool is_repeat = false;
1222 
1223  /* Unlike on Linux, not all keys can send repeat events. E.g. modifier keys don't. */
1224  if (keyDown) {
1225  if (system->m_keycode_last_repeat_key == vk) {
1226  is_repeat = true;
1227  }
1228  system->m_keycode_last_repeat_key = vk;
1229  }
1230  else {
1231  if (system->m_keycode_last_repeat_key == vk) {
1232  system->m_keycode_last_repeat_key = 0;
1233  }
1234  }
1235 
1236  wchar_t utf16[3] = {0};
1237  BYTE state[256] = {0};
1238  int r;
1239  GetKeyboardState((PBYTE)state);
1240  bool ctrl_pressed = state[VK_CONTROL] & 0x80;
1241  bool alt_pressed = state[VK_MENU] & 0x80;
1242 
1243  /* No text with control key pressed (Alt can be used to insert special characters though!). */
1244  if (ctrl_pressed && !alt_pressed) {
1245  utf8_char[0] = '\0';
1246  }
1247  // Don't call ToUnicodeEx on dead keys as it clears the buffer and so won't allow diacritical
1248  // composition.
1249  else if (MapVirtualKeyW(vk, 2) != 0) {
1250  /* TODO: #ToUnicodeEx can respond with up to 4 utf16 chars (only 2 here).
1251  * Could be up to 24 utf8 bytes. */
1252  if ((r = ToUnicodeEx(
1253  vk, raw.data.keyboard.MakeCode, state, utf16, 2, 0, system->m_keylayout))) {
1254  if ((r > 0 && r < 3)) {
1255  utf16[r] = 0;
1256  conv_utf_16_to_8(utf16, utf8_char, 6);
1257  }
1258  else if (r == -1) {
1259  utf8_char[0] = '\0';
1260  }
1261  }
1262  }
1263 
1264  if (!keyDown) {
1265  utf8_char[0] = '\0';
1266  ascii = '\0';
1267  }
1268  else {
1269  ascii = utf8_char[0] & 0x80 ? '?' : utf8_char[0];
1270  }
1271 
1272 #ifdef WITH_INPUT_IME
1273  if (window->getImeInput()->IsImeKeyEvent(ascii, key)) {
1274  return NULL;
1275  }
1276 #endif /* WITH_INPUT_IME */
1277 
1278  event = new GHOST_EventKey(system->getMilliSeconds(),
1280  window,
1281  key,
1282  is_repeat,
1283  utf8_char);
1284 
1285  // GHOST_PRINTF("%c\n", ascii); // we already get this info via EventPrinter
1286  }
1287  else {
1288  event = NULL;
1289  }
1290 
1291  return event;
1292 }
1293 
1295 {
1297  GHOST_Event *sizeEvent = new GHOST_Event(
1298  system->getMilliSeconds(), GHOST_kEventWindowSize, window);
1299 
1300  /* We get WM_SIZE before we fully init. Do not dispatch before we are continuously resizing. */
1301  if (window->m_inLiveResize) {
1302  system->pushEvent(sizeEvent);
1303  system->dispatchEvents();
1304  return NULL;
1305  }
1306  else {
1307  return sizeEvent;
1308  }
1309 }
1310 
1312  GHOST_WindowWin32 *window)
1313 {
1315 
1317  system->getWindowManager()->setActiveWindow(window);
1318  }
1319 
1320  return new GHOST_Event(system->getMilliSeconds(), type, window);
1321 }
1322 
1323 #ifdef WITH_INPUT_IME
1324 GHOST_Event *GHOST_SystemWin32::processImeEvent(GHOST_TEventType type,
1325  GHOST_WindowWin32 *window,
1327 {
1329  return new GHOST_EventIME(system->getMilliSeconds(), type, window, data);
1330 }
1331 #endif
1332 
1334  GHOST_TDragnDropTypes draggedObjectType,
1335  GHOST_WindowWin32 *window,
1336  int mouseX,
1337  int mouseY,
1338  void *data)
1339 {
1341  return system->pushEvent(new GHOST_EventDragnDrop(
1342  system->getMilliSeconds(), eventType, draggedObjectType, window, mouseX, mouseY, data));
1343 }
1344 
1346 {
1348 
1349  /* If API is set to WinPointer (Windows Ink), unload Wintab so that trouble drivers don't disable
1350  * Windows Ink. Load Wintab when API is Automatic because decision logic relies on knowing
1351  * whether a Wintab device is present. */
1352  const bool loadWintab = GHOST_kTabletWinPointer != api;
1354 
1355  for (GHOST_IWindow *win : wm->getWindows()) {
1356  GHOST_WindowWin32 *windowWin32 = (GHOST_WindowWin32 *)win;
1357  if (loadWintab) {
1358  windowWin32->loadWintab(GHOST_kWindowStateMinimized != windowWin32->getState());
1359 
1360  if (windowWin32->usingTabletAPI(GHOST_kTabletWintab)) {
1361  windowWin32->resetPointerPenInfo();
1362  }
1363  }
1364  else {
1365  windowWin32->closeWintab();
1366  }
1367  }
1368 }
1369 
1371 {
1372  GHOST_System::initDebug(debug);
1374 }
1375 
1377 {
1378  minmax->ptMinTrackSize.x = 320;
1379  minmax->ptMinTrackSize.y = 240;
1380 }
1381 
1382 #ifdef WITH_INPUT_NDOF
1383 bool GHOST_SystemWin32::processNDOF(RAWINPUT const &raw)
1384 {
1385  bool eventSent = false;
1386  uint64_t now = getMilliSeconds();
1387 
1388  static bool firstEvent = true;
1389  if (firstEvent) { // determine exactly which device is plugged in
1390  RID_DEVICE_INFO info;
1391  unsigned infoSize = sizeof(RID_DEVICE_INFO);
1392  info.cbSize = infoSize;
1393 
1394  GetRawInputDeviceInfo(raw.header.hDevice, RIDI_DEVICEINFO, &info, &infoSize);
1395  if (info.dwType == RIM_TYPEHID)
1396  m_ndofManager->setDevice(info.hid.dwVendorId, info.hid.dwProductId);
1397  else
1398  GHOST_PRINT("<!> not a HID device... mouse/kb perhaps?\n");
1399 
1400  firstEvent = false;
1401  }
1402 
1403  // The NDOF manager sends button changes immediately, and *pretends* to
1404  // send motion. Mark as 'sent' so motion will always get dispatched.
1405  eventSent = true;
1406 
1407  BYTE const *data = raw.data.hid.bRawData;
1408 
1409  BYTE packetType = data[0];
1410  switch (packetType) {
1411  case 1: // translation
1412  {
1413  const short *axis = (short *)(data + 1);
1414  // massage into blender view coords (same goes for rotation)
1415  const int t[3] = {axis[0], -axis[2], axis[1]};
1416  m_ndofManager->updateTranslation(t, now);
1417 
1418  if (raw.data.hid.dwSizeHid == 13) {
1419  // this report also includes rotation
1420  const int r[3] = {-axis[3], axis[5], -axis[4]};
1421  m_ndofManager->updateRotation(r, now);
1422 
1423  // I've never gotten one of these, has anyone else?
1424  GHOST_PRINT("ndof: combined T + R\n");
1425  }
1426  break;
1427  }
1428  case 2: // rotation
1429  {
1430  const short *axis = (short *)(data + 1);
1431  const int r[3] = {-axis[0], axis[2], -axis[1]};
1432  m_ndofManager->updateRotation(r, now);
1433  break;
1434  }
1435  case 3: // buttons
1436  {
1437  int button_bits;
1438  memcpy(&button_bits, data + 1, sizeof(button_bits));
1439  m_ndofManager->updateButtons(button_bits, now);
1440  break;
1441  }
1442  }
1443  return eventSent;
1444 }
1445 #endif // WITH_INPUT_NDOF
1446 
1448 {
1449  GHOST_WindowWin32 *active_window = static_cast<GHOST_WindowWin32 *>(
1451  if (active_window) {
1452  active_window->updateDirectManipulation();
1453  }
1454 }
1455 
1457 {
1458  GHOST_WindowWin32 *active_window = static_cast<GHOST_WindowWin32 *>(
1460 
1461  if (!active_window) {
1462  return;
1463  }
1464 
1465  GHOST_TTrackpadInfo trackpad_info = active_window->getTrackpadInfo();
1467 
1468  int32_t cursor_x, cursor_y;
1469  system->getCursorPosition(cursor_x, cursor_y);
1470 
1471  if (trackpad_info.x != 0 || trackpad_info.y != 0) {
1472  system->pushEvent(new GHOST_EventTrackpad(system->getMilliSeconds(),
1473  active_window,
1475  cursor_x,
1476  cursor_y,
1477  trackpad_info.x,
1478  trackpad_info.y,
1479  trackpad_info.isScrollDirectionInverted));
1480  }
1481  if (trackpad_info.scale != 0) {
1482  system->pushEvent(new GHOST_EventTrackpad(system->getMilliSeconds(),
1483  active_window,
1485  cursor_x,
1486  cursor_y,
1487  trackpad_info.scale,
1488  0,
1489  false));
1490  }
1491 }
1492 
1493 LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1494 {
1495  GHOST_Event *event = NULL;
1496  bool eventHandled = false;
1497 
1498  LRESULT lResult = 0;
1500 #ifdef WITH_INPUT_IME
1501  GHOST_EventManager *eventManager = system->getEventManager();
1502 #endif
1503  GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized");
1504 
1505  if (hwnd) {
1506 
1507  if (msg == WM_NCCREATE) {
1508  // Tell Windows to automatically handle scaling of non-client areas
1509  // such as the caption bar. EnableNonClientDpiScaling was introduced in Windows 10
1510  HMODULE m_user32 = ::LoadLibrary("User32.dll");
1511  if (m_user32) {
1512  GHOST_WIN32_EnableNonClientDpiScaling fpEnableNonClientDpiScaling =
1513  (GHOST_WIN32_EnableNonClientDpiScaling)::GetProcAddress(m_user32,
1514  "EnableNonClientDpiScaling");
1515  if (fpEnableNonClientDpiScaling) {
1516  fpEnableNonClientDpiScaling(hwnd);
1517  }
1518  }
1519  }
1520 
1521  GHOST_WindowWin32 *window = (GHOST_WindowWin32 *)::GetWindowLongPtr(hwnd, GWLP_USERDATA);
1522  if (window) {
1523  switch (msg) {
1524  // we need to check if new key layout has AltGr
1525  case WM_INPUTLANGCHANGE: {
1526  system->handleKeyboardChange();
1527 #ifdef WITH_INPUT_IME
1528  window->getImeInput()->UpdateInputLanguage();
1529  window->getImeInput()->UpdateConversionStatus(hwnd);
1530 #endif
1531  break;
1532  }
1534  // Keyboard events, processed
1536  case WM_INPUT: {
1537  RAWINPUT raw;
1538  RAWINPUT *raw_ptr = &raw;
1539  UINT rawSize = sizeof(RAWINPUT);
1540 
1541  GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw_ptr, &rawSize, sizeof(RAWINPUTHEADER));
1542 
1543  switch (raw.header.dwType) {
1544  case RIM_TYPEKEYBOARD:
1545  event = processKeyEvent(window, raw);
1546  if (!event) {
1547  GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ");
1548  GHOST_PRINT(msg);
1549  GHOST_PRINT(" key ignored\n");
1550  }
1551  break;
1552 #ifdef WITH_INPUT_NDOF
1553  case RIM_TYPEHID:
1554  if (system->processNDOF(raw)) {
1555  eventHandled = true;
1556  }
1557  break;
1558 #endif
1559  }
1560  break;
1561  }
1562 #ifdef WITH_INPUT_IME
1564  // IME events, processed, read more in GHOST_IME.h
1566  case WM_IME_NOTIFY: {
1567  /* Update conversion status when IME is changed or input mode is changed. */
1568  if (wParam == IMN_SETOPENSTATUS || wParam == IMN_SETCONVERSIONMODE) {
1569  window->getImeInput()->UpdateConversionStatus(hwnd);
1570  }
1571  break;
1572  }
1573  case WM_IME_SETCONTEXT: {
1574  GHOST_ImeWin32 *ime = window->getImeInput();
1575  ime->UpdateInputLanguage();
1576  ime->UpdateConversionStatus(hwnd);
1577  ime->CreateImeWindow(hwnd);
1578  ime->CleanupComposition(hwnd);
1579  ime->CheckFirst(hwnd);
1580  break;
1581  }
1582  case WM_IME_STARTCOMPOSITION: {
1583  GHOST_ImeWin32 *ime = window->getImeInput();
1584  eventHandled = true;
1585  ime->CreateImeWindow(hwnd);
1586  ime->ResetComposition(hwnd);
1587  event = processImeEvent(GHOST_kEventImeCompositionStart, window, &ime->eventImeData);
1588  break;
1589  }
1590  case WM_IME_COMPOSITION: {
1591  GHOST_ImeWin32 *ime = window->getImeInput();
1592  eventHandled = true;
1593  ime->UpdateImeWindow(hwnd);
1594  ime->UpdateInfo(hwnd);
1595  if (ime->eventImeData.result_len) {
1596  /* remove redundant IME event */
1597  eventManager->removeTypeEvents(GHOST_kEventImeComposition, window);
1598  }
1599  event = processImeEvent(GHOST_kEventImeComposition, window, &ime->eventImeData);
1600  break;
1601  }
1602  case WM_IME_ENDCOMPOSITION: {
1603  GHOST_ImeWin32 *ime = window->getImeInput();
1604  eventHandled = true;
1605  /* remove input event after end comp event, avoid redundant input */
1606  eventManager->removeTypeEvents(GHOST_kEventKeyDown, window);
1607  ime->ResetComposition(hwnd);
1608  ime->DestroyImeWindow(hwnd);
1609  event = processImeEvent(GHOST_kEventImeCompositionEnd, window, &ime->eventImeData);
1610  break;
1611  }
1612 #endif /* WITH_INPUT_IME */
1614  // Keyboard events, ignored
1616  case WM_KEYDOWN:
1617  case WM_SYSKEYDOWN:
1618  case WM_KEYUP:
1619  case WM_SYSKEYUP:
1620  /* These functions were replaced by #WM_INPUT. */
1621  case WM_CHAR:
1622  /* The WM_CHAR message is posted to the window with the keyboard focus when
1623  * a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR
1624  * contains the character code of the key that was pressed.
1625  */
1626  case WM_DEADCHAR:
1627  /* The WM_DEADCHAR message is posted to the window with the keyboard focus when a
1628  * WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR
1629  * specifies a character code generated by a dead key. A dead key is a key that
1630  * generates a character, such as the umlaut (double-dot), that is combined with
1631  * another character to form a composite character. For example, the umlaut-O
1632  * character (Ö) is generated by typing the dead key for the umlaut character, and
1633  * then typing the O key.
1634  */
1635  break;
1636  case WM_SYSDEADCHAR:
1637  /* The WM_SYSDEADCHAR message is sent to the window with the keyboard focus when
1638  * a WM_SYSKEYDOWN message is translated by the TranslateMessage function.
1639  * WM_SYSDEADCHAR specifies the character code of a system dead key - that is,
1640  * a dead key that is pressed while holding down the alt key.
1641  */
1642  case WM_SYSCHAR:
1643  /* The WM_SYSCHAR message is sent to the window with the keyboard focus when
1644  * a WM_SYSCHAR message is translated by the TranslateMessage function.
1645  * WM_SYSCHAR specifies the character code of a dead key - that is,
1646  * a dead key that is pressed while holding down the alt key.
1647  * To prevent the sound, DefWindowProc must be avoided by return
1648  */
1649  break;
1650  case WM_SYSCOMMAND:
1651  /* The WM_SYSCOMMAND message is sent to the window when system commands such as
1652  * maximize, minimize or close the window are triggered. Also it is sent when ALT
1653  * button is press for menu. To prevent this we must return preventing DefWindowProc.
1654  *
1655  * Note that the four low-order bits of the wParam parameter are used internally by the
1656  * OS. To obtain the correct result when testing the value of wParam, an application must
1657  * combine the value 0xFFF0 with the wParam value by using the bit-wise AND operator.
1658  */
1659  switch (wParam & 0xFFF0) {
1660  case SC_KEYMENU:
1661  eventHandled = true;
1662  break;
1663  case SC_RESTORE: {
1664  ::ShowWindow(hwnd, SW_RESTORE);
1665  window->setState(window->getState());
1666 
1667  GHOST_Wintab *wt = window->getWintab();
1668  if (wt) {
1669  wt->enable();
1670  }
1671 
1672  eventHandled = true;
1673  break;
1674  }
1675  case SC_MAXIMIZE: {
1676  GHOST_Wintab *wt = window->getWintab();
1677  if (wt) {
1678  wt->enable();
1679  }
1680  /* Don't report event as handled so that default handling occurs. */
1681  break;
1682  }
1683  case SC_MINIMIZE: {
1684  GHOST_Wintab *wt = window->getWintab();
1685  if (wt) {
1686  wt->disable();
1687  }
1688  /* Don't report event as handled so that default handling occurs. */
1689  break;
1690  }
1691  }
1692  break;
1694  // Wintab events, processed
1696  case WT_CSRCHANGE: {
1697  WINTAB_PRINTF("HWND %p HCTX %p WT_CSRCHANGE\n", window->getHWND(), (void *)lParam);
1698  GHOST_Wintab *wt = window->getWintab();
1699  if (wt) {
1700  wt->updateCursorInfo();
1701  }
1702  eventHandled = true;
1703  break;
1704  }
1705  case WT_PROXIMITY: {
1706  WINTAB_PRINTF("HWND %p HCTX %p WT_PROXIMITY\n", window->getHWND(), (void *)wParam);
1707  if (LOWORD(lParam)) {
1708  WINTAB_PRINTF(" Cursor entering context.\n");
1709  }
1710  else {
1711  WINTAB_PRINTF(" Cursor leaving context.\n");
1712  }
1713  if (HIWORD(lParam)) {
1714  WINTAB_PRINTF(" Cursor entering or leaving hardware proximity.\n");
1715  }
1716  else {
1717  WINTAB_PRINTF(" Cursor neither entering nor leaving hardware proximity.\n");
1718  }
1719 
1720  GHOST_Wintab *wt = window->getWintab();
1721  if (wt) {
1722  bool inRange = LOWORD(lParam);
1723  if (inRange) {
1724  /* Some devices don't emit WT_CSRCHANGE events, so update cursor info here. */
1725  wt->updateCursorInfo();
1726  }
1727  else {
1728  wt->leaveRange();
1729  }
1730  }
1731  eventHandled = true;
1732  break;
1733  }
1734  case WT_INFOCHANGE: {
1735  WINTAB_PRINTF("HWND %p HCTX %p WT_INFOCHANGE\n", window->getHWND(), (void *)wParam);
1736  GHOST_Wintab *wt = window->getWintab();
1737  if (wt) {
1738  wt->processInfoChange(lParam);
1739 
1740  if (window->usingTabletAPI(GHOST_kTabletWintab)) {
1741  window->resetPointerPenInfo();
1742  }
1743  }
1744  eventHandled = true;
1745  break;
1746  }
1747  case WT_PACKET:
1748  processWintabEvent(window);
1749  eventHandled = true;
1750  break;
1752  // Wintab events, debug
1754  case WT_CTXOPEN:
1755  WINTAB_PRINTF("HWND %p HCTX %p WT_CTXOPEN\n", window->getHWND(), (void *)wParam);
1756  break;
1757  case WT_CTXCLOSE:
1758  WINTAB_PRINTF("HWND %p HCTX %p WT_CTXCLOSE\n", window->getHWND(), (void *)wParam);
1759  break;
1760  case WT_CTXUPDATE:
1761  WINTAB_PRINTF("HWND %p HCTX %p WT_CTXUPDATE\n", window->getHWND(), (void *)wParam);
1762  break;
1763  case WT_CTXOVERLAP:
1764  WINTAB_PRINTF("HWND %p HCTX %p WT_CTXOVERLAP", window->getHWND(), (void *)wParam);
1765  switch (lParam) {
1766  case CXS_DISABLED:
1767  WINTAB_PRINTF(" CXS_DISABLED\n");
1768  break;
1769  case CXS_OBSCURED:
1770  WINTAB_PRINTF(" CXS_OBSCURED\n");
1771  break;
1772  case CXS_ONTOP:
1773  WINTAB_PRINTF(" CXS_ONTOP\n");
1774  break;
1775  }
1776  break;
1778  // Pointer events, processed
1780  case WM_POINTERUPDATE:
1781  case WM_POINTERDOWN:
1782  case WM_POINTERUP:
1783  processPointerEvent(msg, window, wParam, lParam, eventHandled);
1784  break;
1785  case WM_POINTERLEAVE: {
1786  uint32_t pointerId = GET_POINTERID_WPARAM(wParam);
1787  POINTER_INFO pointerInfo;
1788  if (!GetPointerInfo(pointerId, &pointerInfo)) {
1789  break;
1790  }
1791 
1792  /* Reset pointer pen info if pen device has left tracking range. */
1793  if (pointerInfo.pointerType == PT_PEN) {
1794  window->resetPointerPenInfo();
1795  eventHandled = true;
1796  }
1797  break;
1798  }
1800  // Mouse events, processed
1802  case WM_LBUTTONDOWN:
1804  break;
1805  case WM_MBUTTONDOWN:
1807  break;
1808  case WM_RBUTTONDOWN:
1810  break;
1811  case WM_XBUTTONDOWN:
1812  if ((short)HIWORD(wParam) == XBUTTON1) {
1814  }
1815  else if ((short)HIWORD(wParam) == XBUTTON2) {
1817  }
1818  break;
1819  case WM_LBUTTONUP:
1821  break;
1822  case WM_MBUTTONUP:
1824  break;
1825  case WM_RBUTTONUP:
1827  break;
1828  case WM_XBUTTONUP:
1829  if ((short)HIWORD(wParam) == XBUTTON1) {
1831  }
1832  else if ((short)HIWORD(wParam) == XBUTTON2) {
1834  }
1835  break;
1836  case WM_MOUSEMOVE:
1837  if (!window->m_mousePresent) {
1838  WINTAB_PRINTF("HWND %p mouse enter\n", window->getHWND());
1839  TRACKMOUSEEVENT tme = {sizeof(tme)};
1840  tme.dwFlags = TME_LEAVE;
1841  tme.hwndTrack = hwnd;
1842  TrackMouseEvent(&tme);
1843  window->m_mousePresent = true;
1844  GHOST_Wintab *wt = window->getWintab();
1845  if (wt) {
1846  wt->gainFocus();
1847  }
1848  }
1849 
1850  event = processCursorEvent(window);
1851 
1852  break;
1853  case WM_MOUSEWHEEL: {
1854  /* The WM_MOUSEWHEEL message is sent to the focus window
1855  * when the mouse wheel is rotated. The DefWindowProc
1856  * function propagates the message to the window's parent.
1857  * There should be no internal forwarding of the message,
1858  * since DefWindowProc propagates it up the parent chain
1859  * until it finds a window that processes it.
1860  */
1861  processWheelEvent(window, wParam, lParam);
1862  eventHandled = true;
1863 #ifdef BROKEN_PEEK_TOUCHPAD
1864  PostMessage(hwnd, WM_USER, 0, 0);
1865 #endif
1866  break;
1867  }
1868  case WM_SETCURSOR:
1869  /* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor
1870  * to move within a window and mouse input is not captured.
1871  * This means we have to set the cursor shape every time the mouse moves!
1872  * The DefWindowProc function uses this message to set the cursor to an
1873  * arrow if it is not in the client area.
1874  */
1875  if (LOWORD(lParam) == HTCLIENT) {
1876  // Load the current cursor
1877  window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
1878  // Bypass call to DefWindowProc
1879  return 0;
1880  }
1881  else {
1882  // Outside of client area show standard cursor
1884  }
1885  break;
1886  case WM_MOUSELEAVE: {
1887  WINTAB_PRINTF("HWND %p mouse leave\n", window->getHWND());
1888  window->m_mousePresent = false;
1889  if (window->getTabletData().Active == GHOST_kTabletModeNone) {
1890  event = processCursorEvent(window);
1891  }
1892  GHOST_Wintab *wt = window->getWintab();
1893  if (wt) {
1894  wt->loseFocus();
1895  }
1896  break;
1897  }
1899  // Mouse events, ignored
1901  case WM_NCMOUSEMOVE:
1902  /* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved
1903  * within the non-client area of the window. This message is posted to the window that
1904  * contains the cursor. If a window has captured the mouse, this message is not posted.
1905  */
1906  case WM_NCHITTEST:
1907  /* The WM_NCHITTEST message is sent to a window when the cursor moves, or
1908  * when a mouse button is pressed or released. If the mouse is not captured,
1909  * the message is sent to the window beneath the cursor. Otherwise, the message
1910  * is sent to the window that has captured the mouse.
1911  */
1912  break;
1913 
1915  // Window events, processed
1917  case WM_CLOSE:
1918  /* The WM_CLOSE message is sent as a signal that a window
1919  * or an application should terminate. Restore if minimized. */
1920  if (IsIconic(hwnd)) {
1921  ShowWindow(hwnd, SW_RESTORE);
1922  }
1924  break;
1925  case WM_ACTIVATE:
1926  /* The WM_ACTIVATE message is sent to both the window being activated and the window
1927  * being deactivated. If the windows use the same input queue, the message is sent
1928  * synchronously, first to the window procedure of the top-level window being
1929  * deactivated, then to the window procedure of the top-level window being activated.
1930  * If the windows use different input queues, the message is sent asynchronously,
1931  * so the window is activated immediately. */
1932  {
1933  GHOST_ModifierKeys modifiers;
1934  modifiers.clear();
1935  system->storeModifierKeys(modifiers);
1936  system->m_wheelDeltaAccum = 0;
1937  system->m_keycode_last_repeat_key = 0;
1938  event = processWindowEvent(LOWORD(wParam) ? GHOST_kEventWindowActivate :
1940  window);
1941  /* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL
1942  * will not be dispatched to OUR active window if we minimize one of OUR windows. */
1943  if (LOWORD(wParam) == WA_INACTIVE)
1944  window->lostMouseCapture();
1945 
1946  lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
1947  break;
1948  }
1949  case WM_ENTERSIZEMOVE:
1950  /* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving
1951  * or sizing modal loop. The window enters the moving or sizing modal loop when the user
1952  * clicks the window's title bar or sizing border, or when the window passes the
1953  * WM_SYSCOMMAND message to the DefWindowProc function and the wParam parameter of the
1954  * message specifies the SC_MOVE or SC_SIZE value. The operation is complete when
1955  * DefWindowProc returns.
1956  */
1957  window->m_inLiveResize = 1;
1958  break;
1959  case WM_EXITSIZEMOVE:
1960  window->m_inLiveResize = 0;
1961  break;
1962  case WM_PAINT:
1963  /* An application sends the WM_PAINT message when the system or another application
1964  * makes a request to paint a portion of an application's window. The message is sent
1965  * when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage
1966  * function when the application obtains a WM_PAINT message by using the GetMessage or
1967  * PeekMessage function.
1968  */
1969  if (!window->m_inLiveResize) {
1971  ::ValidateRect(hwnd, NULL);
1972  }
1973  else {
1974  eventHandled = true;
1975  }
1976  break;
1977  case WM_GETMINMAXINFO:
1978  /* The WM_GETMINMAXINFO message is sent to a window when the size or
1979  * position of the window is about to change. An application can use
1980  * this message to override the window's default maximized size and
1981  * position, or its default minimum or maximum tracking size.
1982  */
1983  processMinMaxInfo((MINMAXINFO *)lParam);
1984  /* Let DefWindowProc handle it. */
1985  break;
1986  case WM_SIZING:
1987  event = processWindowSizeEvent(window);
1988  break;
1989  case WM_SIZE:
1990  /* The WM_SIZE message is sent to a window after its size has changed.
1991  * The WM_SIZE and WM_MOVE messages are not sent if an application handles the
1992  * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
1993  * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
1994  * message without calling DefWindowProc.
1995  */
1996  event = processWindowSizeEvent(window);
1997  break;
1998  case WM_CAPTURECHANGED:
1999  window->lostMouseCapture();
2000  break;
2001  case WM_MOVING:
2002  /* The WM_MOVING message is sent to a window that the user is moving. By processing
2003  * this message, an application can monitor the size and position of the drag rectangle
2004  * and, if needed, change its size or position.
2005  */
2006  case WM_MOVE:
2007  /* The WM_SIZE and WM_MOVE messages are not sent if an application handles the
2008  * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
2009  * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
2010  * message without calling DefWindowProc.
2011  */
2012  /* See #WM_SIZE comment. */
2013  if (window->m_inLiveResize) {
2015  system->dispatchEvents();
2016  }
2017  else {
2018  event = processWindowEvent(GHOST_kEventWindowMove, window);
2019  }
2020 
2021  break;
2022  case WM_DPICHANGED:
2023  /* The WM_DPICHANGED message is sent when the effective dots per inch (dpi) for a
2024  * window has changed. The DPI is the scale factor for a window. There are multiple
2025  * events that can cause the DPI to change such as when the window is moved to a monitor
2026  * with a different DPI.
2027  */
2028  {
2029  // The suggested new size and position of the window.
2030  RECT *const suggestedWindowRect = (RECT *)lParam;
2031 
2032  // Push DPI change event first
2034  system->dispatchEvents();
2035  eventHandled = true;
2036 
2037  // Then move and resize window
2038  SetWindowPos(hwnd,
2039  NULL,
2040  suggestedWindowRect->left,
2041  suggestedWindowRect->top,
2042  suggestedWindowRect->right - suggestedWindowRect->left,
2043  suggestedWindowRect->bottom - suggestedWindowRect->top,
2044  SWP_NOZORDER | SWP_NOACTIVATE);
2045 
2046  window->updateDPI();
2047  }
2048  break;
2049  case WM_DISPLAYCHANGE: {
2050  GHOST_Wintab *wt = window->getWintab();
2051  if (wt) {
2052  wt->remapCoordinates();
2053  }
2054  break;
2055  }
2056  case WM_KILLFOCUS:
2057  /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard
2058  * focus. We want to prevent this if a window is still active and it loses focus to
2059  * nowhere. */
2060  if (!wParam && hwnd == ::GetActiveWindow()) {
2061  ::SetFocus(hwnd);
2062  }
2063  break;
2064  case WM_SETTINGCHANGE:
2065  /* Microsoft: "Note that some applications send this message with lParam set to NULL" */
2066  if ((lParam != NULL) && (wcscmp(LPCWSTR(lParam), L"ImmersiveColorSet") == 0)) {
2067  window->ThemeRefresh();
2068  }
2069  break;
2071  // Window events, ignored
2073  case WM_WINDOWPOSCHANGED:
2074  /* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place
2075  * in the Z order has changed as a result of a call to the SetWindowPos function or
2076  * another window-management function.
2077  * The WM_SIZE and WM_MOVE messages are not sent if an application handles the
2078  * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
2079  * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
2080  * message without calling DefWindowProc.
2081  */
2082  case WM_ERASEBKGND:
2083  /* An application sends the WM_ERASEBKGND message when the window background must be
2084  * erased (for example, when a window is resized). The message is sent to prepare an
2085  * invalidated portion of a window for painting.
2086  */
2087  case WM_NCPAINT:
2088  /* An application sends the WM_NCPAINT message to a window
2089  * when its frame must be painted. */
2090  case WM_NCACTIVATE:
2091  /* The WM_NCACTIVATE message is sent to a window when its non-client area needs to be
2092  * changed to indicate an active or inactive state. */
2093  case WM_DESTROY:
2094  /* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the
2095  * window procedure of the window being destroyed after the window is removed from the
2096  * screen. This message is sent first to the window being destroyed and then to the child
2097  * windows (if any) as they are destroyed. During the processing of the message, it can
2098  * be assumed that all child windows still exist. */
2099  case WM_NCDESTROY:
2100  /* The WM_NCDESTROY message informs a window that its non-client area is being
2101  * destroyed. The DestroyWindow function sends the WM_NCDESTROY message to the window
2102  * following the WM_DESTROY message. WM_DESTROY is used to free the allocated memory
2103  * object associated with the window.
2104  */
2105  break;
2106  case WM_SHOWWINDOW:
2107  /* The WM_SHOWWINDOW message is sent to a window when the window is
2108  * about to be hidden or shown. */
2109  case WM_WINDOWPOSCHANGING:
2110  /* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in
2111  * the Z order is about to change as a result of a call to the SetWindowPos function or
2112  * another window-management function.
2113  */
2114  case WM_SETFOCUS:
2115  /* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */
2116  break;
2118  // Other events
2120  case WM_GETTEXT:
2121  /* An application sends a WM_GETTEXT message to copy the text that
2122  * corresponds to a window into a buffer provided by the caller.
2123  */
2124  case WM_ACTIVATEAPP:
2125  /* The WM_ACTIVATEAPP message is sent when a window belonging to a
2126  * different application than the active window is about to be activated.
2127  * The message is sent to the application whose window is being activated
2128  * and to the application whose window is being deactivated.
2129  */
2130  case WM_TIMER:
2131  /* The WIN32 docs say:
2132  * The WM_TIMER message is posted to the installing thread's message queue
2133  * when a timer expires. You can process the message by providing a WM_TIMER
2134  * case in the window procedure. Otherwise, the default window procedure will
2135  * call the TimerProc callback function specified in the call to the SetTimer
2136  * function used to install the timer.
2137  *
2138  * In GHOST, we let DefWindowProc call the timer callback.
2139  */
2140  break;
2141  case DM_POINTERHITTEST:
2142  /* The DM_POINTERHITTEST message is sent to a window, when pointer input is first
2143  * detected, in order to determine the most probable input target for Direct
2144  * Manipulation. */
2145  if (system->m_multitouchGestures) {
2146  window->onPointerHitTest(wParam);
2147  }
2148  break;
2149  }
2150  }
2151  else {
2152  // Event found for a window before the pointer to the class has been set.
2153  GHOST_PRINT("GHOST_SystemWin32::wndProc: GHOST window event before creation\n");
2154  /* These are events we typically miss at this point:
2155  * WM_GETMINMAXINFO 0x24
2156  * WM_NCCREATE 0x81
2157  * WM_NCCALCSIZE 0x83
2158  * WM_CREATE 0x01
2159  * We let DefWindowProc do the work.
2160  */
2161  }
2162  }
2163  else {
2164  // Events without valid hwnd
2165  GHOST_PRINT("GHOST_SystemWin32::wndProc: event without window\n");
2166  }
2167 
2168  if (event) {
2169  system->pushEvent(event);
2170  eventHandled = true;
2171  }
2172 
2173  if (!eventHandled)
2174  lResult = ::DefWindowProcW(hwnd, msg, wParam, lParam);
2175 
2176  return lResult;
2177 }
2178 
2179 char *GHOST_SystemWin32::getClipboard(bool selection) const
2180 {
2181  char *temp_buff;
2182 
2183  if (IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(NULL)) {
2184  wchar_t *buffer;
2185  HANDLE hData = GetClipboardData(CF_UNICODETEXT);
2186  if (hData == NULL) {
2187  CloseClipboard();
2188  return NULL;
2189  }
2190  buffer = (wchar_t *)GlobalLock(hData);
2191  if (!buffer) {
2192  CloseClipboard();
2193  return NULL;
2194  }
2195 
2196  temp_buff = alloc_utf_8_from_16(buffer, 0);
2197 
2198  /* Buffer mustn't be accessed after CloseClipboard
2199  * it would like accessing free-d memory */
2200  GlobalUnlock(hData);
2201  CloseClipboard();
2202 
2203  return temp_buff;
2204  }
2205  else if (IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL)) {
2206  char *buffer;
2207  size_t len = 0;
2208  HANDLE hData = GetClipboardData(CF_TEXT);
2209  if (hData == NULL) {
2210  CloseClipboard();
2211  return NULL;
2212  }
2213  buffer = (char *)GlobalLock(hData);
2214  if (!buffer) {
2215  CloseClipboard();
2216  return NULL;
2217  }
2218 
2219  len = strlen(buffer);
2220  temp_buff = (char *)malloc(len + 1);
2221  strncpy(temp_buff, buffer, len);
2222  temp_buff[len] = '\0';
2223 
2224  /* Buffer mustn't be accessed after CloseClipboard
2225  * it would like accessing free-d memory */
2226  GlobalUnlock(hData);
2227  CloseClipboard();
2228 
2229  return temp_buff;
2230  }
2231  else {
2232  return NULL;
2233  }
2234 }
2235 
2236 void GHOST_SystemWin32::putClipboard(const char *buffer, bool selection) const
2237 {
2238  if (selection || !buffer) {
2239  return;
2240  } // for copying the selection, used on X11
2241 
2242  if (OpenClipboard(NULL)) {
2243  EmptyClipboard();
2244 
2245  // Get length of buffer including the terminating null
2246  size_t len = count_utf_16_from_8(buffer);
2247 
2248  HGLOBAL clipbuffer = GlobalAlloc(GMEM_MOVEABLE, sizeof(wchar_t) * len);
2249  if (clipbuffer) {
2250  wchar_t *data = (wchar_t *)GlobalLock(clipbuffer);
2251 
2253 
2254  GlobalUnlock(clipbuffer);
2255  SetClipboardData(CF_UNICODETEXT, clipbuffer);
2256  }
2257 
2258  CloseClipboard();
2259  }
2260 }
2261 
2262 /* -------------------------------------------------------------------- */
2267  const char *message,
2268  const char *help_label,
2269  const char *continue_label,
2270  const char *link,
2271  GHOST_DialogOptions dialog_options) const
2272 {
2273  const wchar_t *title_16 = alloc_utf16_from_8(title, 0);
2274  const wchar_t *message_16 = alloc_utf16_from_8(message, 0);
2275  const wchar_t *help_label_16 = alloc_utf16_from_8(help_label, 0);
2276  const wchar_t *continue_label_16 = alloc_utf16_from_8(continue_label, 0);
2277 
2278  int nButtonPressed = 0;
2279  TASKDIALOGCONFIG config = {0};
2280  const TASKDIALOG_BUTTON buttons[] = {{IDOK, help_label_16}, {IDCONTINUE, continue_label_16}};
2281 
2282  config.cbSize = sizeof(config);
2283  config.hInstance = 0;
2284  config.dwCommonButtons = 0;
2285  config.pszMainIcon = (dialog_options & GHOST_DialogError ? TD_ERROR_ICON :
2286  dialog_options & GHOST_DialogWarning ? TD_WARNING_ICON :
2287  TD_INFORMATION_ICON);
2288  config.pszWindowTitle = L"Blender";
2289  config.pszMainInstruction = title_16;
2290  config.pszContent = message_16;
2291  config.pButtons = (link) ? buttons : buttons + 1;
2292  config.cButtons = (link) ? 2 : 1;
2293 
2294  TaskDialogIndirect(&config, &nButtonPressed, NULL, NULL);
2295  switch (nButtonPressed) {
2296  case IDOK:
2297  ShellExecute(NULL, "open", link, NULL, NULL, SW_SHOWNORMAL);
2298  break;
2299  case IDCONTINUE:
2300  break;
2301  default:
2302  break; // should never happen
2303  }
2304 
2305  free((void *)title_16);
2306  free((void *)message_16);
2307  free((void *)help_label_16);
2308  free((void *)continue_label_16);
2309 
2310  return GHOST_kSuccess;
2311 }
2312 
2315 static DWORD GetParentProcessID(void)
2316 {
2317  HANDLE snapshot;
2318  PROCESSENTRY32 pe32 = {0};
2319  DWORD ppid = 0, pid = GetCurrentProcessId();
2320  snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
2321  if (snapshot == INVALID_HANDLE_VALUE) {
2322  return -1;
2323  }
2324  pe32.dwSize = sizeof(pe32);
2325  if (!Process32First(snapshot, &pe32)) {
2326  CloseHandle(snapshot);
2327  return -1;
2328  }
2329  do {
2330  if (pe32.th32ProcessID == pid) {
2331  ppid = pe32.th32ParentProcessID;
2332  break;
2333  }
2334  } while (Process32Next(snapshot, &pe32));
2335  CloseHandle(snapshot);
2336  return ppid;
2337 }
2338 
2339 static bool getProcessName(int pid, char *buffer, int max_len)
2340 {
2341  bool result = false;
2342  HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
2343  if (handle) {
2344  GetModuleFileNameEx(handle, 0, buffer, max_len);
2345  result = true;
2346  }
2347  CloseHandle(handle);
2348  return result;
2349 }
2350 
2352 {
2353  HWND hwnd = GetConsoleWindow();
2354 
2355  if (hwnd) {
2356  DWORD pid = (DWORD)-1;
2357  DWORD ppid = GetParentProcessID();
2358  char parent_name[MAX_PATH];
2359  bool start_from_launcher = false;
2360 
2361  GetWindowThreadProcessId(hwnd, &pid);
2362  if (getProcessName(ppid, parent_name, sizeof(parent_name))) {
2363  char *filename = strrchr(parent_name, '\\');
2364  if (filename != NULL) {
2365  start_from_launcher = strstr(filename, "blender.exe") != NULL;
2366  }
2367  }
2368 
2369  /* When we're starting from a wrapper we need to compare with parent process ID. */
2370  if (pid != (start_from_launcher ? ppid : GetCurrentProcessId()))
2371  return true;
2372  }
2373 
2374  return false;
2375 }
2376 
2378 {
2379  HWND wnd = GetConsoleWindow();
2380 
2381  switch (action) {
2383  if (!isStartedFromCommandPrompt()) {
2384  ShowWindow(wnd, SW_HIDE);
2385  m_consoleStatus = 0;
2386  }
2387  break;
2388  }
2390  ShowWindow(wnd, SW_HIDE);
2391  m_consoleStatus = 0;
2392  break;
2394  ShowWindow(wnd, SW_SHOW);
2395  if (!isStartedFromCommandPrompt()) {
2396  DeleteMenu(GetSystemMenu(wnd, FALSE), SC_CLOSE, MF_BYCOMMAND);
2397  }
2398  m_consoleStatus = 1;
2399  break;
2401  ShowWindow(wnd, m_consoleStatus ? SW_HIDE : SW_SHOW);
2404  DeleteMenu(GetSystemMenu(wnd, FALSE), SC_CLOSE, MF_BYCOMMAND);
2405  }
2406  break;
2407  }
2408 
2409  return m_consoleStatus;
2410 }
void BLI_kdtree_nd_() free(KDTree *tree)
Definition: kdtree_impl.h:102
#define FALSE
Definition: GHOST_C-Test.c:17
#define GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY
#define GHOST_PRINTF(x,...)
Definition: GHOST_Debug.h:36
#define GHOST_ASSERT(x, info)
Definition: GHOST_Debug.h:54
#define GHOST_PRINT(x)
Definition: GHOST_Debug.h:35
#define VK_EQUALS
static void initRawInput()
#define DEVICE_COUNT
#define VK_BACK_QUOTE
#define VK_CLOSE_BRACKET
#define VK_SEMICOLON
#define VK_COMMA
#define VK_SLASH
BOOL(API * GHOST_WIN32_EnableNonClientDpiScaling)(HWND)
static bool isStartedFromCommandPrompt()
#define VK_OPEN_BRACKET
#define VK_BACK_SLASH
#define VK_GR_LESS
#define VK_PERIOD
static DWORD GetParentProcessID(void)
static bool getProcessName(int pid, char *buffer, int max_len)
#define VK_MINUS
@ GHOST_kTrackpadEventMagnify
Definition: GHOST_Types.h:455
@ GHOST_kTrackpadEventScroll
Definition: GHOST_Types.h:452
GHOST_TWindowState
Definition: GHOST_Types.h:129
@ GHOST_kWindowStateMinimized
Definition: GHOST_Types.h:132
@ GHOST_kStandardCursorDefault
Definition: GHOST_Types.h:216
GHOST_TEventType
Definition: GHOST_Types.h:169
@ GHOST_kEventWindowClose
Definition: GHOST_Types.h:189
@ GHOST_kEventWindowMove
Definition: GHOST_Types.h:194
@ GHOST_kEventWindowSize
Definition: GHOST_Types.h:193
@ GHOST_kEventImeComposition
Definition: GHOST_Types.h:208
@ GHOST_kEventCursorMove
Definition: GHOST_Types.h:172
@ GHOST_kEventButtonUp
Definition: GHOST_Types.h:174
@ GHOST_kEventWindowActivate
Definition: GHOST_Types.h:190
@ GHOST_kEventWindowUpdate
Definition: GHOST_Types.h:192
@ GHOST_kEventWindowDeactivate
Definition: GHOST_Types.h:191
@ GHOST_kEventButtonDown
Definition: GHOST_Types.h:173
@ GHOST_kEventKeyDown
Definition: GHOST_Types.h:183
@ GHOST_kEventImeCompositionStart
Definition: GHOST_Types.h:207
@ GHOST_kEventImeCompositionEnd
Definition: GHOST_Types.h:209
@ GHOST_kEventWindowDPIHintChanged
Definition: GHOST_Types.h:195
@ GHOST_kEventKeyUp
Definition: GHOST_Types.h:184
static const GHOST_TabletData GHOST_TABLET_DATA_NONE
Definition: GHOST_Types.h:104
@ GHOST_kDebugWintab
Definition: GHOST_Types.h:587
@ GHOST_kTabletModeNone
Definition: GHOST_Types.h:84
@ GHOST_glAlphaBackground
Definition: GHOST_Types.h:64
@ GHOST_glStereoVisual
Definition: GHOST_Types.h:62
@ GHOST_glDebugContext
Definition: GHOST_Types.h:63
GHOST_TKey
Definition: GHOST_Types.h:259
@ GHOST_kKeyInsert
Definition: GHOST_Types.h:346
@ GHOST_kKeySemicolon
Definition: GHOST_Types.h:288
@ GHOST_kKeyMediaPlay
Definition: GHOST_Types.h:398
@ GHOST_kKeyQuote
Definition: GHOST_Types.h:269
@ GHOST_kKeyNumpad3
Definition: GHOST_Types.h:357
@ GHOST_kKeyAccentGrave
Definition: GHOST_Types.h:322
@ GHOST_kKeyNumpad1
Definition: GHOST_Types.h:355
@ GHOST_kKeyLeftAlt
Definition: GHOST_Types.h:328
@ GHOST_kKeyRightShift
Definition: GHOST_Types.h:325
@ GHOST_kKeyNumLock
Definition: GHOST_Types.h:335
@ GHOST_kKeyEnter
Definition: GHOST_Types.h:265
@ GHOST_kKeyNumpadSlash
Definition: GHOST_Types.h:369
@ GHOST_kKeyRightArrow
Definition: GHOST_Types.h:339
@ GHOST_kKeyF13
Definition: GHOST_Types.h:384
@ GHOST_kKeyNumpad4
Definition: GHOST_Types.h:358
@ GHOST_kKeyPause
Definition: GHOST_Types.h:344
@ GHOST_kKeyCapsLock
Definition: GHOST_Types.h:334
@ GHOST_kKeyApp
Definition: GHOST_Types.h:332
@ GHOST_kKeyMinus
Definition: GHOST_Types.h:271
@ GHOST_kKeyMediaStop
Definition: GHOST_Types.h:399
@ GHOST_kKeyBackSpace
Definition: GHOST_Types.h:261
@ GHOST_kKey0
Definition: GHOST_Types.h:277
@ GHOST_kKeyDownPage
Definition: GHOST_Types.h:351
@ GHOST_kKeyGrLess
Definition: GHOST_Types.h:331
@ GHOST_kKeyDownArrow
Definition: GHOST_Types.h:341
@ GHOST_kKeyNumpadPeriod
Definition: GHOST_Types.h:364
@ GHOST_kKeyF1
Definition: GHOST_Types.h:372
@ GHOST_kKeyNumpadAsterisk
Definition: GHOST_Types.h:368
@ GHOST_kKeyPrintScreen
Definition: GHOST_Types.h:343
@ GHOST_kKeyLeftControl
Definition: GHOST_Types.h:326
@ GHOST_kKeyLeftBracket
Definition: GHOST_Types.h:319
@ GHOST_kKeyTab
Definition: GHOST_Types.h:262
@ GHOST_kKeyComma
Definition: GHOST_Types.h:270
@ GHOST_kKeyRightBracket
Definition: GHOST_Types.h:320
@ GHOST_kKeyBackslash
Definition: GHOST_Types.h:321
@ GHOST_kKeyOS
Definition: GHOST_Types.h:330
@ GHOST_kKeyNumpad2
Definition: GHOST_Types.h:356
@ GHOST_kKeyRightAlt
Definition: GHOST_Types.h:329
@ GHOST_kKeyPeriod
Definition: GHOST_Types.h:273
@ GHOST_kKeyNumpadPlus
Definition: GHOST_Types.h:366
@ GHOST_kKeyUpPage
Definition: GHOST_Types.h:350
@ GHOST_kKeyNumpad5
Definition: GHOST_Types.h:359
@ GHOST_kKeyLeftArrow
Definition: GHOST_Types.h:338
@ GHOST_kKeyEqual
Definition: GHOST_Types.h:289
@ GHOST_kKeyHome
Definition: GHOST_Types.h:348
@ GHOST_kKeyNumpad6
Definition: GHOST_Types.h:360
@ GHOST_kKeyNumpad8
Definition: GHOST_Types.h:362
@ GHOST_kKeyNumpad9
Definition: GHOST_Types.h:363
@ GHOST_kKeyEnd
Definition: GHOST_Types.h:349
@ GHOST_kKeyUpArrow
Definition: GHOST_Types.h:340
@ GHOST_kKeyDelete
Definition: GHOST_Types.h:347
@ GHOST_kKeyNumpad0
Definition: GHOST_Types.h:354
@ GHOST_kKeyA
Definition: GHOST_Types.h:292
@ GHOST_kKeyMediaFirst
Definition: GHOST_Types.h:400
@ GHOST_kKeyNumpad7
Definition: GHOST_Types.h:361
@ GHOST_kKeyRightControl
Definition: GHOST_Types.h:327
@ GHOST_kKeyEsc
Definition: GHOST_Types.h:267
@ GHOST_kKeyUnknown
Definition: GHOST_Types.h:260
@ GHOST_kKeyScrollLock
Definition: GHOST_Types.h:336
@ GHOST_kKeySlash
Definition: GHOST_Types.h:274
@ GHOST_kKeyNumpadEnter
Definition: GHOST_Types.h:365
@ GHOST_kKeyNumpadMinus
Definition: GHOST_Types.h:367
@ GHOST_kKeyLeftShift
Definition: GHOST_Types.h:324
@ GHOST_kKeyMediaLast
Definition: GHOST_Types.h:401
@ GHOST_kKeySpace
Definition: GHOST_Types.h:268
GHOST_TDrawingContextType
Definition: GHOST_Types.h:148
GHOST_TModifierKey
Definition: GHOST_Types.h:118
@ GHOST_kModifierKeyRightControl
Definition: GHOST_Types.h:124
@ GHOST_kModifierKeyLeftControl
Definition: GHOST_Types.h:123
@ GHOST_kModifierKeyRightAlt
Definition: GHOST_Types.h:122
@ GHOST_kModifierKeyOS
Definition: GHOST_Types.h:125
@ GHOST_kModifierKeyRightShift
Definition: GHOST_Types.h:120
@ GHOST_kModifierKeyLeftAlt
Definition: GHOST_Types.h:121
@ GHOST_kModifierKeyLeftShift
Definition: GHOST_Types.h:119
GHOST_TSuccess
Definition: GHOST_Types.h:74
@ GHOST_kFailure
Definition: GHOST_Types.h:74
@ GHOST_kSuccess
Definition: GHOST_Types.h:74
@ GHOST_kFireTimeNever
Definition: GHOST_Types.h:116
GHOST_TDragnDropTypes
Definition: GHOST_Types.h:474
GHOST_TButton
Definition: GHOST_Types.h:156
@ GHOST_kButtonMaskRight
Definition: GHOST_Types.h:160
@ GHOST_kButtonMaskButton4
Definition: GHOST_Types.h:161
@ GHOST_kButtonMaskLeft
Definition: GHOST_Types.h:158
@ GHOST_kButtonMaskButton5
Definition: GHOST_Types.h:162
@ GHOST_kButtonMaskMiddle
Definition: GHOST_Types.h:159
GHOST_TConsoleWindowState
Definition: GHOST_Types.h:139
@ GHOST_kConsoleWindowStateShow
Definition: GHOST_Types.h:141
@ GHOST_kConsoleWindowStateHideForNonConsoleLaunch
Definition: GHOST_Types.h:143
@ GHOST_kConsoleWindowStateHide
Definition: GHOST_Types.h:140
@ GHOST_kConsoleWindowStateToggle
Definition: GHOST_Types.h:142
GHOST_TTabletAPI
Definition: GHOST_Types.h:89
@ GHOST_kTabletWinPointer
Definition: GHOST_Types.h:93
@ GHOST_kTabletWintab
Definition: GHOST_Types.h:94
GHOST_DialogOptions
Definition: GHOST_Types.h:67
@ GHOST_DialogError
Definition: GHOST_Types.h:69
@ GHOST_DialogWarning
Definition: GHOST_Types.h:68
#define GET_POINTERID_WPARAM(wParam)
@ MousePressed
@ MouseReleased
typedef UINT(API *GHOST_WIN32_GetDpiForWindow)(HWND)
#define WINTAB_PRINTF(x,...)
Definition: GHOST_Wintab.h:29
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble GLdouble r _GL_VOID_RET _GL_VOID GLfloat GLfloat r _GL_VOID_RET _GL_VOID GLint GLint r _GL_VOID_RET _GL_VOID GLshort GLshort r _GL_VOID_RET _GL_VOID GLdouble GLdouble r
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei width
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble top
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble t
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Sky Generate a procedural sky texture Noise Generate fractal Perlin noise Wave Generate procedural bands or rings with noise Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition: btDbvt.cpp:299
virtual GHOST_TSuccess initialize(void)
virtual GHOST_TSuccess getNumDisplays(uint8_t &numDisplays) const
void removeTypeEvents(GHOST_TEventType type, GHOST_IWindow *window=NULL)
static GHOST_ISystem * getSystem()
GHOST_TSuccess setCursorPosition(int32_t x, int32_t y)
uint64_t getMilliSeconds() const
void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const
void setTabletAPI(GHOST_TTabletAPI api) override
GHOST_TKey hardKey(RAWINPUT const &raw, bool *r_keyDown, bool *r_is_repeated_modifier)
static void processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam)
GHOST_IContext * createOffscreenContext(GHOST_GLSettings glSettings)
static GHOST_EventButton * processButtonEvent(GHOST_TEventType type, GHOST_WindowWin32 *window, GHOST_TButton mask)
unsigned short m_keycode_last_repeat_key
GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const
GHOST_IWindow * createWindow(const char *title, int32_t left, int32_t top, uint32_t width, uint32_t height, GHOST_TWindowState state, GHOST_TDrawingContextType type, GHOST_GLSettings glSettings, const bool exclusive=false, const bool is_dialog=false, const GHOST_IWindow *parentWindow=0)
GHOST_TKey processSpecialKey(short vKey, short scanCode) const
static GHOST_Event * processWindowSizeEvent(GHOST_WindowWin32 *window)
GHOST_TKey convertKey(short vKey, short ScanCode, short extend) const
GHOST_TSuccess init()
void putClipboard(const char *buffer, bool selection) const
static GHOST_ContextD3D * createOffscreenContextD3D()
bool processEvents(bool waitForEvent)
char * getClipboard(bool selection) const
uint64_t tickCountToMillis(__int64 ticks) const
static GHOST_TSuccess disposeContextD3D(GHOST_ContextD3D *context)
static void processWintabEvent(GHOST_WindowWin32 *window)
uint64_t performanceCounterToMillis(__int64 perf_ticks) const
GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const
void retrieveModifierKeys(GHOST_ModifierKeys &keys) const
static GHOST_EventKey * processKeyEvent(GHOST_WindowWin32 *window, RAWINPUT const &raw)
void initDebug(GHOST_Debug debug) override
void getAllDisplayDimensions(uint32_t &width, uint32_t &height) const
GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const
static void processMinMaxInfo(MINMAXINFO *minmax)
int setConsoleWindowState(GHOST_TConsoleWindowState action)
void storeModifierKeys(const GHOST_ModifierKeys &keys)
static GHOST_Event * processWindowEvent(GHOST_TEventType type, GHOST_WindowWin32 *window)
uint8_t getNumDisplays() const
static GHOST_EventCursor * processCursorEvent(GHOST_WindowWin32 *window)
void handleKeyboardChange(void)
static void processPointerEvent(UINT type, GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam, bool &eventhandled)
GHOST_TSuccess disposeContext(GHOST_IContext *context)
GHOST_TSuccess showMessageBox(const char *title, const char *message, const char *help_label, const char *continue_label, const char *link, GHOST_DialogOptions dialog_options) const
static LRESULT WINAPI s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
static GHOST_TSuccess pushDragDropEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType, GHOST_WindowWin32 *window, int mouseX, int mouseY, void *data)
GHOST_TSuccess exit()
GHOST_EventManager * getEventManager() const
Definition: GHOST_System.h:427
GHOST_WindowManager * getWindowManager() const
Definition: GHOST_System.h:432
virtual GHOST_TSuccess exit()
bool m_multitouchGestures
Definition: GHOST_System.h:414
virtual GHOST_TSuccess init()
virtual void setTabletAPI(GHOST_TTabletAPI api)
GHOST_TimerManager * getTimerManager() const
Definition: GHOST_System.h:422
GHOST_WindowManager * m_windowManager
Definition: GHOST_System.h:395
virtual void initDebug(GHOST_Debug debug)
void dispatchEvents()
GHOST_TSuccess pushEvent(GHOST_IEvent *event)
GHOST_DisplayManager * m_displayManager
Definition: GHOST_System.h:389
GHOST_EventManager * m_eventManager
Definition: GHOST_System.h:398
bool fireTimers(uint64_t time)
GHOST_TSuccess addWindow(GHOST_IWindow *window)
GHOST_IWindow * getActiveWindow(void) const
const std::vector< GHOST_IWindow * > & getWindows() const
GHOST_TSuccess setActiveWindow(GHOST_IWindow *window)
GHOST_TTrackpadInfo getTrackpadInfo()
void loadCursor(bool visible, GHOST_TStandardCursor cursorShape) const
bool usingTabletAPI(GHOST_TTabletAPI api) const
GHOST_TSuccess getPointerInfo(std::vector< GHOST_PointerInfoWin32 > &outPointerInfo, WPARAM wParam, LPARAM lParam)
void loadWintab(bool enable)
GHOST_TabletData getTabletData()
GHOST_Wintab * getWintab() const
void updateMouseCapture(GHOST_MouseCaptureEventWin32 event)
GHOST_TSuccess setState(GHOST_TWindowState state)
void getClientBounds(GHOST_Rect &bounds) const
void onPointerHitTest(WPARAM wParam)
GHOST_TWindowState getState() const
GHOST_TSuccess getCursorGrabBounds(GHOST_Rect &bounds)
bool getCursorVisibility() const
Definition: GHOST_Window.h:408
void setCursorGrabAccum(int32_t x, int32_t y)
Definition: GHOST_Window.h:440
GHOST_TAxisFlag getCursorGrabAxis() const
Definition: GHOST_Window.h:423
GHOST_TStandardCursor getCursorShape() const
Definition: GHOST_Window.h:446
bool getCursorGrabModeIsWarp() const
Definition: GHOST_Window.h:418
void getCursorGrabAccum(int32_t &x, int32_t &y) const
Definition: GHOST_Window.h:434
void mapWintabToSysCoordinates(int x_in, int y_in, int &x_out, int &y_out)
GHOST_TabletData getLastTabletData()
void getInput(std::vector< GHOST_WintabInfoWin32 > &outWintabInfo)
void processInfoChange(LPARAM lParam)
void updateCursorInfo()
void remapCoordinates()
bool trustCoordinates()
bool testCoordinates(int sysX, int sysY, int wtX, int wtY)
static void setDebug(bool debug)
double time
int len
Definition: draw_manager.c:108
uint pos
int count
ccl_global float * buffer
const int state
ccl_device_inline float4 mask(const int4 &mask, const float4 &a)
Definition: math_float4.h:513
static ulong * next
static int left
#define L
static const VertexNature POINT
Definition: Nature.h:20
Vector< CPUDevice > devices
list of all CPUDevices. for every hardware thread an instance of CPUDevice is created
T abs(const T &a)
static const pxr::TfToken raw("raw", pxr::TfToken::Immortal)
unsigned int uint32_t
Definition: stdint.h:80
__int64 int64_t
Definition: stdint.h:89
signed int int32_t
Definition: stdint.h:77
unsigned char uint8_t
Definition: stdint.h:78
unsigned __int64 uint64_t
Definition: stdint.h:90
void set(GHOST_TButton mask, bool down)
void set(GHOST_TModifierKey mask, bool down)
bool get(GHOST_TModifierKey mask) const
GHOST_TTabletMode Active
Definition: GHOST_Types.h:98
wchar_t * alloc_utf16_from_8(const char *in8, size_t add)
Definition: utfconv.c:291
int conv_utf_8_to_16(const char *in8, wchar_t *out16, size_t size16)
Definition: utfconv.c:181
char * alloc_utf_8_from_16(const wchar_t *in16, size_t add)
Definition: utfconv.c:279
int conv_utf_16_to_8(const wchar_t *in16, char *out8, size_t size8)
Definition: utfconv.c:115
size_t count_utf_16_from_8(const char *string8)
Definition: utfconv.c:57