Panda3D
winGraphicsWindow.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file winGraphicsWindow.cxx
10  * @author drose
11  * @date 2002-12-20
12  */
13 
14 #include "winGraphicsWindow.h"
15 #include "config_windisplay.h"
16 #include "winGraphicsPipe.h"
17 
18 #include "graphicsPipe.h"
19 #include "keyboardButton.h"
20 #include "mouseButton.h"
21 #include "clockObject.h"
22 #include "config_putil.h"
23 #include "throw_event.h"
24 #include "nativeWindowHandle.h"
25 
26 #include <tchar.h>
27 
28 #ifndef WM_DPICHANGED
29 #define WM_DPICHANGED 0x02E0
30 #endif
31 
32 #ifndef WM_TOUCH
33 #define WM_TOUCH 0x0240
34 #endif
35 
36 #if WINVER < 0x0601
37 // Not used on Windows XP, but we still need to define it.
38 #define TOUCH_COORD_TO_PIXEL(l) ((l) / 100)
39 
40 using std::endl;
41 using std::wstring;
42 
43 DECLARE_HANDLE(HTOUCHINPUT);
44 #endif
45 
46 TypeHandle WinGraphicsWindow::_type_handle;
47 TypeHandle WinGraphicsWindow::WinWindowHandle::_type_handle;
48 
49 WinGraphicsWindow::WindowHandles WinGraphicsWindow::_window_handles;
50 WinGraphicsWindow *WinGraphicsWindow::_creating_window = nullptr;
51 
52 WinGraphicsWindow *WinGraphicsWindow::_cursor_window = nullptr;
53 bool WinGraphicsWindow::_cursor_hidden = false;
54 
55 RECT WinGraphicsWindow::_mouse_unconfined_cliprect;
56 
57 // These are used to save the previous state of the fancy Win2000 effects that
58 // interfere with rendering when the mouse wanders into a window's client
59 // area.
60 bool WinGraphicsWindow::_got_saved_params = false;
61 int WinGraphicsWindow::_saved_mouse_trails;
62 BOOL WinGraphicsWindow::_saved_cursor_shadow;
63 BOOL WinGraphicsWindow::_saved_mouse_vanish;
64 
65 WinGraphicsWindow::IconFilenames WinGraphicsWindow::_icon_filenames;
66 WinGraphicsWindow::IconFilenames WinGraphicsWindow::_cursor_filenames;
67 
68 WinGraphicsWindow::WindowClasses WinGraphicsWindow::_window_classes;
69 int WinGraphicsWindow::_window_class_index = 0;
70 
71 static const char * const errorbox_title = "Panda3D Error";
72 
73 // These static variables contain pointers to the touch input functions, which
74 // are dynamically extracted from USER32.DLL
75 typedef BOOL (WINAPI *PFN_REGISTERTOUCHWINDOW)(IN HWND hWnd, IN ULONG ulFlags);
76 typedef BOOL (WINAPI *PFN_GETTOUCHINPUTINFO)(IN HTOUCHINPUT hTouchInput, IN UINT cInputs, OUT PTOUCHINPUT pInputs, IN int cbSize);
77 typedef BOOL (WINAPI *PFN_CLOSETOUCHINPUTHANDLE)(IN HTOUCHINPUT hTouchInput);
78 
79 static PFN_REGISTERTOUCHWINDOW pRegisterTouchWindow = 0;
80 static PFN_GETTOUCHINPUTINFO pGetTouchInputInfo = 0;
81 static PFN_CLOSETOUCHINPUTHANDLE pCloseTouchInputHandle = 0;
82 
83 /**
84  *
85  */
86 WinGraphicsWindow::
87 WinGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
88  const std::string &name,
89  const FrameBufferProperties &fb_prop,
90  const WindowProperties &win_prop,
91  int flags,
93  GraphicsOutput *host) :
94  GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
95 {
96  initialize_input_devices();
97  _hWnd = (HWND)0;
98  _ime_open = false;
99  _ime_active = false;
100  _tracking_mouse_leaving = false;
101  _cursor = 0;
102  _lost_keypresses = false;
103  _lshift_down = false;
104  _rshift_down = false;
105  _lcontrol_down = false;
106  _rcontrol_down = false;
107  _lalt_down = false;
108  _ralt_down = false;
109  _hparent = nullptr;
110  _num_touches = 0;
111 }
112 
113 /**
114  *
115  */
116 WinGraphicsWindow::
117 ~WinGraphicsWindow() {
118  if (_window_handle != nullptr) {
119  DCAST(WinWindowHandle, _window_handle)->clear_window();
120  }
121 }
122 
123 /**
124  * Returns the MouseData associated with the nth input device's pointer.
125  */
127 get_pointer(int device) const {
128  MouseData result;
129  {
130  LightMutexHolder holder(_input_lock);
131  nassertr(device >= 0 && device < (int)_input_devices.size(), MouseData());
132 
133  result = ((const GraphicsWindowInputDevice *)_input_devices[device].p())->get_pointer();
134 
135  // We recheck this immediately to get the most up-to-date value.
136  POINT cpos;
137  if (device == 0 && result._in_window && GetCursorPos(&cpos) && ScreenToClient(_hWnd, &cpos)) {
138  double time = ClockObject::get_global_clock()->get_real_time();
139  result._xpos = cpos.x;
140  result._ypos = cpos.y;
141  ((GraphicsWindowInputDevice *)_input_devices[0].p())->set_pointer_in_window(result._xpos, result._ypos, time);
142  }
143  }
144  return result;
145 }
146 
147 /**
148  * Forces the pointer to the indicated position within the window, if
149  * possible.
150  *
151  * Returns true if successful, false on failure. This may fail if the mouse
152  * is not currently within the window, or if the API doesn't support this
153  * operation.
154  */
156 move_pointer(int device, int x, int y) {
157  // First, indicate that the IME is no longer active, so that it won't send
158  // the string through WM_IME_COMPOSITION. But we still leave _ime_open
159  // true, so that it also won't send the string through WM_CHAR.
160  _ime_active = false;
161 
162  // Note: this is not thread-safe; it should be called only from App.
163  // Probably not an issue.
164  if (device == 0) {
165  // Move the system mouse pointer.
166  if (!_properties.get_foreground() )
167  // !_input->get_pointer().get_in_window())
168  {
169  // If the window doesn't have input focus, or the mouse isn't
170  // currently within the window, forget it.
171  return false;
172  }
173 
174  RECT view_rect;
175  get_client_rect_screen(_hWnd, &view_rect);
176 
177  SetCursorPos(view_rect.left + x, view_rect.top + y);
178  _input->set_pointer_in_window(x, y);
179  return true;
180  } else {
181  // Move a raw mouse.
182  if ((device < 1)||(device >= (int)_input_devices.size())) {
183  return false;
184  }
185  //_input_devices[device].set_pointer_in_window(x, y);
186  return true;
187  }
188 }
189 
190 /**
191  * Forces the ime window to close, if any
192  *
193  */
196  // Check if the ime window is open
197  if (!_ime_open)
198  return;
199 
200  HIMC hIMC = ImmGetContext(_hWnd);
201  if (hIMC != 0) {
202  if (!ImmSetOpenStatus(hIMC, false)) {
203  windisplay_cat.debug() << "ImmSetOpenStatus failed\n";
204  }
205  ImmReleaseContext(_hWnd, hIMC);
206  }
207  _ime_open = false;
208 
209  windisplay_cat.debug() << "success: closed ime window\n";
210  return;
211 }
212 
213 /**
214  * This function will be called within the draw thread after end_frame() has
215  * been called on all windows, to initiate the exchange of the front and back
216  * buffers.
217  *
218  * This should instruct the window to prepare for the flip at the next video
219  * sync, but it should not wait.
220  *
221  * We have the two separate functions, begin_flip() and end_flip(), to make it
222  * easier to flip all of the windows at the same time.
223  */
226 }
227 
228 /**
229  * Do whatever processing is necessary to ensure that the window responds to
230  * user events. Also, honor any requests recently made via
231  * request_properties()
232  *
233  * This function is called only within the window thread.
234  */
238 
239  // We can't treat the message loop specially just because the window is
240  // minimized, because we might be reading messages queued up for some other
241  // window, which is not minimized.
242  /*
243  if (!_window_active) {
244  // Get 1 msg at a time until no more are left and we block and sleep, or
245  // message changes _return_control_to_app or !_window_active status
246 
247  while(!_window_active && (!_return_control_to_app)) {
248  process_1_event();
249  }
250  _return_control_to_app = false;
251 
252  } else
253  */
254 
255  MSG msg;
256 
257  if (!disable_message_loop) {
258  // Handle all the messages on the queue in a row. Some of these might be
259  // for another window, but they will get dispatched appropriately.
260  while (PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE)) {
261  process_1_event();
262  }
263  }
264 }
265 
266 /**
267  * Applies the requested set of properties to the window, if possible, for
268  * instance to request a change in size or minimization status.
269  *
270  * The window properties are applied immediately, rather than waiting until
271  * the next frame. This implies that this method may *only* be called from
272  * within the window thread.
273  *
274  * The properties that have been applied are cleared from the structure by
275  * this function; so on return, whatever remains in the properties structure
276  * are those that were unchanged for some reason (probably because the
277  * underlying interface does not support changing that property on an open
278  * window).
279  */
283  if (!properties.is_any_specified()) {
284  // The base class has already handled this case.
285  return;
286  }
287 
288  if (properties.has_undecorated() ||
289  properties.has_fixed_size()) {
290  if (properties.has_undecorated()) {
291  _properties.set_undecorated(properties.get_undecorated());
292  properties.clear_undecorated();
293  }
294  if (properties.has_fixed_size()) {
295  _properties.set_fixed_size(properties.get_fixed_size());
296  properties.clear_fixed_size();
297  }
298  // When switching undecorated mode, Windows will keep the window at the
299  // current outer size, whereas we want to keep it with the configured
300  // inner size. Store the current size and origin.
301  LPoint2i top_left = _properties.get_origin();
302  LPoint2i bottom_right = top_left + _properties.get_size();
303 
304  DWORD window_style = make_style(_properties);
305  SetWindowLong(_hWnd, GWL_STYLE, window_style);
306 
307  // Now calculate the proper size and origin with the new window style.
308  RECT view_rect;
309  SetRect(&view_rect, top_left[0], top_left[1],
310  bottom_right[0], bottom_right[1]);
311  WINDOWINFO wi;
312  GetWindowInfo(_hWnd, &wi);
313  AdjustWindowRectEx(&view_rect, wi.dwStyle, FALSE, wi.dwExStyle);
314 
315  // We need to call this to ensure that the style change takes effect.
316  SetWindowPos(_hWnd, HWND_NOTOPMOST, view_rect.left, view_rect.top,
317  view_rect.right - view_rect.left,
318  view_rect.bottom - view_rect.top,
319  SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED |
320  SWP_NOSENDCHANGING | SWP_SHOWWINDOW);
321  }
322 
323  if (properties.has_title()) {
324  std::string title = properties.get_title();
325  _properties.set_title(title);
326  TextEncoder encoder;
327  wstring title_w = encoder.decode_text(title);
328  SetWindowTextW(_hWnd, title_w.c_str());
329  properties.clear_title();
330  }
331 
332  if (properties.has_icon_filename()) {
333  HICON icon = get_icon(properties.get_icon_filename());
334  if (icon != 0) {
335  ::SendMessage(_hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon);
336  ::SendMessage(_hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon);
337 
338  _properties.set_icon_filename(properties.get_icon_filename());
339  properties.clear_icon_filename();
340  }
341  }
342 
343  if (properties.has_cursor_hidden()) {
344  bool hide_cursor = properties.get_cursor_hidden();
345  _properties.set_cursor_hidden(hide_cursor);
346  if (_cursor_window == this) {
347  hide_or_show_cursor(hide_cursor);
348  }
349 
350  properties.clear_cursor_hidden();
351  }
352 
353  if (properties.has_cursor_filename()) {
354  Filename filename = properties.get_cursor_filename();
355  _properties.set_cursor_filename(filename);
356 
357  _cursor = get_cursor(filename);
358  if (_cursor == 0) {
359  _cursor = LoadCursor(nullptr, IDC_ARROW);
360  }
361 
362  if (_cursor_window == this) {
363  SetCursor(_cursor);
364  }
365 
366  properties.clear_cursor_filename();
367  }
368 
369  if (properties.has_z_order()) {
370  WindowProperties::ZOrder last_z_order = _properties.get_z_order();
371  _properties.set_z_order(properties.get_z_order());
372  adjust_z_order(last_z_order, properties.get_z_order());
373 
374  properties.clear_z_order();
375  }
376 
377  if (properties.has_foreground() && properties.get_foreground()) {
378  if (!SetActiveWindow(_hWnd)) {
379  windisplay_cat.warning()
380  << "SetForegroundWindow() failed!\n";
381  } else {
382  _properties.set_foreground(true);
383  }
384 
385  properties.clear_foreground();
386  }
387 
388  if (properties.has_minimized()) {
389  if (_properties.get_minimized() != properties.get_minimized()) {
390  if (properties.get_minimized()) {
391  ShowWindow(_hWnd, SW_MINIMIZE);
392  } else {
393  ShowWindow(_hWnd, SW_RESTORE);
394  }
395  _properties.set_minimized(properties.get_minimized());
396  _properties.set_foreground(!properties.get_minimized());
397  }
398  properties.clear_minimized();
399  }
400 
401  if (properties.has_fullscreen()) {
402  if (properties.get_fullscreen() && !is_fullscreen()) {
403  if (do_fullscreen_switch()){
404  _properties.set_fullscreen(true);
405  properties.clear_fullscreen();
406  } else {
407  windisplay_cat.warning()
408  << "Switching to fullscreen mode failed!\n";
409  }
410  } else if (!properties.get_fullscreen() && is_fullscreen()){
411  if (do_windowed_switch()){
412  _properties.set_fullscreen(false);
413  properties.clear_fullscreen();
414  } else {
415  windisplay_cat.warning()
416  << "Switching to windowed mode failed!\n";
417  }
418  }
419  }
420 
421  if (properties.has_mouse_mode()) {
422  if (properties.get_mouse_mode() != _properties.get_mouse_mode()) {
423  switch (properties.get_mouse_mode()) {
424  case WindowProperties::M_absolute:
425  case WindowProperties::M_relative: // not implemented, treat as absolute
426 
427  if (_properties.get_mouse_mode() == WindowProperties::M_confined) {
428  ClipCursor(nullptr);
429  windisplay_cat.info() << "Unconfining cursor from window\n";
430  }
431  _properties.set_mouse_mode(WindowProperties::M_absolute);
432  break;
433 
434  case WindowProperties::M_confined:
435  if (confine_cursor()) {
436  _properties.set_mouse_mode(WindowProperties::M_confined);
437  }
438  break;
439  }
440  }
441  properties.clear_mouse_mode();
442  }
443 
444 }
445 
446 /**
447  * To be called at the end of the frame, after the window has successfully
448  * been drawn and is ready to be flipped (if appropriate).
449  */
450 void WinGraphicsWindow::
451 trigger_flip() {
452  GraphicsWindow::trigger_flip();
453 
454  if (!get_unexposed_draw()) {
455  // Now that we've drawn or whatever, invalidate the rectangle so we won't
456  // redraw again until we get the WM_PAINT message.
457 
458  InvalidateRect(_hWnd, nullptr, FALSE);
459  _got_expose_event = false;
460 
461  if (windisplay_cat.is_spam()) {
462  windisplay_cat.spam()
463  << "InvalidateRect: " << this << "\n";
464  }
465  }
466 }
467 
468 /**
469  * Closes the window right now. Called from the window thread.
470  */
471 void WinGraphicsWindow::
472 close_window() {
473  set_cursor_out_of_window();
474  DestroyWindow(_hWnd);
475 
476  if (is_fullscreen()) {
477  // revert to default display mode.
478  do_fullscreen_disable();
479  }
480 
481  // Remove the window handle from our global map.
482  _window_handles.erase(_hWnd);
483  _hWnd = (HWND)0;
484 
485  GraphicsWindow::close_window();
486 }
487 
488 /**
489  * Opens the window right now. Called from the window thread. Returns true
490  * if the window is successfully opened, or false if there was a problem.
491  */
492 bool WinGraphicsWindow::
493 open_window() {
494  if (_properties.has_cursor_filename()) {
495  _cursor = get_cursor(_properties.get_cursor_filename());
496  }
497  if (_cursor == 0) {
498  _cursor = LoadCursor(nullptr, IDC_ARROW);
499  }
500  bool want_foreground = (!_properties.has_foreground() || _properties.get_foreground());
501  bool want_minimized = (_properties.has_minimized() && _properties.get_minimized()) && !want_foreground;
502 
503  HWND old_foreground_window = GetForegroundWindow();
504 
505  // Store the current window pointer in _creating_window, so we can call
506  // CreateWindow() and know which window it is sending events to even before
507  // it gives us a handle. Warning: this is not thread safe!
508  _creating_window = this;
509  bool opened = open_graphic_window();
510  _creating_window = nullptr;
511 
512  if (!opened) {
513  return false;
514  }
515 
516  // Now that we have a window handle, store it in our global map, so future
517  // messages for this window can be routed properly.
518  _window_handles.insert(WindowHandles::value_type(_hWnd, this));
519 
520  // move window to top of zorder.
521  SetWindowPos(_hWnd, HWND_TOP, 0,0,0,0,
522  SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE);
523 
524  // need to do twice to override any minimized flags in StartProcessInfo
525  if (want_minimized) {
526  ShowWindow(_hWnd, SW_MINIMIZE);
527  ShowWindow(_hWnd, SW_MINIMIZE);
528  } else {
529  ShowWindow(_hWnd, SW_SHOWNORMAL);
530  ShowWindow(_hWnd, SW_SHOWNORMAL);
531  }
532 
533  HWND new_foreground_window = _hWnd;
534  if (!want_foreground) {
535  // If we specifically requested the window not to be on top, restore the
536  // previous foreground window (if we can).
537  new_foreground_window = old_foreground_window;
538  }
539 
540  if (!SetActiveWindow(new_foreground_window)) {
541  windisplay_cat.warning()
542  << "SetActiveWindow() failed!\n";
543  }
544 
545  // Let's aggressively call SetForegroundWindow() in addition to
546  // SetActiveWindow(). It seems to work in some cases to make the window
547  // come to the top, where SetActiveWindow doesn't work.
548  if (!SetForegroundWindow(new_foreground_window)) {
549  windisplay_cat.warning()
550  << "SetForegroundWindow() failed!\n";
551  }
552 
553  // Determine the initial open status of the IME.
554  _ime_open = false;
555  _ime_active = false;
556  HIMC hIMC = ImmGetContext(_hWnd);
557  if (hIMC != 0) {
558  _ime_open = (ImmGetOpenStatus(hIMC) != 0);
559  ImmReleaseContext(_hWnd, hIMC);
560  }
561 
562  // Registers to receive the WM_INPUT messages
563  if (_input_devices.size() > 1) {
564  RAWINPUTDEVICE Rid;
565  Rid.usUsagePage = 0x01;
566  Rid.usUsage = 0x02;
567  Rid.dwFlags = 0;// RIDEV_NOLEGACY; // adds HID mouse and also ignores legacy mouse messages
568  Rid.hwndTarget = _hWnd;
569  RegisterRawInputDevices(&Rid, 1, sizeof (Rid));
570  }
571 
572  // Create a WindowHandle for ourselves
573  _window_handle = NativeWindowHandle::make_win(_hWnd);
574 
575  // Actually, we want a WinWindowHandle.
576  _window_handle = new WinWindowHandle(this, *_window_handle);
577 
578  // And tell our parent window that we're now its child.
579  if (_parent_window_handle != nullptr) {
580  _parent_window_handle->attach_child(_window_handle);
581  }
582 
583  // set us as the focus window for keyboard input
584  set_focus();
585 
586  // Try initializing the touch function pointers.
587  static bool initialized = false;
588  if (!initialized) {
589  initialized = true;
590  HMODULE user32 = GetModuleHandleA("user32.dll");
591  if (user32) {
592  // Introduced in Windows 7.
593  pRegisterTouchWindow = (PFN_REGISTERTOUCHWINDOW)GetProcAddress(user32, "RegisterTouchWindow");
594  pGetTouchInputInfo = (PFN_GETTOUCHINPUTINFO)GetProcAddress(user32, "GetTouchInputInfo");
595  pCloseTouchInputHandle = (PFN_CLOSETOUCHINPUTHANDLE)GetProcAddress(user32, "CloseTouchInputHandle");
596  }
597  }
598 
599  // Register for Win7 touch events.
600  if (pRegisterTouchWindow != nullptr) {
601  pRegisterTouchWindow(_hWnd, 0);
602  }
603 
604  return true;
605 }
606 
607 /**
608  * Creates the array of input devices. The first one is always the system
609  * mouse and keyboard. Each subsequent one is a raw mouse device. Also
610  * initializes a parallel array, _input_device_handle, with the win32 handle
611  * of each raw input device.
612  */
613 void WinGraphicsWindow::
614 initialize_input_devices() {
615  UINT nInputDevices;
616  PRAWINPUTDEVICELIST pRawInputDeviceList;
617 
618  nassertv(_input_devices.size() == 0);
619 
620  // Clear the handle array, and set up the system keyboardmouse
621  memset(_input_device_handle, 0, sizeof(_input_device_handle));
622  PT(GraphicsWindowInputDevice) device =
623  GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse");
624  add_input_device(device);
625  _input = device;
626 
627  // Get the number of devices.
628  if (GetRawInputDeviceList(nullptr, &nInputDevices, sizeof(RAWINPUTDEVICELIST)) != 0) {
629  return;
630  }
631 
632  // Allocate the array to hold the DeviceList
633  pRawInputDeviceList = (PRAWINPUTDEVICELIST)alloca(sizeof(RAWINPUTDEVICELIST) * nInputDevices);
634  if (pRawInputDeviceList==0) {
635  return;
636  }
637 
638  // Fill the Array
639  if (GetRawInputDeviceList(pRawInputDeviceList, &nInputDevices, sizeof(RAWINPUTDEVICELIST)) == -1) {
640  return;
641  }
642 
643  // Loop through all raw devices and find the raw mice
644  for (int i = 0; i < (int)nInputDevices; i++) {
645  if (pRawInputDeviceList[i].dwType == RIM_TYPEMOUSE) {
646  // Fetch information about specified mouse device.
647  UINT nSize;
648  if (GetRawInputDeviceInfoA(pRawInputDeviceList[i].hDevice, RIDI_DEVICENAME, (LPVOID)0, &nSize) != 0) {
649  return;
650  }
651  char *psName = (char*)alloca(sizeof(TCHAR) * nSize);
652  if (psName == 0) return;
653  if (GetRawInputDeviceInfoA(pRawInputDeviceList[i].hDevice, RIDI_DEVICENAME, (LPVOID)psName, &nSize) < 0) {
654  return;
655  }
656 
657  // If it's not an RDP mouse, add it to the list of raw mice.
658  if (strncmp(psName,"\\??\\Root#RDP_MOU#0000#",22)!=0) {
659  if (_input_devices.size() < 32) {
660  if (strncmp(psName,"\\??\\",4)==0) psName += 4;
661  char *pound1 = strchr(psName,'#');
662  char *pound2 = pound1 ? strchr(pound1+1,'#') : 0;
663  char *pound3 = pound2 ? strchr(pound2+1,'#') : 0;
664  if (pound3) *pound3 = 0;
665  for (char *p = psName; *p; p++) {
666  if (!isalnum(*p)) {
667  *p = '_';
668  }
669  }
670  if (pound2) *pound2 = '.';
671  _input_device_handle[_input_devices.size()] = pRawInputDeviceList[i].hDevice;
672 
673  PT(GraphicsWindowInputDevice) device = GraphicsWindowInputDevice::pointer_only(this, psName);
674  device->set_pointer_in_window(0, 0);
675  add_input_device(device);
676  }
677  }
678  }
679  }
680 }
681 
682 /**
683  * This is a hook for derived classes to do something special, if necessary,
684  * when a fullscreen window has been minimized. The given WindowProperties
685  * struct will be applied to this window's properties after this function
686  * returns.
687  */
688 void WinGraphicsWindow::
689 fullscreen_minimized(WindowProperties &) {
690 }
691 
692 /**
693  * This is a hook for derived classes to do something special, if necessary,
694  * when a fullscreen window has been restored after being minimized. The
695  * given WindowProperties struct will be applied to this window's properties
696  * after this function returns.
697  */
698 void WinGraphicsWindow::
699 fullscreen_restored(WindowProperties &) {
700 }
701 
702 /**
703  * Called from the window thread in response to a request from within the code
704  * (via request_properties()) to change the size and/or position of the
705  * window. Returns true if the window is successfully changed, or false if
706  * there was a problem.
707  */
708 bool WinGraphicsWindow::
709 do_reshape_request(int x_origin, int y_origin, bool has_origin,
710  int x_size, int y_size) {
711  if (windisplay_cat.is_debug()) {
712  windisplay_cat.debug()
713  << "Got reshape request (" << x_origin << ", " << y_origin
714  << ", " << has_origin << ", " << x_size << ", " << y_size << ")\n";
715  }
716  if (!is_fullscreen()) {
717  if (has_origin) {
718  // A coordinate of -2 means to center the window in its client area.
719  if (x_origin == -2) {
720  x_origin = 0.5 * (_pipe->get_display_width() - x_size);
721  }
722  if (y_origin == -2) {
723  y_origin = 0.5 * (_pipe->get_display_height() - y_size);
724  }
725  _properties.set_origin(x_origin, y_origin);
726 
727  if (x_origin == -1 && y_origin == -1) {
728  x_origin = 0;
729  y_origin = 0;
730  has_origin = false;
731  }
732  }
733 
734  // Compute the appropriate size and placement for the window, including
735  // decorations.
736  RECT view_rect;
737  SetRect(&view_rect, x_origin, y_origin,
738  x_origin + x_size, y_origin + y_size);
739  WINDOWINFO wi;
740  GetWindowInfo(_hWnd, &wi);
741  AdjustWindowRectEx(&view_rect, wi.dwStyle, FALSE, wi.dwExStyle);
742 
743  UINT flags = SWP_NOZORDER | SWP_NOSENDCHANGING;
744 
745  if (has_origin) {
746  x_origin = view_rect.left;
747  y_origin = view_rect.top;
748  } else {
749  x_origin = CW_USEDEFAULT;
750  y_origin = CW_USEDEFAULT;
751  flags |= SWP_NOMOVE;
752  }
753 
754  SetWindowPos(_hWnd, nullptr, x_origin, y_origin,
755  view_rect.right - view_rect.left,
756  view_rect.bottom - view_rect.top,
757  flags);
758 
759  // If we are in confined mode, we must update the clip region.
760  if (_properties.has_mouse_mode() &&
761  _properties.get_mouse_mode() == WindowProperties::M_confined) {
762  confine_cursor();
763  }
764 
765  handle_reshape();
766  return true;
767  }
768 
769  // Resizing a fullscreen window is a little trickier.
770  return do_fullscreen_resize(x_size, y_size);
771 }
772 
773 /**
774  * Called in the window thread when the window size or location is changed,
775  * this updates the properties structure accordingly.
776  */
777 void WinGraphicsWindow::
778 handle_reshape() {
779  RECT view_rect;
780  if (!GetClientRect(_hWnd, &view_rect)) {
781  // Sometimes we get a "reshape" before the window is fully created, in
782  // which case GetClientRect() ought to fail. Ignore this.
783  if (windisplay_cat.is_debug()) {
784  windisplay_cat.debug()
785  << "GetClientRect() failed in handle_reshape. Ignoring.\n";
786  }
787  return;
788  }
789 
790  // But in practice, GetClientRect() doesn't really fail, but just returns
791  // all zeroes. Ignore this too.
792  if (view_rect.left == 0 && view_rect.right == 0 &&
793  view_rect.bottom == 0 && view_rect.top == 0) {
794  if (windisplay_cat.is_debug()) {
795  windisplay_cat.debug()
796  << "GetClientRect() returned all zeroes in handle_reshape. Ignoring.\n";
797  }
798  return;
799  }
800 
801  bool result = (FALSE != ClientToScreen(_hWnd, (POINT*)&view_rect.left)); // translates top,left pnt
802  if (result) {
803  result = (FALSE != ClientToScreen(_hWnd, (POINT*)&view_rect.right)); // translates right,bottom pnt
804  }
805 
806  if (!result) {
807  if (windisplay_cat.is_debug()) {
808  windisplay_cat.debug()
809  << "ClientToScreen() failed in handle_reshape. Ignoring.\n";
810  }
811  return;
812  }
813 
814  WindowProperties properties;
815  properties.set_size((view_rect.right - view_rect.left),
816  (view_rect.bottom - view_rect.top));
817 
818  // _props origin should reflect upper left of view rectangle
819  properties.set_origin(view_rect.left, view_rect.top);
820 
821  if (windisplay_cat.is_debug()) {
822  windisplay_cat.debug()
823  << "reshape to origin: (" << properties.get_x_origin() << ","
824  << properties.get_y_origin() << "), size: (" << properties.get_x_size()
825  << "," << properties.get_y_size() << ")\n";
826  }
827 
828  adjust_z_order();
829  system_changed_properties(properties);
830 }
831 
832 /**
833  * Called in the window thread to resize a fullscreen window.
834  */
835 bool WinGraphicsWindow::
836 do_fullscreen_resize(int x_size, int y_size) {
837  HWND hDesktopWindow = GetDesktopWindow();
838  HDC scrnDC = GetDC(hDesktopWindow);
839  DWORD dwFullScreenBitDepth = GetDeviceCaps(scrnDC, BITSPIXEL);
840  ReleaseDC(hDesktopWindow, scrnDC);
841 
842  // resize will always leave screen bitdepth unchanged
843 
844  // allowing resizing of lowvidmem cards to > 640x480. why? I'll assume
845  // check was already done by caller, so he knows what he wants
846 
847  DEVMODE dm;
848  if (!find_acceptable_display_mode(x_size, y_size,
849  dwFullScreenBitDepth, dm)) {
850  windisplay_cat.error()
851  << "window resize(" << x_size << ", " << y_size
852  << ") failed, no compatible fullscreen display mode found!\n";
853  return false;
854  }
855 
856  // this causes WM_SIZE msg to be produced
857  SetWindowPos(_hWnd, nullptr, 0,0, x_size, y_size,
858  SWP_NOZORDER | SWP_NOMOVE | SWP_NOSENDCHANGING);
859  int chg_result = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
860 
861  if (chg_result != DISP_CHANGE_SUCCESSFUL) {
862  windisplay_cat.error()
863  << "resize ChangeDisplaySettings failed (error code: "
864  << chg_result << ") for specified res: "
865  << dm.dmPelsWidth << " x " << dm.dmPelsHeight
866  << " x " << dm.dmBitsPerPel << ", "
867  << dm.dmDisplayFrequency << " Hz\n";
868  return false;
869  }
870 
871  _fullscreen_display_mode = dm;
872 
873  windisplay_cat.info()
874  << "Resized fullscreen window to " << x_size << ", " << y_size
875  << " bitdepth " << dwFullScreenBitDepth << ", "
876  << dm.dmDisplayFrequency << "Hz\n";
877 
878  _properties.set_size(x_size, y_size);
879  set_size_and_recalc(x_size, y_size);
880 
881  return true;
882 }
883 
884 /**
885  * Called in the set_properties_now function to switch to fullscreen.
886  */
887 bool WinGraphicsWindow::
888 do_fullscreen_switch() {
889  if (!do_fullscreen_enable()) {
890  // Couldn't get fullscreen.
891  return false;
892  }
893 
894  WindowProperties props(_properties);
895  props.set_fullscreen(true);
896  DWORD window_style = make_style(props);
897  SetWindowLong(_hWnd, GWL_STYLE, window_style);
898 
899  WINDOW_METRICS metrics;
900  bool has_origin;
901  if (!calculate_metrics(true, window_style, metrics, has_origin)){
902  return false;
903  }
904 
905  SetWindowPos(_hWnd, HWND_NOTOPMOST, 0, 0, metrics.width, metrics.height,
906  SWP_FRAMECHANGED | SWP_SHOWWINDOW);
907  return true;
908 }
909 
910 /**
911  * Called in the set_properties_now function to switch to windowed mode.
912  */
913 bool WinGraphicsWindow::
914 do_windowed_switch() {
915  do_fullscreen_disable();
916 
917  WindowProperties props(_properties);
918  props.set_fullscreen(false);
919  DWORD window_style = make_style(props);
920  SetWindowLong(_hWnd, GWL_STYLE, window_style);
921 
922  WINDOW_METRICS metrics;
923  bool has_origin;
924 
925  if (!calculate_metrics(false, window_style, metrics, has_origin)){
926  return false;
927  }
928 
929  // We send SWP_FRAMECHANGED so that the new styles are taken into account.
930  // Also, we place the Windows at 0,0 to play safe until we decide how to get
931  // Panda to remember the windowed origin.
932 
933  SetWindowPos(_hWnd, HWND_NOTOPMOST, 0, 0,
934  metrics.width, metrics.height,
935  SWP_FRAMECHANGED | SWP_SHOWWINDOW);
936 
937  // If we had a confined cursor, we must reconfine it now.
938  if (_properties.has_mouse_mode() &&
939  _properties.get_mouse_mode() == WindowProperties::M_confined) {
940  confine_cursor();
941  }
942 
943  return true;
944 }
945 
946 /**
947  * Called before creating a fullscreen window to give the driver a chance to
948  * adjust the particular resolution request, if necessary.
949  */
950 void WinGraphicsWindow::
951 reconsider_fullscreen_size(DWORD &, DWORD &, DWORD &) {
952 }
953 
954 /**
955  * Some windows graphics contexts (e.g. DirectX) require special support to
956  * enable the displaying of an overlay window (particularly the IME window)
957  * over the fullscreen graphics window. This is a hook for the window to
958  * enable or disable that mode when necessary.
959  */
960 void WinGraphicsWindow::
961 support_overlay_window(bool) {
962 }
963 
964 /**
965  * Constructs a dwStyle for the specified mode, be it windowed or fullscreen.
966  */
967 DWORD WinGraphicsWindow::
968 make_style(const WindowProperties &properties) {
969  // from MSDN: An OpenGL window has its own pixel format. Because of this,
970  // only device contexts retrieved for the client area of an OpenGL window
971  // are allowed to draw into the window. As a result, an OpenGL window
972  // should be created with the WS_CLIPCHILDREN and WS_CLIPSIBLINGS styles.
973  // Additionally, the window class attribute should not include the
974  // CS_PARENTDC style.
975 
976  DWORD window_style = WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
977 
978  if (properties.get_fullscreen()) {
979  window_style |= WS_SYSMENU;
980  } else if (!properties.get_undecorated()) {
981  window_style |= (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX);
982 
983  if (!properties.get_fixed_size()) {
984  window_style |= (WS_SIZEBOX | WS_MAXIMIZEBOX);
985  } else {
986  window_style |= WS_BORDER;
987  }
988  }
989  return window_style;
990 }
991 
992 /**
993  * Calculates the metrics for the specified mode, be it windowed or
994  * fullscreen.
995  */
996 bool WinGraphicsWindow::
997 calculate_metrics(bool fullscreen, DWORD window_style, WINDOW_METRICS &metrics,
998  bool &has_origin) {
999  metrics.x = 0;
1000  metrics.y = 0;
1001  has_origin = _properties.has_origin();
1002  if (!fullscreen && has_origin) {
1003  metrics.x = _properties.get_x_origin();
1004  metrics.y = _properties.get_y_origin();
1005 
1006  // A coordinate of -2 means to center the window in its client area.
1007  if (metrics.x == -2) {
1008  metrics.x = 0.5 * (_pipe->get_display_width() - _properties.get_x_size());
1009  }
1010  if (metrics.y == -2) {
1011  metrics.y = 0.5 * (_pipe->get_display_height() - _properties.get_y_size());
1012  }
1013  _properties.set_origin(metrics.x, metrics.y);
1014 
1015  if (metrics.x == -1 && metrics.y == -1) {
1016  metrics.x = 0;
1017  metrics.y = 0;
1018  has_origin = false;
1019  }
1020  }
1021 
1022  metrics.width = _properties.get_x_size();
1023  metrics.height = _properties.get_y_size();
1024 
1025  if (!fullscreen){
1026  RECT win_rect;
1027  SetRect(&win_rect, metrics.x, metrics.y,
1028  metrics.x + metrics.width, metrics.y + metrics.height);
1029 
1030  // Compute window size based on desired client area size
1031  if (!AdjustWindowRect(&win_rect, window_style, FALSE)) {
1032  windisplay_cat.error()
1033  << "AdjustWindowRect failed!" << endl;
1034  return false;
1035  }
1036 
1037  if (has_origin) {
1038  metrics.x = win_rect.left;
1039  metrics.y = win_rect.top;
1040  } else {
1041  metrics.x = CW_USEDEFAULT;
1042  metrics.y = CW_USEDEFAULT;
1043  }
1044  metrics.width = win_rect.right - win_rect.left;
1045  metrics.height = win_rect.bottom - win_rect.top;
1046  }
1047 
1048  return true;
1049 }
1050 
1051 /**
1052  * Creates a regular or fullscreen window.
1053  */
1054 bool WinGraphicsWindow::
1055 open_graphic_window() {
1056  DWORD window_style = make_style(_properties);
1057 
1058  wstring title;
1059  if (_properties.has_title()) {
1060  TextEncoder encoder;
1061  title = encoder.decode_text(_properties.get_title());
1062  }
1063 
1064  if (!_properties.has_size()) {
1065  // Just fill in a conservative default size if one isn't specified.
1066  _properties.set_size(640, 480);
1067  }
1068 
1069  WINDOW_METRICS metrics;
1070  bool has_origin;
1071  if (!calculate_metrics(fullscreen, window_style, metrics, has_origin)){
1072  return false;
1073  }
1074 
1075  const WindowClass &wclass = register_window_class(_properties);
1076  HINSTANCE hinstance = GetModuleHandle(nullptr);
1077 
1078  _hparent = nullptr;
1079 
1080  if (!fullscreen){
1081  WindowHandle *window_handle = _properties.get_parent_window();
1082  if (window_handle != nullptr) {
1083  windisplay_cat.info()
1084  << "Got parent_window " << *window_handle << "\n";
1085  WindowHandle::OSHandle *os_handle = window_handle->get_os_handle();
1086  if (os_handle != nullptr) {
1087  windisplay_cat.info()
1088  << "os_handle type " << os_handle->get_type() << "\n";
1089 
1090  if (os_handle->is_of_type(NativeWindowHandle::WinHandle::get_class_type())) {
1091  NativeWindowHandle::WinHandle *win_handle = DCAST(NativeWindowHandle::WinHandle, os_handle);
1092  _hparent = win_handle->get_handle();
1093  } else if (os_handle->is_of_type(NativeWindowHandle::IntHandle::get_class_type())) {
1094  NativeWindowHandle::IntHandle *int_handle = DCAST(NativeWindowHandle::IntHandle, os_handle);
1095  _hparent = (HWND)int_handle->get_handle();
1096  }
1097  }
1098  }
1099  _parent_window_handle = window_handle;
1100  } else {
1101  _parent_window_handle = nullptr;
1102  }
1103 
1104  if (!_hparent) { // This can be a regular window or a fullscreen window
1105  _hWnd = CreateWindowW(wclass._name.c_str(), title.c_str(), window_style,
1106  metrics.x, metrics.y,
1107  metrics.width,
1108  metrics.height,
1109  nullptr, nullptr, hinstance, 0);
1110  } else { // This is a regular window with a parent
1111  int x_origin = 0;
1112  int y_origin = 0;
1113 
1114  if (!fullscreen && has_origin) {
1115  x_origin = _properties.get_x_origin();
1116  y_origin = _properties.get_y_origin();
1117  }
1118 
1119  _hWnd = CreateWindowW(wclass._name.c_str(), title.c_str(),
1120  WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS ,
1121  x_origin, y_origin,
1122  _properties.get_x_size(), _properties.get_y_size(),
1123  _hparent, nullptr, hinstance, 0);
1124 
1125  if (_hWnd) {
1126  // join our keyboard state with the parents
1127 
1128  // Actually, let's not. Is there really any reason to do this? It
1129  // causes problems with the browser plugin--it deadlocks when the parent
1130  // process is waiting on the child process.
1131  // AttachThreadInput(GetWindowThreadProcessId(_hparent,NULL),
1132  // GetCurrentThreadId(),TRUE);
1133 
1134  WindowProperties properties;
1135  properties.set_foreground(true);
1136  system_changed_properties(properties);
1137  }
1138  }
1139 
1140  if (!_hWnd) {
1141  windisplay_cat.error()
1142  << "CreateWindow() failed!" << endl;
1143  show_error_message();
1144  return false;
1145  }
1146 
1147  // I'd prefer to CreateWindow after DisplayChange in case it messes up GL
1148  // somehow, but I need the window's black background to cover up the desktop
1149  // during the mode change.
1150 
1151  if (fullscreen){
1152  if (!do_fullscreen_enable()){
1153  return false;
1154  }
1155  }
1156 
1157  return true;
1158 }
1159 
1160 /**
1161  * This is a low-level function that just puts Windows in fullscreen mode.
1162  * Not to confuse with do_fullscreen_switch().
1163  */
1164 bool WinGraphicsWindow::
1165 do_fullscreen_enable() {
1166 
1167  HWND hDesktopWindow = GetDesktopWindow();
1168  HDC scrnDC = GetDC(hDesktopWindow);
1169  DWORD cur_bitdepth = GetDeviceCaps(scrnDC, BITSPIXEL);
1170  // DWORD drvr_ver = GetDeviceCaps(scrnDC, DRIVERVERSION); DWORD
1171  // cur_scrnwidth = GetDeviceCaps(scrnDC, HORZRES); DWORD cur_scrnheight =
1172  // GetDeviceCaps(scrnDC, VERTRES);
1173  ReleaseDC(hDesktopWindow, scrnDC);
1174 
1175  DWORD dwWidth = _properties.get_x_size();
1176  DWORD dwHeight = _properties.get_y_size();
1177  DWORD dwFullScreenBitDepth = cur_bitdepth;
1178 
1179  DEVMODE dm;
1180  reconsider_fullscreen_size(dwWidth, dwHeight, dwFullScreenBitDepth);
1181  if (!find_acceptable_display_mode(dwWidth, dwHeight, dwFullScreenBitDepth, dm)) {
1182  windisplay_cat.error()
1183  << "Videocard has no supported display resolutions at specified res ("
1184  << dwWidth << " x " << dwHeight << " x " << dwFullScreenBitDepth <<")\n";
1185  return false;
1186  }
1187 
1188  dm.dmPelsWidth = dwWidth;
1189  dm.dmPelsHeight = dwHeight;
1190  dm.dmBitsPerPel = dwFullScreenBitDepth;
1191  int chg_result = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
1192 
1193  if (chg_result != DISP_CHANGE_SUCCESSFUL) {
1194  windisplay_cat.error()
1195  << "ChangeDisplaySettings failed (error code: "
1196  << chg_result << ") for specified res: "
1197  << dm.dmPelsWidth << " x " << dm.dmPelsHeight
1198  << " x " << dm.dmBitsPerPel << ", "
1199  << dm.dmDisplayFrequency << " Hz\n";
1200  return false;
1201  }
1202 
1203  _fullscreen_display_mode = dm;
1204 
1205  _properties.set_origin(0, 0);
1206  _properties.set_size(dwWidth, dwHeight);
1207 
1208  return true;
1209 
1210 }
1211 
1212 /**
1213  * This is a low-level function that just gets Windows out of fullscreen mode.
1214  * Not to confuse with do_windowed_switch().
1215  */
1216 bool WinGraphicsWindow::
1217 do_fullscreen_disable() {
1218  int chg_result = ChangeDisplaySettings(nullptr, 0x0);
1219  if (chg_result != DISP_CHANGE_SUCCESSFUL) {
1220  windisplay_cat.warning()
1221  << "ChangeDisplaySettings failed to restore Windowed mode\n";
1222  return false;
1223  }
1224  return true;
1225 }
1226 
1227 /**
1228  * Adjusts the Z-order of a window after it has been moved.
1229  */
1230 void WinGraphicsWindow::
1231 adjust_z_order() {
1232  WindowProperties::ZOrder z_order = _properties.get_z_order();
1233  adjust_z_order(z_order, z_order);
1234 }
1235 
1236 /**
1237  * Adjusts the Z-order of a window after it has been moved.
1238  */
1239 void WinGraphicsWindow::
1240 adjust_z_order(WindowProperties::ZOrder last_z_order,
1241  WindowProperties::ZOrder this_z_order) {
1242  HWND order;
1243  bool do_change = false;
1244 
1245  switch (this_z_order) {
1246  case WindowProperties::Z_bottom:
1247  order = HWND_BOTTOM;
1248  do_change = true;
1249  break;
1250 
1251  case WindowProperties::Z_normal:
1252  if ((last_z_order != WindowProperties::Z_normal) &&
1253  // If we aren't changing the window order, don't move it to the top.
1254  (last_z_order != WindowProperties::Z_bottom ||
1255  _properties.get_foreground())
1256  // If the window was previously on the bottom, but it doesn't have
1257  // focus now, don't move it to the top; it will get moved the next
1258  // time we get focus.
1259  ) {
1260  order = HWND_NOTOPMOST;
1261  do_change = true;
1262  }
1263  break;
1264 
1265  case WindowProperties::Z_top:
1266  order = HWND_TOPMOST;
1267  do_change = true;
1268  break;
1269  }
1270  if (do_change) {
1271  BOOL result = SetWindowPos(_hWnd, order, 0,0,0,0,
1272  SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE);
1273  if (!result) {
1274  windisplay_cat.warning()
1275  << "SetWindowPos failed.\n";
1276  }
1277  }
1278 }
1279 
1280 /**
1281  * Intended to be called whenever mouse motion is detected within the window,
1282  * this indicates that the mouse is within the window and tells Windows that
1283  * we want to be told when the mouse leaves the window.
1284  */
1285 void WinGraphicsWindow::
1286 track_mouse_leaving(HWND hwnd) {
1287  WinGraphicsPipe *winpipe;
1288  DCAST_INTO_V(winpipe, _pipe);
1289 
1290  TRACKMOUSEEVENT tme = {
1291  sizeof(TRACKMOUSEEVENT),
1292  TME_LEAVE,
1293  hwnd,
1294  0
1295  };
1296 
1297  // tell win32 to post WM_MOUSELEAVE msgs
1298  BOOL bSucceeded = TrackMouseEvent(&tme);
1299 
1300  if (!bSucceeded && windisplay_cat.is_debug()) {
1301  windisplay_cat.debug()
1302  << "TrackMouseEvent failed!, LastError=" << GetLastError() << endl;
1303  }
1304 
1305  _tracking_mouse_leaving = true;
1306 }
1307 
1308 /**
1309  * Confines the mouse cursor to the window.
1310  */
1311 bool WinGraphicsWindow::
1312 confine_cursor() {
1313  RECT clip;
1314  if (!GetWindowRect(_hWnd, &clip)) {
1315  windisplay_cat.warning()
1316  << "GetWindowRect() failed, cannot confine cursor.\n";
1317  return false;
1318  } else {
1319  windisplay_cat.info()
1320  << "ClipCursor() to " << clip.left << "," << clip.top << " to "
1321  << clip.right << "," << clip.bottom << endl;
1322 
1323  if (!ClipCursor(&clip)) {
1324  windisplay_cat.warning()
1325  << "Failed to confine cursor to window.\n";
1326  return false;
1327  } else {
1328  return true;
1329  }
1330  }
1331 }
1332 
1333 /**
1334  * Attempts to set this window as the "focus" window, so that keyboard events
1335  * come here.
1336  */
1337 void WinGraphicsWindow::
1338 set_focus() {
1339  if (SetFocus(_hWnd) == nullptr && GetLastError() != 0) {
1340  // If the SetFocus() request failed, maybe we're running in the plugin
1341  // environment on Vista, with UAC enabled. In this case, we're not
1342  // allowed to assign focus to the Panda window for some stupid reason. So
1343  // instead, we have to ask the parent window (in the browser process) to
1344  // proxy our keyboard events for us.
1345  if (_parent_window_handle != nullptr && _window_handle != nullptr) {
1346  _parent_window_handle->request_keyboard_focus(_window_handle);
1347  } else {
1348  // Otherwise, something is wrong.
1349  windisplay_cat.error()
1350  << "SetFocus failed: " << GetLastError() << "\n";
1351  }
1352  }
1353 }
1354 
1355 /**
1356  * This is called to receive a keyboard event generated by proxy by another
1357  * window in a parent process. This hacky system is used in the web plugin
1358  * system to allow the Panda window to receive keyboard events on Vista, which
1359  * doesn't allow the Panda window to set keyboard focus to itself.
1360  */
1362 receive_windows_message(unsigned int msg, int wparam, int lparam) {
1363  // Well, we'll just deliver this directly to window_proc(), supplying our
1364  // own window handle. For the most part, we don't care about the window
1365  // handle anyway, but this might become an issue for the IME. TODO:
1366  // investigate IME issues.
1367 
1368  window_proc(_hWnd, msg, wparam, lparam);
1369 }
1370 
1371 /**
1372  * This is the nonstatic window_proc function. It is called to handle window
1373  * events for this particular window.
1374  */
1376 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
1377  if (windisplay_cat.is_spam()) {
1378  windisplay_cat.spam()
1380  << " window_proc(" << (void *)this << ", " << hwnd << ", "
1381  << msg << ", " << wparam << ", " << lparam << ")\n";
1382  }
1383  WindowProperties properties;
1384 
1385  switch (msg) {
1386  case WM_MOUSEMOVE:
1387  if (!_tracking_mouse_leaving) {
1388  // need to re-call TrackMouseEvent every time mouse re-enters window
1389  track_mouse_leaving(hwnd);
1390  }
1391  set_cursor_in_window();
1392  if(handle_mouse_motion(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam))))
1393  return 0;
1394  break;
1395 
1396  case WM_INPUT:
1397  handle_raw_input((HRAWINPUT)lparam);
1398  break;
1399 
1400  case WM_MOUSELEAVE:
1401  _tracking_mouse_leaving = false;
1402  handle_mouse_exit();
1403  set_cursor_out_of_window();
1404  break;
1405 
1406  case WM_CREATE:
1407  {
1408  track_mouse_leaving(hwnd);
1409  ClearToBlack(hwnd, _properties);
1410 
1411  POINT cpos;
1412  GetCursorPos(&cpos);
1413  ScreenToClient(hwnd, &cpos);
1414  RECT clientRect;
1415  GetClientRect(hwnd, &clientRect);
1416  if (PtInRect(&clientRect,cpos)) {
1417  set_cursor_in_window(); // should window focus be true as well?
1418  } else {
1419  set_cursor_out_of_window();
1420  }
1421  }
1422  break;
1423 
1424  /*
1425  case WM_SHOWWINDOW:
1426  // You'd think WM_SHOWWINDOW would be just the thing for embedded windows,
1427  // but it turns out it's not sent to the child windows when the parent is
1428  // minimized. I guess it's only sent for an explicit call to ShowWindow,
1429  // phooey.
1430  {
1431  if (windisplay_cat.is_debug()) {
1432  windisplay_cat.debug()
1433  << "WM_SHOWWINDOW: " << hwnd << ", " << wparam << "\n";
1434  }
1435  if (wparam) {
1436  // Window is being shown.
1437  properties.set_minimized(false);
1438  } else {
1439  // Window is being hidden.
1440  properties.set_minimized(true);
1441  }
1442  system_changed_properties(properties);
1443  }
1444  break;
1445  */
1446 
1447  case WM_CLOSE:
1448  // This is a message from the system indicating that the user has
1449  // requested to close the window (e.g. alt-f4).
1450  {
1451  std::string close_request_event = get_close_request_event();
1452  if (!close_request_event.empty()) {
1453  // In this case, the app has indicated a desire to intercept the
1454  // request and process it directly.
1455  throw_event(close_request_event);
1456  return 0;
1457 
1458  } else {
1459  // In this case, the default case, the app does not intend to service
1460  // the request, so we do by closing the window.
1461  close_window();
1462  properties.set_open(false);
1463  system_changed_properties(properties);
1464 
1465  // TODO: make sure we release the GSG properly.
1466  }
1467  }
1468  break;
1469 
1470  case WM_CHILDACTIVATE:
1471  if (windisplay_cat.is_debug()) {
1472  windisplay_cat.debug()
1473  << "WM_CHILDACTIVATE: " << hwnd << "\n";
1474  }
1475  break;
1476 
1477  case WM_ACTIVATE:
1478  if (windisplay_cat.is_debug()) {
1479  windisplay_cat.debug()
1480  << "WM_ACTIVATE: " << hwnd << ", " << wparam << ", " << lparam << "\n";
1481  }
1482  properties.set_minimized((wparam & 0xffff0000) != 0);
1483  if ((wparam & 0xffff) != WA_INACTIVE)
1484  {
1485  properties.set_foreground(true);
1486  if (is_fullscreen())
1487  {
1488  // When a fullscreen window goes active, it automatically gets un-
1489  // minimized.
1490  int chg_result =
1491  ChangeDisplaySettings(&_fullscreen_display_mode, CDS_FULLSCREEN);
1492  if (chg_result != DISP_CHANGE_SUCCESSFUL) {
1493  const DEVMODE &dm = _fullscreen_display_mode;
1494  windisplay_cat.error()
1495  << "restore ChangeDisplaySettings failed (error code: "
1496  << chg_result << ") for specified res: "
1497  << dm.dmPelsWidth << " x " << dm.dmPelsHeight
1498  << " x " << dm.dmBitsPerPel << ", "
1499  << dm.dmDisplayFrequency << " Hz\n";
1500  }
1501 
1502  GdiFlush();
1503  SetWindowPos(_hWnd, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOOWNERZORDER);
1504  fullscreen_restored(properties);
1505  }
1506 
1507  // If we had a confined cursor, we must reconfine it upon activation.
1508  if (_properties.has_mouse_mode() &&
1509  _properties.get_mouse_mode() == WindowProperties::M_confined) {
1510  if (!confine_cursor()) {
1511  properties.set_mouse_mode(WindowProperties::M_absolute);
1512  }
1513  }
1514  }
1515  else
1516  {
1517  properties.set_foreground(false);
1518  if (is_fullscreen())
1519  {
1520  // When a fullscreen window goes inactive, it automatically gets
1521  // minimized.
1522  properties.set_minimized(true);
1523 
1524  // It seems order is important here. We must minimize the window
1525  // before restoring the display settings, or risk losing the
1526  // graphics context.
1527  ShowWindow(_hWnd, SW_MINIMIZE);
1528  GdiFlush();
1529  do_fullscreen_disable();
1530  fullscreen_minimized(properties);
1531  }
1532  }
1533 
1534  adjust_z_order();
1535  system_changed_properties(properties);
1536  break;
1537 
1538  case WM_SIZE:
1539  // Actually, since we don't return in WM_WINDOWPOSCHANGED, WM_SIZE won't
1540  // end up being called at all. This is more efficient according to MSDN.
1541  if (windisplay_cat.is_debug()) {
1542  windisplay_cat.debug()
1543  << "WM_SIZE: " << hwnd << ", " << wparam << "\n";
1544  }
1545  break;
1546 
1547  case WM_EXITSIZEMOVE:
1548  if (windisplay_cat.is_debug()) {
1549  windisplay_cat.debug()
1550  << "WM_EXITSIZEMOVE: " << hwnd << ", " << wparam << "\n";
1551  }
1552 
1553  // If we had a confined cursor, we must reconfine it upon a resize.
1554  if (_properties.has_mouse_mode() &&
1555  _properties.get_mouse_mode() == WindowProperties::M_confined) {
1556  confine_cursor();
1557  }
1558  break;
1559 
1560  case WM_WINDOWPOSCHANGED:
1561  if (windisplay_cat.is_debug()) {
1562  windisplay_cat.debug()
1563  << "WM_WINDOWPOSCHANGED: " << hwnd << ", " << wparam << "\n";
1564  }
1565  if (_hWnd != nullptr) {
1566  handle_reshape();
1567  }
1568  adjust_z_order();
1569  return 0;
1570 
1571  case WM_PAINT:
1572  // In response to WM_PAINT, we check to see if there are any update
1573  // regions at all; if there are, we declare the window exposed. This is
1574  // used to implement !_unexposed_draw.
1575  if (GetUpdateRect(_hWnd, nullptr, false)) {
1576  if (windisplay_cat.is_spam()) {
1577  windisplay_cat.spam()
1578  << "Got update regions: " << this << "\n";
1579  }
1580  _got_expose_event = true;
1581  }
1582  break;
1583 
1584  case WM_LBUTTONDOWN:
1585  if (_lost_keypresses) {
1586  resend_lost_keypresses();
1587  }
1588  SetCapture(hwnd);
1589  _input->set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam)));
1590  _input->button_down(MouseButton::button(0), get_message_time());
1591 
1592  // A button-click in the window means to grab the keyboard focus.
1593  set_focus();
1594  return 0;
1595 
1596  case WM_MBUTTONDOWN:
1597  if (_lost_keypresses) {
1598  resend_lost_keypresses();
1599  }
1600  SetCapture(hwnd);
1601  _input->set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam)));
1602  _input->button_down(MouseButton::button(1), get_message_time());
1603  // A button-click in the window means to grab the keyboard focus.
1604  set_focus();
1605  return 0;
1606 
1607  case WM_RBUTTONDOWN:
1608  if (_lost_keypresses) {
1609  resend_lost_keypresses();
1610  }
1611  SetCapture(hwnd);
1612  _input->set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam)));
1613  _input->button_down(MouseButton::button(2), get_message_time());
1614  // A button-click in the window means to grab the keyboard focus.
1615  set_focus();
1616  return 0;
1617 
1618  case WM_XBUTTONDOWN:
1619  {
1620  if (_lost_keypresses) {
1621  resend_lost_keypresses();
1622  }
1623  SetCapture(hwnd);
1624  int whichButton = GET_XBUTTON_WPARAM(wparam);
1625  _input->set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam)));
1626  if (whichButton == XBUTTON1) {
1627  _input->button_down(MouseButton::button(3), get_message_time());
1628  } else if (whichButton == XBUTTON2) {
1629  _input->button_down(MouseButton::button(4), get_message_time());
1630  }
1631  }
1632  return 0;
1633 
1634  case WM_LBUTTONUP:
1635  if (_lost_keypresses) {
1636  resend_lost_keypresses();
1637  }
1638  ReleaseCapture();
1639  _input->button_up(MouseButton::button(0), get_message_time());
1640  return 0;
1641 
1642  case WM_MBUTTONUP:
1643  if (_lost_keypresses) {
1644  resend_lost_keypresses();
1645  }
1646  ReleaseCapture();
1647  _input->button_up(MouseButton::button(1), get_message_time());
1648  return 0;
1649 
1650  case WM_RBUTTONUP:
1651  if (_lost_keypresses) {
1652  resend_lost_keypresses();
1653  }
1654  ReleaseCapture();
1655  _input->button_up(MouseButton::button(2), get_message_time());
1656  return 0;
1657 
1658  case WM_XBUTTONUP:
1659  {
1660  if (_lost_keypresses) {
1661  resend_lost_keypresses();
1662  }
1663  ReleaseCapture();
1664  int whichButton = GET_XBUTTON_WPARAM(wparam);
1665  if (whichButton == XBUTTON1) {
1666  _input->button_up(MouseButton::button(3), get_message_time());
1667  } else if (whichButton == XBUTTON2) {
1668  _input->button_up(MouseButton::button(4), get_message_time());
1669  }
1670  }
1671  return 0;
1672 
1673  case WM_MOUSEWHEEL:
1674  {
1675  int delta = GET_WHEEL_DELTA_WPARAM(wparam);
1676 
1677  POINT point;
1678  GetCursorPos(&point);
1679  ScreenToClient(hwnd, &point);
1680  double time = get_message_time();
1681 
1682  if (delta >= 0) {
1683  while (delta > 0) {
1684  handle_keypress(MouseButton::wheel_up(), point.x, point.y, time);
1685  handle_keyrelease(MouseButton::wheel_up(), time);
1686  delta -= WHEEL_DELTA;
1687  }
1688  } else {
1689  while (delta < 0) {
1690  handle_keypress(MouseButton::wheel_down(), point.x, point.y, time);
1691  handle_keyrelease(MouseButton::wheel_down(), time);
1692  delta += WHEEL_DELTA;
1693  }
1694  }
1695  return 0;
1696  }
1697  break;
1698 
1699 
1700  case WM_IME_SETCONTEXT:
1701  if (!ime_hide)
1702  break;
1703 
1704  windisplay_cat.debug() << "hwnd = " << hwnd << " and GetFocus = " << GetFocus() << endl;
1705  _ime_hWnd = ImmGetDefaultIMEWnd(hwnd);
1706  if (::SendMessage(_ime_hWnd, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0))
1707  // if (::SendMessage(hwnd, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0))
1708  windisplay_cat.debug() << "SendMessage failed for " << _ime_hWnd << endl;
1709  else
1710  windisplay_cat.debug() << "SendMessage Succeeded for " << _ime_hWnd << endl;
1711 
1712  windisplay_cat.debug() << "wparam is " << wparam << ", lparam is " << lparam << endl;
1713  lparam &= ~ISC_SHOWUIALL;
1714  if (ImmIsUIMessage(_ime_hWnd, msg, wparam, lparam))
1715  windisplay_cat.debug() << "wparam is " << wparam << ", lparam is " << lparam << endl;
1716 
1717  break;
1718 
1719 
1720  case WM_IME_NOTIFY:
1721  if (wparam == IMN_SETOPENSTATUS) {
1722  HIMC hIMC = ImmGetContext(hwnd);
1723  nassertr(hIMC != 0, 0);
1724  _ime_open = (ImmGetOpenStatus(hIMC) != 0);
1725  if (!_ime_open) {
1726  _ime_active = false; // Sanity enforcement.
1727  }
1728  if (ime_hide) {
1729  // if (0) {
1730  COMPOSITIONFORM comf;
1731  CANDIDATEFORM canf;
1732  ImmGetCompositionWindow(hIMC, &comf);
1733  ImmGetCandidateWindow(hIMC, 0, &canf);
1734  windisplay_cat.debug() <<
1735  "comf style " << comf.dwStyle <<
1736  " comf point: x" << comf.ptCurrentPos.x << ",y " << comf.ptCurrentPos.y <<
1737  " comf rect: l " << comf.rcArea.left << ",t " << comf.rcArea.top << ",r " <<
1738  comf.rcArea.right << ",b " << comf.rcArea.bottom << endl;
1739  windisplay_cat.debug() <<
1740  "canf style " << canf.dwStyle <<
1741  " canf point: x" << canf.ptCurrentPos.x << ",y " << canf.ptCurrentPos.y <<
1742  " canf rect: l " << canf.rcArea.left << ",t " << canf.rcArea.top << ",r " <<
1743  canf.rcArea.right << ",b " << canf.rcArea.bottom << endl;
1744  comf.dwStyle = CFS_POINT;
1745  comf.ptCurrentPos.x = 2000;
1746  comf.ptCurrentPos.y = 2000;
1747 
1748  canf.dwStyle = CFS_EXCLUDE;
1749  canf.dwIndex = 0;
1750  canf.ptCurrentPos.x = 0;
1751  canf.ptCurrentPos.y = 0;
1752  canf.rcArea.left = 0;
1753  canf.rcArea.top = 0;
1754  canf.rcArea.right = 640;
1755  canf.rcArea.bottom = 480;
1756 
1757 #if 0
1758  comf.rcArea.left = 200;
1759  comf.rcArea.top = 200;
1760  comf.rcArea.right = 0;
1761  comf.rcArea.bottom = 0;
1762 #endif
1763 
1764  if (ImmSetCompositionWindow(hIMC, &comf))
1765  windisplay_cat.debug() << "set composition form: success\n";
1766  for (int i=0; i<3; ++i) {
1767  if (ImmSetCandidateWindow(hIMC, &canf))
1768  windisplay_cat.debug() << "set candidate form: success\n";
1769  canf.dwIndex++;
1770  }
1771  }
1772 
1773  ImmReleaseContext(hwnd, hIMC);
1774  }
1775  break;
1776 
1777  case WM_IME_STARTCOMPOSITION:
1778  support_overlay_window(true);
1779  _ime_active = true;
1780  break;
1781 
1782  case WM_IME_ENDCOMPOSITION:
1783  support_overlay_window(false);
1784  _ime_active = false;
1785 
1786  if (ime_aware) {
1787  wstring ws;
1788  _input->candidate(ws, 0, 0, 0);
1789  }
1790 
1791  break;
1792 
1793  case WM_IME_COMPOSITION:
1794  if (ime_aware) {
1795 
1796  // If the ime window is not marked as active at this point, we must be
1797  // in the process of closing it down (in close_ime), and we don't want
1798  // to send the current composition string in that case. But we do need
1799  // to return 0 to tell windows not to try to send the composition string
1800  // through WM_CHAR messages.
1801  if (!_ime_active) {
1802  return 0;
1803  }
1804 
1805  HIMC hIMC = ImmGetContext(hwnd);
1806  nassertr(hIMC != 0, 0);
1807 
1808  DWORD result_size = 0;
1809  static const int ime_buffer_size = 256;
1810  static const int ime_buffer_size_bytes = ime_buffer_size / sizeof(wchar_t);
1811  wchar_t ime_buffer[ime_buffer_size];
1812  size_t cursor_pos, delta_start;
1813 
1814  if (lparam & GCS_RESULTSTR) {
1815  result_size = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR,
1816  ime_buffer, ime_buffer_size_bytes);
1817  size_t num_chars = result_size / sizeof(wchar_t);
1818  for (size_t i = 0; i < num_chars; ++i) {
1819  _input->keystroke(ime_buffer[i]);
1820  }
1821  }
1822 
1823  if (lparam & GCS_COMPSTR) {
1824  result_size = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, nullptr, 0);
1825  cursor_pos = result_size & 0xffff;
1826 
1827  result_size = ImmGetCompositionStringW(hIMC, GCS_DELTASTART, nullptr, 0);
1828  delta_start = result_size & 0xffff;
1829  result_size = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, ime_buffer, ime_buffer_size);
1830  size_t num_chars = result_size / sizeof(wchar_t);
1831 
1832  _input->candidate(wstring(ime_buffer, num_chars),
1833  std::min(cursor_pos, delta_start),
1834  std::max(cursor_pos, delta_start),
1835  cursor_pos);
1836  }
1837  ImmReleaseContext(hwnd, hIMC);
1838  }
1839  break;
1840 
1841  case WM_CHAR:
1842  // Ignore WM_CHAR messages if we have the IME open, since everything will
1843  // come in through WM_IME_COMPOSITION. (It's supposed to come in through
1844  // WM_CHAR, too, but there seems to be a bug in Win2000 in that it only
1845  // sends question mark characters through here.)
1846 
1847  // Actually, probably that "bug" was due to the fact that we were
1848  // previously using the ANSI versions of RegisterClass etc., in which case
1849  // the actual value passed to WM_CHAR seems to be poorly defined. Now we
1850  // are using RegisterClassW etc., which means WM_CHAR is absolutely
1851  // supposed to be utf-16.
1852  if (!_ime_open) {
1853  _input->keystroke(wparam);
1854  }
1855  break;
1856 
1857  case WM_SYSKEYDOWN:
1858  if (_lost_keypresses) {
1859  resend_lost_keypresses();
1860  }
1861  if (windisplay_cat.is_debug()) {
1862  windisplay_cat.debug()
1863  << "syskeydown: " << wparam << " (" << lookup_key(wparam) << ")\n";
1864  }
1865  {
1866  // Alt and F10 are sent as WM_SYSKEYDOWN instead of WM_KEYDOWN want to
1867  // use defwindproc on Alt syskey so std windows cmd Alt-F4 works, etc
1868  POINT point;
1869  GetCursorPos(&point);
1870  ScreenToClient(hwnd, &point);
1871  handle_keypress(lookup_key(wparam), point.x, point.y,
1872  get_message_time());
1873 
1874  if ((lparam & 0x40000000) == 0) {
1875  handle_raw_keypress(lookup_raw_key(lparam), get_message_time());
1876  }
1877 
1878 /*
1879  * wparam does not contain leftright information for SHIFT, CONTROL, or ALT,
1880  * so we have to track their status and do the right thing. We'll send the
1881  * leftright specific key event along with the general key event. Key
1882  * repeating is not being handled consistently for LALT and RALT, but from
1883  * comments below, it's only being handled the way it is for backspace, so
1884  * we'll leave it as is.
1885  */
1886  if (wparam == VK_MENU) {
1887  if ((GetKeyState(VK_LMENU) & 0x8000) != 0 && ! _lalt_down) {
1888  handle_keypress(KeyboardButton::lalt(), point.x, point.y,
1889  get_message_time());
1890  _lalt_down = true;
1891  }
1892  if ((GetKeyState(VK_RMENU) & 0x8000) != 0 && ! _ralt_down) {
1893  handle_keypress(KeyboardButton::ralt(), point.x, point.y,
1894  get_message_time());
1895  _ralt_down = true;
1896  }
1897  }
1898  if (wparam == VK_F10) {
1899  // bypass default windproc F10 behavior (it activates the main menu,
1900  // but we have none)
1901  return 0;
1902  }
1903  }
1904  break;
1905 
1906  case WM_SYSCOMMAND:
1907  if (wparam == SC_KEYMENU) {
1908  // if Alt is released (alone wo other keys), defwindproc will send this
1909  // command, which will 'activate' the title bar menu (we have none) and
1910  // give focus to it. we don't want this to happen, so kill this msg.
1911 
1912  // Note that the WM_SYSKEYUP message for Alt has already been sent (if
1913  // it is going to be), so ignoring this special message does no harm.
1914  return 0;
1915  }
1916  break;
1917 
1918  case WM_KEYDOWN:
1919  if (_lost_keypresses) {
1920  resend_lost_keypresses();
1921  }
1922  if (windisplay_cat.is_debug()) {
1923  windisplay_cat.debug()
1924  << "keydown: " << wparam << " (" << lookup_key(wparam) << ")\n";
1925  }
1926 
1927  // If this bit is not zero, this is just a keyrepeat echo; we ignore these
1928  // for handle_keypress (we respect keyrepeat only for handle_keystroke).
1929  if ((lparam & 0x40000000) == 0) {
1930  POINT point;
1931  GetCursorPos(&point);
1932  ScreenToClient(hwnd, &point);
1933  handle_keypress(lookup_key(wparam), point.x, point.y,
1934  get_message_time());
1935  handle_raw_keypress(lookup_raw_key(lparam), get_message_time());
1936 
1937  // wparam does not contain leftright information for SHIFT, CONTROL, or
1938  // ALT, so we have to track their status and do the right thing. We'll
1939  // send the leftright specific key event along with the general key
1940  // event.
1941  if (wparam == VK_SHIFT) {
1942  if ((GetKeyState(VK_LSHIFT) & 0x8000) != 0 && ! _lshift_down) {
1943  handle_keypress(KeyboardButton::lshift(), point.x, point.y,
1944  get_message_time());
1945  _lshift_down = true;
1946  }
1947  if ((GetKeyState(VK_RSHIFT) & 0x8000) != 0 && ! _rshift_down) {
1948  handle_keypress(KeyboardButton::rshift(), point.x, point.y,
1949  get_message_time());
1950  _rshift_down = true;
1951  }
1952  } else if(wparam == VK_CONTROL) {
1953  if ((GetKeyState(VK_LCONTROL) & 0x8000) != 0 && ! _lcontrol_down) {
1954  handle_keypress(KeyboardButton::lcontrol(), point.x, point.y,
1955  get_message_time());
1956  _lcontrol_down = true;
1957  }
1958  if ((GetKeyState(VK_RCONTROL) & 0x8000) != 0 && ! _rcontrol_down) {
1959  handle_keypress(KeyboardButton::rcontrol(), point.x, point.y,
1960  get_message_time());
1961  _rcontrol_down = true;
1962  }
1963  }
1964 
1965  // Handle Cntrl-V paste from clipboard. Is there a better way to detect
1966  // this hotkey?
1967  if ((wparam=='V') && (GetKeyState(VK_CONTROL) < 0) &&
1968  !_input_devices.empty() && paste_emit_keystrokes) {
1969  HGLOBAL hglb;
1970  char *lptstr;
1971 
1972  if (IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(nullptr)) {
1973  // Maybe we should support CF_UNICODETEXT if it is available too?
1974  hglb = GetClipboardData(CF_TEXT);
1975  if (hglb!=nullptr) {
1976  lptstr = (char *) GlobalLock(hglb);
1977  if (lptstr != nullptr) {
1978  char *pChar;
1979  for (pChar = lptstr; *pChar; pChar++) {
1980  _input->keystroke((uchar)*pChar);
1981  }
1982  GlobalUnlock(hglb);
1983  }
1984  }
1985  CloseClipboard();
1986  }
1987  }
1988  } else {
1989  // Actually, for now we'll respect the repeat anyway, just so we support
1990  // backspace properly. Rethink later.
1991  POINT point;
1992  GetCursorPos(&point);
1993  ScreenToClient(hwnd, &point);
1994  handle_keypress(lookup_key(wparam), point.x, point.y,
1995  get_message_time());
1996 
1997 /*
1998  * wparam does not contain leftright information for SHIFT, CONTROL, or ALT,
1999  * so we have to track their status and do the right thing. We'll send the
2000  * leftright specific key event along with the general key event. If the user
2001  * presses LSHIFT and then RSHIFT, the RSHIFT event will come in with the
2002  * keyrepeat flag on (i.e. it will end up in this block). The logic below
2003  * should detect this correctly and only send the RSHIFT event. Note that the
2004  * CONTROL event will be sent twice, once for each keypress. Since keyrepeats
2005  * are currently being sent simply as additional keypress events, that should
2006  * be okay for now.
2007  */
2008  if (wparam == VK_SHIFT) {
2009  if (((GetKeyState(VK_LSHIFT) & 0x8000) != 0) && ! _lshift_down ) {
2010  handle_keypress(KeyboardButton::lshift(), point.x, point.y,
2011  get_message_time());
2012  _lshift_down = true;
2013  } else if (((GetKeyState(VK_RSHIFT) & 0x8000) != 0) && ! _rshift_down ) {
2014  handle_keypress(KeyboardButton::rshift(), point.x, point.y,
2015  get_message_time());
2016  _rshift_down = true;
2017  } else {
2018  if ((GetKeyState(VK_LSHIFT) & 0x8000) != 0) {
2019  handle_keypress(KeyboardButton::lshift(), point.x, point.y,
2020  get_message_time());
2021  }
2022  if ((GetKeyState(VK_RSHIFT) & 0x8000) != 0) {
2023  handle_keypress(KeyboardButton::rshift(), point.x, point.y,
2024  get_message_time());
2025  }
2026  }
2027  } else if(wparam == VK_CONTROL) {
2028  if (((GetKeyState(VK_LCONTROL) & 0x8000) != 0) && ! _lcontrol_down ) {
2029  handle_keypress(KeyboardButton::lcontrol(), point.x, point.y,
2030  get_message_time());
2031  _lcontrol_down = true;
2032  } else if (((GetKeyState(VK_RCONTROL) & 0x8000) != 0) && ! _rcontrol_down ) {
2033  handle_keypress(KeyboardButton::rcontrol(), point.x, point.y,
2034  get_message_time());
2035  _rcontrol_down = true;
2036  } else {
2037  if ((GetKeyState(VK_LCONTROL) & 0x8000) != 0) {
2038  handle_keypress(KeyboardButton::lcontrol(), point.x, point.y,
2039  get_message_time());
2040  }
2041  if ((GetKeyState(VK_RCONTROL) & 0x8000) != 0) {
2042  handle_keypress(KeyboardButton::rcontrol(), point.x, point.y,
2043  get_message_time());
2044  }
2045  }
2046  }
2047  }
2048  break;
2049 
2050  case WM_SYSKEYUP:
2051  case WM_KEYUP:
2052  if (_lost_keypresses) {
2053  resend_lost_keypresses();
2054  }
2055  if (windisplay_cat.is_debug()) {
2056  windisplay_cat.debug()
2057  << "keyup: " << wparam << " (" << lookup_key(wparam) << ")\n";
2058  }
2059  handle_keyrelease(lookup_key(wparam), get_message_time());
2060  handle_raw_keyrelease(lookup_raw_key(lparam), get_message_time());
2061 
2062  // wparam does not contain leftright information for SHIFT, CONTROL, or
2063  // ALT, so we have to track their status and do the right thing. We'll
2064  // send the leftright specific key event along with the general key event.
2065  if (wparam == VK_SHIFT) {
2066  if ((GetKeyState(VK_LSHIFT) & 0x8000) == 0 && _lshift_down) {
2067  handle_keyrelease(KeyboardButton::lshift(), get_message_time());
2068  _lshift_down = false;
2069  }
2070  if ((GetKeyState(VK_RSHIFT) & 0x8000) == 0 && _rshift_down) {
2071  handle_keyrelease(KeyboardButton::rshift(), get_message_time());
2072  _rshift_down = false;
2073  }
2074  } else if(wparam == VK_CONTROL) {
2075  if ((GetKeyState(VK_LCONTROL) & 0x8000) == 0 && _lcontrol_down) {
2076  handle_keyrelease(KeyboardButton::lcontrol(), get_message_time());
2077  _lcontrol_down = false;
2078  }
2079  if ((GetKeyState(VK_RCONTROL) & 0x8000) == 0 && _rcontrol_down) {
2080  handle_keyrelease(KeyboardButton::rcontrol(), get_message_time());
2081  _rcontrol_down = false;
2082  }
2083  } else if(wparam == VK_MENU) {
2084  if ((GetKeyState(VK_LMENU) & 0x8000) == 0 && _lalt_down) {
2085  handle_keyrelease(KeyboardButton::lalt(), get_message_time());
2086  _lalt_down = false;
2087  }
2088  if ((GetKeyState(VK_RMENU) & 0x8000) == 0 && _ralt_down) {
2089  handle_keyrelease(KeyboardButton::ralt(), get_message_time());
2090  _ralt_down = false;
2091  }
2092  }
2093  break;
2094 
2095  case WM_KILLFOCUS:
2096  if (windisplay_cat.is_debug()) {
2097  windisplay_cat.debug()
2098  << "killfocus\n";
2099  }
2100 
2101  _input->focus_lost(get_message_time());
2102  properties.set_foreground(false);
2103  system_changed_properties(properties);
2104  break;
2105 
2106  case WM_SETFOCUS:
2107  // You would think that this would be a good time to call
2108  // resend_lost_keypresses(), but it turns out that we get WM_SETFOCUS
2109  // slightly before Windows starts resending key updown events to us.
2110 
2111 /*
2112  * In particular, if the user restored focus using alt-tab, then at this point
2113  * the keyboard state will indicate that both the alt and tab keys are held
2114  * down. However, there is a small window of opportunity for the user to
2115  * release these keys before Windows starts telling us about keyup events.
2116  * Thus, if we record the fact that alt and tab are being held down now, we
2117  * may miss the keyup events for them, and they can get "stuck" down.
2118  */
2119 
2120  // So we have to defer calling resend_lost_keypresses() until we know
2121  // Windows is ready to send us key updown events. I don't know when we
2122  // can guarantee that, except when we actually do start to receive key
2123  // updown events, so that call is made there.
2124 
2125  if (windisplay_cat.is_debug()) {
2126  windisplay_cat.debug()
2127  << "setfocus\n";
2128  }
2129 
2130  if (_lost_keypresses) {
2131  resend_lost_keypresses();
2132  }
2133 
2134  properties.set_foreground(true);
2135  system_changed_properties(properties);
2136  break;
2137 
2138  case PM_ACTIVE:
2139  if (windisplay_cat.is_debug()) {
2140  windisplay_cat.debug()
2141  << "PM_ACTIVE\n";
2142  }
2143  properties.set_foreground(true);
2144  system_changed_properties(properties);
2145  break;
2146 
2147  case PM_INACTIVE:
2148  if (windisplay_cat.is_debug()) {
2149  windisplay_cat.debug()
2150  << "PM_INACTIVE\n";
2151  }
2152  properties.set_foreground(false);
2153  system_changed_properties(properties);
2154  break;
2155 
2156  case WM_DPICHANGED:
2157  // The window moved to a monitor of different DPI, or someone changed the
2158  // DPI setting in the configuration panel.
2159  if (windisplay_cat.is_debug()) {
2160  windisplay_cat.debug() << "DPI changed to " << LOWORD(wparam);
2161 
2162  if (LOWORD(wparam) != HIWORD(wparam)) {
2163  windisplay_cat.debug(false) << "x" << HIWORD(wparam) << "\n";
2164  } else {
2165  windisplay_cat.debug(false) << "\n";
2166  }
2167  }
2168  // Resize the window if requested to match the new DPI. Obviously, don't
2169  // do this if a fixed size was requested.
2170  if (!_properties.get_fixed_size() && dpi_window_resize) {
2171  RECT &rect = *(LPRECT)lparam;
2172  SetWindowPos(_hWnd, HWND_TOP, rect.left, rect.top,
2173  rect.right - rect.left, rect.bottom - rect.top,
2174  SWP_NOZORDER | SWP_NOACTIVATE);
2175  }
2176  break;
2177 
2178  case WM_TOUCH:
2179  _num_touches = LOWORD(wparam);
2180  if (_num_touches > MAX_TOUCHES) {
2181  _num_touches = MAX_TOUCHES;
2182  }
2183  if (pGetTouchInputInfo != 0) {
2184  pGetTouchInputInfo((HTOUCHINPUT)lparam, _num_touches, _touches, sizeof(TOUCHINPUT));
2185  pCloseTouchInputHandle((HTOUCHINPUT)lparam);
2186  }
2187  break;
2188  }
2189 
2190  // do custom messages processing if any has been set
2191  for ( WinProcClasses::iterator it=_window_proc_classes.begin() ; it != _window_proc_classes.end(); it++ ){
2192  (*it)->wnd_proc(this, hwnd, msg, wparam, lparam);
2193  }
2194 
2195  return DefWindowProcW(hwnd, msg, wparam, lparam);
2196 }
2197 
2198 
2199 /**
2200  * This is attached to the window class for all WinGraphicsWindow windows; it
2201  * is called to handle window events.
2202  */
2203 LONG WINAPI WinGraphicsWindow::
2204 static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
2205  // Look up the window in our global map.
2206  WindowHandles::const_iterator wi;
2207  wi = _window_handles.find(hwnd);
2208  if (wi != _window_handles.end()) {
2209  // We found the window.
2210  return (*wi).second->window_proc(hwnd, msg, wparam, lparam);
2211  }
2212 
2213  // The window wasn't in the map; we must be creating it right now.
2214  if (_creating_window != nullptr) {
2215  return _creating_window->window_proc(hwnd, msg, wparam, lparam);
2216  }
2217 
2218  // Oops, we weren't creating a window! Don't know how to handle the
2219  // message, so just pass it on to Windows to deal with it.
2220  return DefWindowProcW(hwnd, msg, wparam, lparam);
2221 }
2222 
2223 /**
2224  * Handles one event from the message queue.
2225  */
2226 void WinGraphicsWindow::
2227 process_1_event() {
2228  MSG msg;
2229 
2230  if (!GetMessage(&msg, nullptr, 0, 0)) {
2231  // WM_QUIT received. We need a cleaner way to deal with this.
2232  // DestroyAllWindows(false);
2233  exit(msg.wParam); // this will invoke AtExitFn
2234  }
2235 
2236  // Translate virtual key messages
2237  TranslateMessage(&msg);
2238  // Call window_proc
2239  DispatchMessage(&msg);
2240 }
2241 
2242 /**
2243  * Called when the keyboard focus has been restored to the window after it has
2244  * been lost for a time, this rechecks the keyboard state and generates key
2245  * up/down messages for keys that have changed state in the meantime.
2246  */
2247 void WinGraphicsWindow::
2248 resend_lost_keypresses() {
2249  nassertv(_lost_keypresses);
2250  // This is now a no-op. Not sure we really want to generate new "down" or
2251  // "resume" events for keys that were held while the window focus is
2252  // restored.
2253 
2254  _lost_keypresses = false;
2255 }
2256 
2257 /**
2258  * Changes _cursor_window from its current value to the indicated value. This
2259  * also changes the cursor properties appropriately.
2260  */
2261 void WinGraphicsWindow::
2262 update_cursor_window(WinGraphicsWindow *to_window) {
2263  bool hide_cursor = false;
2264  if (to_window == nullptr) {
2265  // We are leaving a graphics window; we should restore the Win2000
2266  // effects.
2267  if (_got_saved_params) {
2268  SystemParametersInfo(SPI_SETMOUSETRAILS, _saved_mouse_trails,
2269  0, 0);
2270  SystemParametersInfo(SPI_SETCURSORSHADOW, 0,
2271  _saved_cursor_shadow ? (PVOID)1 : nullptr, 0);
2272  SystemParametersInfo(SPI_SETMOUSEVANISH, 0,
2273  _saved_mouse_vanish ? (PVOID)1 : nullptr, 0);
2274  _got_saved_params = false;
2275  }
2276 
2277  } else {
2278  const WindowProperties &to_props = to_window->get_properties();
2279  hide_cursor = to_props.get_cursor_hidden();
2280 
2281  // We are entering a graphics window; we should save and disable the
2282  // Win2000 effects. These don't work at all well over a 3-D window.
2283 
2284  // These parameters are only defined for Win2000XP, but they should just
2285  // cause a silent error on earlier OS's, which is OK.
2286  if (!_got_saved_params) {
2287  SystemParametersInfo(SPI_GETMOUSETRAILS, 0,
2288  &_saved_mouse_trails, 0);
2289  SystemParametersInfo(SPI_GETCURSORSHADOW, 0,
2290  &_saved_cursor_shadow, 0);
2291  SystemParametersInfo(SPI_GETMOUSEVANISH, 0,
2292  &_saved_mouse_vanish, 0);
2293  _got_saved_params = true;
2294 
2295  SystemParametersInfo(SPI_SETMOUSETRAILS, 0, (PVOID)0, 0);
2296  SystemParametersInfo(SPI_SETCURSORSHADOW, 0, (PVOID)false, 0);
2297  SystemParametersInfo(SPI_SETMOUSEVANISH, 0, (PVOID)false, 0);
2298  }
2299 
2300  SetCursor(to_window->_cursor);
2301  }
2302 
2303  hide_or_show_cursor(hide_cursor);
2304 
2305  _cursor_window = to_window;
2306 }
2307 
2308 /**
2309  * Hides or shows the mouse cursor according to the indicated parameter. This
2310  * is normally called when the mouse wanders into or out of a window with the
2311  * cursor_hidden properties.
2312  */
2313 void WinGraphicsWindow::
2314 hide_or_show_cursor(bool hide_cursor) {
2315  if (hide_cursor) {
2316  if (!_cursor_hidden) {
2317  ShowCursor(false);
2318  _cursor_hidden = true;
2319  }
2320  } else {
2321  if (_cursor_hidden) {
2322  ShowCursor(true);
2323  _cursor_hidden = false;
2324  }
2325  }
2326 }
2327 
2328 // don't pick any video modes < MIN_REFRESH_RATE Hz
2329 #define MIN_REFRESH_RATE 60
2330 // EnumDisplaySettings may indicate 0 or 1 for refresh rate, which means use
2331 // driver default rate (assume its >min_refresh_rate)
2332 #define ACCEPTABLE_REFRESH_RATE(RATE) ((RATE >= MIN_REFRESH_RATE) || (RATE==0) || (RATE==1))
2333 
2334 /**
2335  * Looks for a fullscreen mode that meets the specified size and bitdepth
2336  * requirements. Returns true if a suitable mode is found, false otherwise.
2337  */
2338 bool WinGraphicsWindow::
2339 find_acceptable_display_mode(DWORD dwWidth, DWORD dwHeight, DWORD bpp,
2340  DEVMODE &dm) {
2341 
2342  // Get the current mode. We'll try to match the refresh rate.
2343  DEVMODE cur_dm;
2344  ZeroMemory(&cur_dm, sizeof(cur_dm));
2345  cur_dm.dmSize = sizeof(cur_dm);
2346  EnumDisplaySettings(nullptr, ENUM_CURRENT_SETTINGS, &cur_dm);
2347 
2348  int modenum = 0;
2349  int saved_modenum = -1;
2350 
2351  while (1) {
2352  ZeroMemory(&dm, sizeof(dm));
2353  dm.dmSize = sizeof(dm);
2354 
2355  if (!EnumDisplaySettings(nullptr, modenum, &dm)) {
2356  break;
2357  }
2358 
2359  if ((dm.dmPelsWidth == dwWidth) && (dm.dmPelsHeight == dwHeight) &&
2360  (dm.dmBitsPerPel == bpp)) {
2361  // If this also matches in refresh rate, we're done here. Otherwise,
2362  // save this as a second choice for later.
2363  if (dm.dmDisplayFrequency == cur_dm.dmDisplayFrequency) {
2364  return true;
2365  } else if (saved_modenum == -1) {
2366  saved_modenum = modenum;
2367  }
2368  }
2369  modenum++;
2370  }
2371 
2372  // Failed to find an exact match, but we do have a match that didn't match
2373  // the refresh rate.
2374  if (saved_modenum != -1) {
2375  ZeroMemory(&dm, sizeof(dm));
2376  dm.dmSize = sizeof(dm);
2377 
2378  if (EnumDisplaySettings(nullptr, saved_modenum, &dm)) {
2379  return true;
2380  }
2381  }
2382 
2383  return false;
2384 }
2385 
2386 /**
2387  * Pops up a dialog box with the indicated Windows error message ID (or the
2388  * last error message generated) for meaningful display to the user.
2389  */
2390 void WinGraphicsWindow::
2391 show_error_message(DWORD message_id) {
2392  LPTSTR message_buffer;
2393 
2394  if (message_id == 0) {
2395  message_id = GetLastError();
2396  }
2397 
2398  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
2399  nullptr, message_id,
2400  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //The user default language
2401  (LPTSTR)&message_buffer, // the weird ptrptr->ptr cast is intentional, see FORMAT_MESSAGE_ALLOCATE_BUFFER
2402  1024, nullptr);
2403  MessageBox(GetDesktopWindow(), message_buffer, _T(errorbox_title), MB_OK);
2404  windisplay_cat.fatal() << "System error msg: " << message_buffer << endl;
2405  LocalFree(message_buffer);
2406 }
2407 
2408 /**
2409  *
2410  */
2411 void WinGraphicsWindow::
2412 handle_keypress(ButtonHandle key, int x, int y, double time) {
2413  _input->set_pointer_in_window(x, y);
2414  if (key != ButtonHandle::none()) {
2415  _input->button_down(key, time);
2416  }
2417 }
2418 
2419 /**
2420  * Indicates we detected a key was already down when the focus is restored to
2421  * the window. Mainly useful for tracking the state of modifier keys.
2422  */
2423 void WinGraphicsWindow::
2424 handle_keyresume(ButtonHandle key, double time) {
2425  if (key != ButtonHandle::none()) {
2426  _input->button_resume_down(key, time);
2427  }
2428 }
2429 
2430 /**
2431  *
2432  */
2433 void WinGraphicsWindow::
2434 handle_keyrelease(ButtonHandle key, double time) {
2435  if (key != ButtonHandle::none()) {
2436  _input->button_up(key, time);
2437  }
2438 }
2439 
2440 /**
2441  *
2442  */
2443 void WinGraphicsWindow::
2444 handle_raw_keypress(ButtonHandle key, double time) {
2445  if (key != ButtonHandle::none()) {
2446  _input->raw_button_down(key, time);
2447  }
2448 }
2449 
2450 /**
2451  *
2452  */
2453 void WinGraphicsWindow::
2454 handle_raw_keyrelease(ButtonHandle key, double time) {
2455  if (key != ButtonHandle::none()) {
2456  _input->raw_button_up(key, time);
2457  }
2458 }
2459 
2460 /**
2461  * Translates the keycode reported by Windows to an appropriate Panda
2462  * ButtonHandle.
2463  */
2464 ButtonHandle WinGraphicsWindow::
2465 lookup_key(WPARAM wparam) const {
2466  // First, check for a few buttons that we filter out when the IME window is
2467  // open.
2468  if (!_ime_active) {
2469  switch(wparam) {
2470  case VK_BACK: return KeyboardButton::backspace();
2471  case VK_DELETE: return KeyboardButton::del();
2472  case VK_ESCAPE: return KeyboardButton::escape();
2473  case VK_SPACE: return KeyboardButton::space();
2474  case VK_UP: return KeyboardButton::up();
2475  case VK_DOWN: return KeyboardButton::down();
2476  case VK_LEFT: return KeyboardButton::left();
2477  case VK_RIGHT: return KeyboardButton::right();
2478  }
2479  }
2480 
2481  // Now check for the rest of the buttons, including the ones that we allow
2482  // through even when the IME window is open.
2483  switch(wparam) {
2484  case VK_TAB: return KeyboardButton::tab();
2485  case VK_PRIOR: return KeyboardButton::page_up();
2486  case VK_NEXT: return KeyboardButton::page_down();
2487  case VK_HOME: return KeyboardButton::home();
2488  case VK_END: return KeyboardButton::end();
2489  case VK_F1: return KeyboardButton::f1();
2490  case VK_F2: return KeyboardButton::f2();
2491  case VK_F3: return KeyboardButton::f3();
2492  case VK_F4: return KeyboardButton::f4();
2493  case VK_F5: return KeyboardButton::f5();
2494  case VK_F6: return KeyboardButton::f6();
2495  case VK_F7: return KeyboardButton::f7();
2496  case VK_F8: return KeyboardButton::f8();
2497  case VK_F9: return KeyboardButton::f9();
2498  case VK_F10: return KeyboardButton::f10();
2499  case VK_F11: return KeyboardButton::f11();
2500  case VK_F12: return KeyboardButton::f12();
2501  case VK_INSERT: return KeyboardButton::insert();
2502  case VK_CAPITAL: return KeyboardButton::caps_lock();
2503  case VK_NUMLOCK: return KeyboardButton::num_lock();
2504  case VK_SCROLL: return KeyboardButton::scroll_lock();
2505  case VK_PAUSE: return KeyboardButton::pause();
2506  case VK_SNAPSHOT: return KeyboardButton::print_screen();
2507 
2508  case VK_SHIFT: return KeyboardButton::shift();
2509  case VK_LSHIFT: return KeyboardButton::lshift();
2510  case VK_RSHIFT: return KeyboardButton::rshift();
2511 
2512  case VK_CONTROL: return KeyboardButton::control();
2513  case VK_LCONTROL: return KeyboardButton::lcontrol();
2514  case VK_RCONTROL: return KeyboardButton::rcontrol();
2515 
2516  case VK_MENU: return KeyboardButton::alt();
2517  case VK_LMENU: return KeyboardButton::lalt();
2518  case VK_RMENU: return KeyboardButton::ralt();
2519 
2520  default:
2521  int key = MapVirtualKey(wparam, 2);
2522  if (isascii(key) && key != 0) {
2523  // We used to try to remap lowercase to uppercase keys here based on the
2524  // state of the shift andor caps lock keys. But that's a mistake, and
2525  // doesn't allow for international or user-defined keyboards; let
2526  // Windows do that mapping.
2527 
2528  // Nowadays, we make a distinction between a "button" and a "keystroke".
2529  // A button corresponds to a physical button on the keyboard and has a
2530  // down and up event associated. A keystroke may or may not correspond
2531  // to a physical button, but will be some Unicode character and will not
2532  // have a corresponding up event.
2533  return KeyboardButton::ascii_key(tolower(key));
2534  }
2535  break;
2536  }
2537  return ButtonHandle::none();
2538 }
2539 
2540 /**
2541  * Translates the scancode reported by Windows to an appropriate Panda
2542  * ButtonHandle.
2543  */
2544 ButtonHandle WinGraphicsWindow::
2545 lookup_raw_key(LPARAM lparam) const {
2546  unsigned char vsc = (lparam & 0xff0000) >> 16;
2547 
2548  if (lparam & 0x1000000) {
2549  // Extended keys
2550  switch (vsc) {
2551  case 28: return KeyboardButton::enter();
2552  case 29: return KeyboardButton::rcontrol();
2553  case 53: return KeyboardButton::ascii_key('/');
2554  case 55: return KeyboardButton::print_screen();
2555  case 56: return KeyboardButton::ralt();
2556  case 69: return KeyboardButton::num_lock();
2557  case 71: return KeyboardButton::home();
2558  case 72: return KeyboardButton::up();
2559  case 73: return KeyboardButton::page_up();
2560  case 75: return KeyboardButton::left();
2561  case 77: return KeyboardButton::right();
2562  case 79: return KeyboardButton::end();
2563  case 80: return KeyboardButton::down();
2564  case 81: return KeyboardButton::page_down();
2565  case 82: return KeyboardButton::insert();
2566  case 83: return KeyboardButton::del();
2567  case 91: return KeyboardButton::lmeta();
2568  case 92: return KeyboardButton::rmeta();
2569  case 93: return KeyboardButton::menu();
2570  }
2571  }
2572 
2573  if (vsc <= 83) {
2574  static ButtonHandle raw_map[] = {
2575  ButtonHandle::none(),
2576  KeyboardButton::escape(),
2589  KeyboardButton::backspace(),
2590  KeyboardButton::tab(),
2603  KeyboardButton::enter(),
2604  KeyboardButton::lcontrol(),
2617  KeyboardButton::lshift(),
2629  KeyboardButton::rshift(),
2631  KeyboardButton::lalt(),
2632  KeyboardButton::space(),
2633  KeyboardButton::caps_lock(),
2634  KeyboardButton::f1(),
2635  KeyboardButton::f2(),
2636  KeyboardButton::f3(),
2637  KeyboardButton::f4(),
2638  KeyboardButton::f5(),
2639  KeyboardButton::f6(),
2640  KeyboardButton::f7(),
2641  KeyboardButton::f8(),
2642  KeyboardButton::f9(),
2643  KeyboardButton::f10(),
2644  KeyboardButton::pause(),
2645  KeyboardButton::scroll_lock(),
2659  };
2660  return raw_map[vsc];
2661  }
2662 
2663  // A few additional keys don't fit well in the above table.
2664  switch (vsc) {
2665  case 87: return KeyboardButton::f11();
2666  case 88: return KeyboardButton::f12();
2667  default: return ButtonHandle::none();
2668  }
2669 }
2670 
2671 /**
2672  * Returns a ButtonMap containing the association between raw buttons and
2673  * virtual buttons.
2674  *
2675  * Note that on Windows, the pause button and numpad keys are not mapped
2676  * reliably.
2677  */
2678 ButtonMap *WinGraphicsWindow::
2679 get_keyboard_map() const {
2680  ButtonMap *map = new ButtonMap;
2681 
2682  wchar_t text[256];
2683  UINT vsc = 0;
2684  unsigned short ex_vsc[] = {0x57, 0x58,
2685  0x011c, 0x011d, 0x0135, 0x0137, 0x0138, 0x0145, 0x0147, 0x0148, 0x0149, 0x014b, 0x014d, 0x014f, 0x0150, 0x0151, 0x0152, 0x0153, 0x015b, 0x015c, 0x015d};
2686 
2687  for (int k = 1; k < 84 + 17; ++k) {
2688  if (k >= 84) {
2689  vsc = ex_vsc[k - 84];
2690  } else {
2691  vsc = k;
2692  }
2693 
2694  UINT lparam = vsc << 16;
2695  ButtonHandle raw_button = lookup_raw_key(lparam);
2696  if (raw_button == ButtonHandle::none()) {
2697  continue;
2698  }
2699 
2700  ButtonHandle button;
2701  if (vsc == 0x45) {
2702  button = KeyboardButton::pause();
2703 
2704  } else if (vsc >= 0x47 && vsc <= 0x53) {
2705  // The numpad keys are not mapped correctly, see KB72583
2706  button = raw_button;
2707 
2708  } else {
2709  if (vsc == 0x145) {
2710  // Don't ask why - I have no idea.
2711  vsc = 0x45;
2712  }
2713  if (vsc & 0x0100) {
2714  // MapVirtualKey recognizes extended codes differently.
2715  vsc ^= 0xe100;
2716  }
2717 
2718  UINT vk = MapVirtualKeyA(vsc, MAPVK_VSC_TO_VK_EX);
2719  button = lookup_key(vk);
2720  //if (button == ButtonHandle::none()) {
2721  // continue;
2722  //}
2723  }
2724 
2725  int len = GetKeyNameTextW(lparam, text, 256);
2726  TextEncoder enc;
2727  enc.set_wtext(wstring(text, len));
2728  map->map_button(raw_button, button, enc.get_text());
2729  }
2730 
2731  return map;
2732 }
2733 
2734 /**
2735  *
2736  */
2737 void WinGraphicsWindow::
2738 handle_raw_input(HRAWINPUT hraw) {
2739  LPBYTE lpb;
2740  UINT dwSize;
2741 
2742  if (hraw == 0) {
2743  return;
2744  }
2745  if (GetRawInputData(hraw, RID_INPUT, nullptr, &dwSize, sizeof(RAWINPUTHEADER)) == -1) {
2746  return;
2747  }
2748 
2749  lpb = (LPBYTE)alloca(sizeof(LPBYTE) * dwSize);
2750  if (lpb == nullptr) {
2751  return;
2752  }
2753 
2754  if (GetRawInputData(hraw, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) {
2755  return;
2756  }
2757 
2758  RAWINPUT *raw = (RAWINPUT *)lpb;
2759  if (raw->header.hDevice == 0) {
2760  return;
2761  }
2762 
2763  for (size_t i = 1; i < _input_devices.size(); ++i) {
2764  if (_input_device_handle[i] == raw->header.hDevice) {
2765  PT(GraphicsWindowInputDevice) input =
2766  DCAST(GraphicsWindowInputDevice, _input_devices[i]);
2767 
2768  int adjx = raw->data.mouse.lLastX;
2769  int adjy = raw->data.mouse.lLastY;
2770 
2771  if (raw->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) {
2772  input->set_pointer_in_window(adjx, adjy);
2773  } else {
2774  input->pointer_moved(adjx, adjy);
2775  }
2776 
2777  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) {
2778  input->button_down(MouseButton::button(0), get_message_time());
2779  }
2780  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP) {
2781  input->button_up(MouseButton::button(0), get_message_time());
2782  }
2783  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) {
2784  input->button_down(MouseButton::button(2), get_message_time());
2785  }
2786  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP) {
2787  input->button_up(MouseButton::button(2), get_message_time());
2788  }
2789  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) {
2790  input->button_down(MouseButton::button(1), get_message_time());
2791  }
2792  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP) {
2793  input->button_up(MouseButton::button(1), get_message_time());
2794  }
2795  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) {
2796  input->button_down(MouseButton::button(3), get_message_time());
2797  }
2798  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP) {
2799  input->button_up(MouseButton::button(3), get_message_time());
2800  }
2801  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) {
2802  input->button_down(MouseButton::button(4), get_message_time());
2803  }
2804  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP) {
2805  input->button_up(MouseButton::button(4), get_message_time());
2806  }
2807  }
2808  }
2809 }
2810 
2811 /**
2812  *
2813  */
2814 bool WinGraphicsWindow::
2815 handle_mouse_motion(int x, int y) {
2816  _input->set_pointer_in_window(x, y);
2817  return false;
2818 }
2819 
2820 /**
2821  *
2822  */
2823 void WinGraphicsWindow::
2824 handle_mouse_exit() {
2825  // note: 'mouse_motion' is considered the 'entry' event
2826  _input->set_pointer_out_of_window();
2827 }
2828 
2829 /**
2830  * Loads and returns an HICON corresponding to the indicated filename. If the
2831  * file cannot be loaded, returns 0.
2832  */
2833 HICON WinGraphicsWindow::
2834 get_icon(const Filename &filename) {
2835  // First, look for the unresolved filename in our index.
2836  IconFilenames::iterator fi = _icon_filenames.find(filename);
2837  if (fi != _icon_filenames.end()) {
2838  return (HICON)((*fi).second);
2839  }
2840 
2841  // If it wasn't found, resolve the filename and search for that.
2842 
2843  // Since we have to use a Windows call to load the image from a filename, we
2844  // can't load a virtual file and we can't use the virtual file system.
2845  Filename resolved = filename;
2846  if (!resolved.resolve_filename(get_model_path())) {
2847  // The filename doesn't exist along the search path.
2848  if (resolved.is_fully_qualified() && resolved.exists()) {
2849  // But it does exist locally, so accept it.
2850 
2851  } else {
2852  windisplay_cat.warning()
2853  << "Could not find icon filename " << filename << "\n";
2854  return 0;
2855  }
2856  }
2857  fi = _icon_filenames.find(resolved);
2858  if (fi != _icon_filenames.end()) {
2859  _icon_filenames[filename] = (*fi).second;
2860  return (HICON)((*fi).second);
2861  }
2862 
2863  Filename os = resolved.to_os_specific();
2864 
2865  HANDLE h = LoadImage(nullptr, os.c_str(),
2866  IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
2867  if (h == 0) {
2868  windisplay_cat.warning()
2869  << "windows icon filename '" << os << "' could not be loaded!!\n";
2870  }
2871 
2872  _icon_filenames[filename] = h;
2873  _icon_filenames[resolved] = h;
2874  return (HICON)h;
2875 }
2876 
2877 /**
2878  * Loads and returns an HCURSOR corresponding to the indicated filename. If
2879  * the file cannot be loaded, returns 0.
2880  */
2881 HCURSOR WinGraphicsWindow::
2882 get_cursor(const Filename &filename) {
2883  // The empty filename means to disable a custom cursor.
2884  if (filename.empty()) {
2885  return 0;
2886  }
2887 
2888  // First, look for the unresolved filename in our index.
2889  IconFilenames::iterator fi = _cursor_filenames.find(filename);
2890  if (fi != _cursor_filenames.end()) {
2891  return (HCURSOR)((*fi).second);
2892  }
2893 
2894  // If it wasn't found, resolve the filename and search for that.
2895 
2896  // Since we have to use a Windows call to load the image from a filename, we
2897  // can't load a virtual file and we can't use the virtual file system.
2898  Filename resolved = filename;
2899  if (!resolved.resolve_filename(get_model_path())) {
2900  // The filename doesn't exist.
2901  windisplay_cat.warning()
2902  << "Could not find cursor filename " << filename << "\n";
2903  return 0;
2904  }
2905  fi = _cursor_filenames.find(resolved);
2906  if (fi != _cursor_filenames.end()) {
2907  _cursor_filenames[filename] = (*fi).second;
2908  return (HCURSOR)((*fi).second);
2909  }
2910 
2911  Filename os = resolved.to_os_specific();
2912 
2913  HANDLE h = LoadImage(nullptr, os.c_str(),
2914  IMAGE_CURSOR, 0, 0, LR_LOADFROMFILE);
2915  if (h == 0) {
2916  windisplay_cat.warning()
2917  << "windows cursor filename '" << os << "' could not be loaded!!\n";
2918  show_error_message();
2919  }
2920 
2921  _cursor_filenames[filename] = h;
2922  _cursor_filenames[resolved] = h;
2923  return (HCURSOR)h;
2924 }
2925 
2926 static HCURSOR get_cursor(const Filename &filename);
2927 
2928 /**
2929  * Registers a Window class appropriate for the indicated properties. This
2930  * class may be shared by multiple windows.
2931  */
2932 const WinGraphicsWindow::WindowClass &WinGraphicsWindow::
2933 register_window_class(const WindowProperties &props) {
2934  WindowClass wcreg(props);
2935  std::wostringstream wclass_name;
2936  wclass_name << L"WinGraphicsWindow" << _window_class_index;
2937  wcreg._name = wclass_name.str();
2938 
2939  std::pair<WindowClasses::iterator, bool> found = _window_classes.insert(wcreg);
2940  const WindowClass &wclass = (*found.first);
2941 
2942  if (!found.second) {
2943  // We have already created a window class.
2944  return wclass;
2945  }
2946 
2947  // We have not yet created this window class.
2948  _window_class_index++;
2949 
2950  WNDCLASSW wc;
2951 
2952  HINSTANCE instance = GetModuleHandle(nullptr);
2953 
2954  // Clear before filling in window structure!
2955  ZeroMemory(&wc, sizeof(wc));
2956  wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
2957  wc.lpfnWndProc = (WNDPROC)static_window_proc;
2958  wc.hInstance = instance;
2959 
2960  wc.hIcon = wclass._icon;
2961 
2962  wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
2963  wc.lpszMenuName = nullptr;
2964  wc.lpszClassName = wclass._name.c_str();
2965 
2966  if (!RegisterClassW(&wc)) {
2967  windisplay_cat.error()
2968  << "could not register window class " << wclass._name << "!" << endl;
2969  return wclass;
2970  }
2971 
2972  return wclass;
2973 }
2974 
2975 /**
2976  *
2977  */
2978 WinGraphicsWindow::WinWindowHandle::
2979 WinWindowHandle(WinGraphicsWindow *window, const WindowHandle &copy) :
2980  WindowHandle(copy),
2981  _window(window)
2982 {
2983 }
2984 
2985 /**
2986  * Should be called by the WinGraphicsWindow's destructor, so that we don't
2987  * end up with a floating pointer should this object persist beyond the
2988  * lifespan of its window.
2989  */
2990 void WinGraphicsWindow::WinWindowHandle::
2991 clear_window() {
2992  _window = nullptr;
2993 }
2994 
2995 /**
2996  * Called on a child handle to deliver a keyboard button event generated in
2997  * the parent window.
2998  */
2999 void WinGraphicsWindow::WinWindowHandle::
3000 receive_windows_message(unsigned int msg, int wparam, int lparam) {
3001  if (_window != nullptr) {
3002  _window->receive_windows_message(msg, wparam, lparam);
3003  }
3004 }
3005 
3006 
3007 // pops up MsgBox wsystem error msg
3008 void PrintErrorMessage(DWORD msgID) {
3009  LPTSTR pMessageBuffer;
3010 
3011  if (msgID==PRINT_LAST_ERROR)
3012  msgID=GetLastError();
3013 
3014  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
3015  nullptr,msgID,
3016  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //The user default language
3017  (LPTSTR) &pMessageBuffer, // the weird ptrptr->ptr cast is intentional, see FORMAT_MESSAGE_ALLOCATE_BUFFER
3018  1024, nullptr);
3019  MessageBox(GetDesktopWindow(),pMessageBuffer,_T(errorbox_title),MB_OK);
3020  windisplay_cat.fatal() << "System error msg: " << pMessageBuffer << endl;
3021  LocalFree( pMessageBuffer );
3022 }
3023 
3024 void
3025 ClearToBlack(HWND hWnd, const WindowProperties &props) {
3026  if (!props.has_origin()) {
3027  if (windisplay_cat.is_debug()) {
3028  windisplay_cat.debug()
3029  << "Skipping ClearToBlack, no origin specified yet.\n";
3030  }
3031  return;
3032  }
3033 
3034  if (windisplay_cat.is_debug()) {
3035  windisplay_cat.debug()
3036  << "ClearToBlack(" << hWnd << ", " << props << ")\n";
3037  }
3038  // clear to black
3039  HDC hDC=GetDC(hWnd); // GetDC is not particularly fast. if this needs to be super-quick, we should cache GetDC's hDC
3040  RECT clrRect = {
3041  props.get_x_origin(), props.get_y_origin(),
3042  props.get_x_origin() + props.get_x_size(),
3043  props.get_y_origin() + props.get_y_size()
3044  };
3045  FillRect(hDC,&clrRect,(HBRUSH)GetStockObject(BLACK_BRUSH));
3046  ReleaseDC(hWnd,hDC);
3047  GdiFlush();
3048 }
3049 
3050 /**
3051  * Fills view_rect with the coordinates of the client area of the indicated
3052  * window, converted to screen coordinates.
3053  */
3054 void get_client_rect_screen(HWND hwnd, RECT *view_rect) {
3055  GetClientRect(hwnd, view_rect);
3056 
3057  POINT ul, lr;
3058  ul.x = view_rect->left;
3059  ul.y = view_rect->top;
3060  lr.x = view_rect->right;
3061  lr.y = view_rect->bottom;
3062 
3063  ClientToScreen(hwnd, &ul);
3064  ClientToScreen(hwnd, &lr);
3065 
3066  view_rect->left = ul.x;
3067  view_rect->top = ul.y;
3068  view_rect->right = lr.x;
3069  view_rect->bottom = lr.y;
3070 }
3071 
3072 /**
3073  * Adds the specified Windows proc event handler to be called whenever a
3074  * Windows event occurs.
3075  *
3076  */
3078  nassertv(wnd_proc != nullptr);
3079  _window_proc_classes.insert( (GraphicsWindowProc*)wnd_proc );
3080 }
3081 
3082 /**
3083  * Removes the specified Windows proc event handler.
3084  *
3085  */
3087  nassertv(wnd_proc != nullptr);
3088  _window_proc_classes.erase( (GraphicsWindowProc*)wnd_proc );
3089 }
3090 
3091 /**
3092  * Removes all Windows proc event handlers.
3093  *
3094  */
3096  _window_proc_classes.clear();
3097 }
3098 
3099 /**
3100  * Returns whether this window supports adding of windows proc handlers.
3101  *
3102  */
3104  return true;
3105 }
3106 
3107 /**
3108  * Returns whether the specified event msg is a touch message.
3109  *
3110  */
3113  return callbackData->get_msg() == WM_TOUCH;
3114 }
3115 
3116 /**
3117  * Returns the current number of touches on this window.
3118  *
3119  */
3122  return _num_touches;
3123 }
3124 
3125 /**
3126  * Returns the TouchInfo object describing the specified touch.
3127  *
3128  */
3130 get_touch_info(int index) {
3131  nassertr(index >= 0 && index < MAX_TOUCHES, TouchInfo());
3132 
3133  TOUCHINPUT ti = _touches[index];
3134  POINT point;
3135  point.x = TOUCH_COORD_TO_PIXEL(ti.x);
3136  point.y = TOUCH_COORD_TO_PIXEL(ti.y);
3137  ScreenToClient(_hWnd, &point);
3138 
3139  TouchInfo ret = TouchInfo();
3140  ret.set_x(point.x);
3141  ret.set_y(point.y);
3142  ret.set_id(ti.dwID);
3143  ret.set_flags(ti.dwFlags);
3144  return ret;
3145 }
LightMutexHolder
Similar to MutexHolder, but for a light mutex.
Definition: lightMutexHolder.h:25
WinGraphicsWindow::begin_flip
virtual void begin_flip()
This function will be called within the draw thread after end_frame() has been called on all windows,...
Definition: winGraphicsWindow.cxx:225
ButtonMap::map_button
void map_button(ButtonHandle raw_button, ButtonHandle button, const std::string &label="")
Registers a new button mapping.
Definition: buttonMap.cxx:23
GraphicsWindow::get_properties
get_properties
Returns the current properties of the window.
Definition: graphicsWindow.h:63
FrameBufferProperties
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
Definition: frameBufferProperties.h:26
GraphicsWindow::set_properties_now
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
Definition: graphicsWindow.cxx:495
config_putil.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
throw_event.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
NativeWindowHandle::IntHandle
Definition: nativeWindowHandle.h:55
winGraphicsWindow.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsWindowInputDevice::button_resume_down
void button_resume_down(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button was depressed earlier, and we only just detected the event after th...
Definition: graphicsWindowInputDevice.cxx:87
winGraphicsPipe.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TextEncoder::set_wtext
void set_wtext(const std::wstring &wtext)
Changes the text that is stored in the encoder.
Definition: textEncoder.I:443
WindowProperties::get_undecorated
get_undecorated
Returns true if the window has no border.
Definition: windowProperties.h:105
GraphicsWindowInputDevice
This is a virtual input device that represents the keyboard and mouse pair that is associated with a ...
Definition: graphicsWindowInputDevice.h:28
WindowProperties::clear_undecorated
clear_undecorated
Removes the undecorated specification from the properties.
Definition: windowProperties.h:105
WindowProperties::has_origin
has_origin
Returns true if the window origin has been specified, false otherwise.
Definition: windowProperties.h:76
WindowProperties::clear_fullscreen
clear_fullscreen
Removes the fullscreen specification from the properties.
Definition: windowProperties.h:119
Filename::to_os_specific
std::string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
Definition: filename.cxx:1123
WindowProperties::has_title
has_title
Returns true if the window title has been specified, false otherwise.
Definition: windowProperties.h:98
WinGraphicsWindow::remove_window_proc
virtual void remove_window_proc(const GraphicsWindowProc *wnd_proc_object)
Removes the specified Windows proc event handler.
Definition: winGraphicsWindow.cxx:3086
WindowProperties::set_origin
set_origin
Specifies the origin on the screen (in pixels, relative to the top-left corner) at which the window s...
Definition: windowProperties.h:76
Filename::exists
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1267
pmap
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
GraphicsWindowInputDevice::focus_lost
void focus_lost(double time=ClockObject::get_global_clock() ->get_frame_time())
This should be called when the window focus is lost, so that we may miss upcoming button events (espe...
Definition: graphicsWindowInputDevice.cxx:134
GraphicsOutput::set_size_and_recalc
void set_size_and_recalc(int x, int y)
Changes the x_size and y_size, then recalculates structures that depend on size.
Definition: graphicsOutput.cxx:1111
mouseButton.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Filename::resolve_filename
bool resolve_filename(const DSearchPath &searchpath, const std::string &default_extension=std::string())
Searches the given search path for the filename.
Definition: filename.cxx:1581
WindowProperties::has_cursor_hidden
has_cursor_hidden
Returns true if set_cursor_hidden() has been specified.
Definition: windowProperties.h:151
GraphicsWindowInputDevice::button_down
void button_down(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been depressed.
Definition: graphicsWindowInputDevice.cxx:75
GraphicsWindowInputDevice::button_up
void button_up(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been released.
Definition: graphicsWindowInputDevice.cxx:97
WinGraphicsWindow::move_pointer
virtual bool move_pointer(int device, int x, int y)
Forces the pointer to the indicated position within the window, if possible.
Definition: winGraphicsWindow.cxx:156
WinGraphicsWindow::get_touch_info
virtual TouchInfo get_touch_info(int index)
Returns the TouchInfo object describing the specified touch.
Definition: winGraphicsWindow.cxx:3130
WindowProperties::get_y_size
int get_y_size() const
Returns size in pixels in the y dimension of the useful part of the window, not including decorations...
Definition: windowProperties.I:159
MouseData
BEGIN_PUBLISH typedef PointerData MouseData
Deprecated alias for PointerData.
Definition: mouseData.h:23
clockObject.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ButtonHandle
A ButtonHandle represents a single button from any device, including keyboard buttons and mouse butto...
Definition: buttonHandle.h:26
WindowProperties::clear_minimized
clear_minimized
Removes the minimized specification from the properties.
Definition: windowProperties.h:133
GraphicsWindowInputDevice::raw_button_down
void raw_button_down(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been depressed.
Definition: graphicsWindowInputDevice.cxx:147
ClockObject::get_global_clock
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
WindowProperties::set_open
set_open
Specifies whether the window should be open.
Definition: windowProperties.h:144
WindowProperties::has_foreground
has_foreground
Returns true if set_foreground() has been specified.
Definition: windowProperties.h:126
WinGraphicsWindow::window_proc
virtual LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
This is the nonstatic window_proc function.
Definition: winGraphicsWindow.cxx:1376
WindowProperties
A container for the various kinds of properties we might ask to have on a graphics window before we o...
Definition: windowProperties.h:29
WindowProperties::get_x_size
int get_x_size() const
Returns size in pixels in the x dimension of the useful part of the window, not including decorations...
Definition: windowProperties.I:149
WindowProperties::get_minimized
get_minimized
Returns true if the window is minimized.
Definition: windowProperties.h:133
keyboardButton.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
WindowProperties::has_undecorated
has_undecorated
Returns true if set_undecorated() has been specified.
Definition: windowProperties.h:105
WindowHandle
This object represents a window on the desktop, not necessarily a Panda window.
Definition: windowHandle.h:34
WindowProperties::set_foreground
set_foreground
Specifies whether the window should be opened in the foreground (true), or left in the background (fa...
Definition: windowProperties.h:126
GraphicsWindowInputDevice::raw_button_up
void raw_button_up(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been released.
Definition: graphicsWindowInputDevice.cxx:156
KeyboardButton::ascii_key
static ButtonHandle ascii_key(char ascii_equivalent)
Returns the ButtonHandle associated with the particular ASCII character, if there is one,...
Definition: keyboardButton.cxx:24
graphicsPipe.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
WinGraphicsWindow
An abstract base class for glGraphicsWindow and dxGraphicsWindow (and, in general,...
Definition: winGraphicsWindow.h:64
MouseButton::button
static ButtonHandle button(int button_number)
Returns the ButtonHandle associated with the particular numbered mouse button (zero-based),...
Definition: mouseButton.cxx:32
GraphicsEngine
This class is the main interface to controlling the render process.
Definition: graphicsEngine.h:53
WindowProperties::get_mouse_mode
get_mouse_mode
See set_mouse_mode().
Definition: windowProperties.h:92
WindowHandle::OSHandle
Definition: windowHandle.h:63
WindowProperties::clear_z_order
clear_z_order
Removes the z_order specification from the properties.
Definition: windowProperties.h:171
ClockObject::get_real_time
get_real_time
Returns the actual number of seconds elapsed since the ClockObject was created, or since it was last ...
Definition: clockObject.h:92
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
GraphicsOutput
This is a base class for the various different classes that represent the result of a frame of render...
Definition: graphicsOutput.h:63
WindowProperties::clear_title
clear_title
Removes the title specification from the properties.
Definition: windowProperties.h:98
WinGraphicsWindow::get_num_touches
virtual int get_num_touches()
Returns the current number of touches on this window.
Definition: winGraphicsWindow.cxx:3121
WindowProperties::set_size
set_size
Specifies the requested size of the window, in pixels.
Definition: windowProperties.h:85
WinGraphicsWindow::receive_windows_message
void receive_windows_message(unsigned int msg, int wparam, int lparam)
This is called to receive a keyboard event generated by proxy by another window in a parent process.
Definition: winGraphicsWindow.cxx:1362
WindowProperties::get_x_origin
int get_x_origin() const
Returns the x coordinate of the window's top-left corner, not including decorations.
Definition: windowProperties.I:82
GraphicsWindowInputDevice::keystroke
void keystroke(int keycode, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated keystroke has been generated.
Definition: graphicsWindowInputDevice.cxx:107
WindowProperties::get_fullscreen
get_fullscreen
Returns true if the window is in fullscreen mode.
Definition: windowProperties.h:119
WindowHandle::receive_windows_message
virtual void receive_windows_message(unsigned int msg, int wparam, int lparam)
Called on a child handle to deliver a keyboard button event generated in the parent window.
Definition: windowHandle.cxx:96
config_windisplay.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsWindowInputDevice::candidate
void candidate(const std::wstring &candidate_string, size_t highlight_start, size_t highlight_end, size_t cursor_pos)
Records that the indicated candidate string has been highlighted.
Definition: graphicsWindowInputDevice.cxx:118
WinGraphicsWindow::get_pointer
virtual MouseData get_pointer(int device) const
Returns the MouseData associated with the nth input device's pointer.
Definition: winGraphicsWindow.cxx:127
WindowProperties::clear_foreground
clear_foreground
Removes the foreground specification from the properties.
Definition: windowProperties.h:126
WinGraphicsWindow::set_properties_now
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
Definition: winGraphicsWindow.cxx:281
WindowProperties::set_mouse_mode
set_mouse_mode
Specifies the mode in which the window is to operate its mouse pointer.
Definition: windowProperties.h:92
WindowProperties::get_z_order
get_z_order
Returns the window's z_order.
Definition: windowProperties.h:171
WindowProperties::has_icon_filename
has_icon_filename
Returns true if set_icon_filename() has been specified.
Definition: windowProperties.h:158
WindowProperties::set_minimized
set_minimized
Specifies whether the window should be created minimized (true), or normal (false).
Definition: windowProperties.h:133
WindowProperties::is_any_specified
bool is_any_specified() const
Returns true if any properties have been specified, false otherwise.
Definition: windowProperties.I:41
GraphicsPipe
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
WindowProperties::get_foreground
get_foreground
Returns true if the window is in the foreground.
Definition: windowProperties.h:126
WinGraphicsWindow::static_window_proc
static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
This is attached to the window class for all WinGraphicsWindow windows; it is called to handle window...
Definition: winGraphicsWindow.cxx:2204
WindowProperties::has_cursor_filename
has_cursor_filename
Returns true if set_cursor_filename() has been specified.
Definition: windowProperties.h:165
WindowProperties::get_cursor_hidden
get_cursor_hidden
Returns true if the mouse cursor is invisible.
Definition: windowProperties.h:151
TouchInfo
Stores information for a single touch event.
Definition: touchInfo.h:22
get_client_rect_screen
void get_client_rect_screen(HWND hwnd, RECT *view_rect)
Fills view_rect with the coordinates of the client area of the indicated window, converted to screen ...
Definition: winGraphicsWindow.cxx:3054
GraphicsWindow::get_close_request_event
get_close_request_event
Returns the name of the event set via set_close_request_event().
Definition: graphicsWindow.h:74
WindowProperties::has_minimized
has_minimized
Returns true if set_minimized() has been specified.
Definition: windowProperties.h:133
GraphicsWindowInputDevice::set_pointer_in_window
void set_pointer_in_window(double x, double y, double time=ClockObject::get_global_clock() ->get_frame_time())
To be called by a particular kind of GraphicsWindow to indicate that the pointer is within the window...
Definition: graphicsWindowInputDevice.cxx:166
WinGraphicsPipe
This is an abstract base class for wglGraphicsPipe and wdxGraphicsPipe; that is, those graphics pipes...
Definition: winGraphicsPipe.h:30
GraphicsWindowProc
Defines an interface for storing platform-specific window processor methods.
Definition: graphicsWindowProc.h:32
WindowProperties::clear_cursor_filename
clear_cursor_filename
Removes the cursor_filename specification from the properties.
Definition: windowProperties.h:165
WindowProperties::get_fixed_size
get_fixed_size
Returns true if the window cannot be resized by the user, false otherwise.
Definition: windowProperties.h:112
WindowProperties::clear_icon_filename
clear_icon_filename
Removes the icon_filename specification from the properties.
Definition: windowProperties.h:158
WindowProperties::has_fullscreen
has_fullscreen
Returns true if set_fullscreen() has been specified.
Definition: windowProperties.h:119
WinGraphicsWindow::add_window_proc
virtual void add_window_proc(const GraphicsWindowProc *wnd_proc_object)
Adds the specified Windows proc event handler to be called whenever a Windows event occurs.
Definition: winGraphicsWindow.cxx:3077
MouseButton::wheel_up
static ButtonHandle wheel_up()
Returns the ButtonHandle generated when the mouse wheel is rolled one notch upwards.
Definition: mouseButton.cxx:84
WindowProperties::get_cursor_filename
get_cursor_filename
Returns the icon filename associated with the mouse cursor.
Definition: windowProperties.h:165
GraphicsWindowInputDevice::set_pointer_out_of_window
void set_pointer_out_of_window(double time=ClockObject::get_global_clock() ->get_frame_time())
To be called by a particular kind of GraphicsWindow to indicate that the pointer is no longer within ...
Definition: graphicsWindowInputDevice.cxx:182
TextEncoder::get_text
get_text
Returns the current text, as encoded via the current encoding system.
Definition: textEncoder.h:124
MouseButton::wheel_down
static ButtonHandle wheel_down()
Returns the ButtonHandle generated when the mouse wheel is rolled one notch downwards.
Definition: mouseButton.cxx:93
WindowProperties::has_z_order
has_z_order
Returns true if the window z_order has been specified, false otherwise.
Definition: windowProperties.h:171
nativeWindowHandle.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsWindow::get_unexposed_draw
get_unexposed_draw
See set_unexposed_draw().
Definition: graphicsWindow.h:78
WinGraphicsWindow::supports_window_procs
virtual bool supports_window_procs() const
Returns whether this window supports adding of windows proc handlers.
Definition: winGraphicsWindow.cxx:3103
WindowProperties::get_title
get_title
Returns the window's title.
Definition: windowProperties.h:98
GraphicsStateGuardian
Encapsulates all the communication with a particular instance of a given rendering backend.
Definition: graphicsStateGuardian.h:65
WinGraphicsWindow::is_touch_event
virtual bool is_touch_event(GraphicsWindowProcCallbackData *callbackData)
Returns whether the specified event msg is a touch message.
Definition: winGraphicsWindow.cxx:3112
WinGraphicsWindow::close_ime
virtual void close_ime()
Forces the ime window to close, if any.
Definition: winGraphicsWindow.cxx:195
GraphicsWindow::process_events
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
Definition: graphicsWindow.cxx:459
WinGraphicsWindow::clear_window_procs
virtual void clear_window_procs()
Removes all Windows proc event handlers.
Definition: winGraphicsWindow.cxx:3095
TextEncoder
This class can be used to convert text between multiple representations, e.g.
Definition: textEncoder.h:33
GraphicsWindowProcCallbackData
This specialization on CallbackData is passed when the callback is initiated from from an implementat...
Definition: graphicsWindowProcCallbackData.h:28
WindowProperties::clear_fixed_size
clear_fixed_size
Removes the fixed_size specification from the properties.
Definition: windowProperties.h:112
WindowProperties::get_icon_filename
get_icon_filename
Returns the icon filename associated with the window.
Definition: windowProperties.h:158
GraphicsWindow::is_fullscreen
bool is_fullscreen() const
Returns true if the window has been opened as a fullscreen window, false otherwise.
Definition: graphicsWindow.I:31
WindowProperties::clear_mouse_mode
clear_mouse_mode
Removes the mouse_mode specification from the properties.
Definition: windowProperties.h:92
WinGraphicsWindow::process_events
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
Definition: winGraphicsWindow.cxx:236
TextEncoder::decode_text
std::wstring decode_text(const std::string &text) const
Returns the given wstring decoded to a single-byte string, via the current encoding system.
Definition: textEncoder.I:490
GraphicsWindow
A window, fullscreen or on a desktop, into which a graphics device sends its output for interactive d...
Definition: graphicsWindow.h:40
WindowProperties::has_fixed_size
has_fixed_size
Returns true if set_fixed_size() has been specified.
Definition: windowProperties.h:112
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
WindowHandle::get_os_handle
get_os_handle
Returns the OS-specific handle stored internally to the WindowHandle wrapper.
Definition: windowHandle.h:44
ButtonMap
This class represents a map containing all of the buttons of a (keyboard) device, though it can also ...
Definition: buttonMap.h:30
WindowProperties::clear_cursor_hidden
clear_cursor_hidden
Removes the cursor_hidden specification from the properties.
Definition: windowProperties.h:151
TypedObject::is_of_type
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
WindowProperties::get_y_origin
int get_y_origin() const
Returns the y coordinate of the window's top-left corner, not including decorations.
Definition: windowProperties.I:92
Filename::is_fully_qualified
bool is_fully_qualified() const
Returns true if the filename is fully qualified, e.g.
Definition: filename.I:562
pset
This is our own Panda specialization on the default STL set.
Definition: pset.h:49