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  */
195 close_ime() {
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  */
225 begin_flip() {
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  */
236 process_events() {
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_CLIPCHILDREN | WS_CLIPSIBLINGS;
977 
978  if (properties.get_fullscreen()) {
979  window_style |= WS_POPUP | WS_SYSMENU;
980  } else {
981  if (_parent_window_handle) {
982  window_style |= WS_CHILD;
983  } else {
984  window_style |= WS_POPUP;
985  }
986 
987  if (!properties.get_undecorated()) {
988  window_style |= (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX);
989 
990  if (!properties.get_fixed_size()) {
991  window_style |= (WS_SIZEBOX | WS_MAXIMIZEBOX);
992  } else {
993  window_style |= WS_BORDER;
994  }
995  }
996  }
997  return window_style;
998 }
999 
1000 /**
1001  * Calculates the metrics for the specified mode, be it windowed or
1002  * fullscreen.
1003  */
1004 bool WinGraphicsWindow::
1005 calculate_metrics(bool fullscreen, DWORD window_style, WINDOW_METRICS &metrics,
1006  bool &has_origin) {
1007  metrics.x = 0;
1008  metrics.y = 0;
1009  has_origin = _properties.has_origin();
1010  if (!fullscreen && has_origin) {
1011  metrics.x = _properties.get_x_origin();
1012  metrics.y = _properties.get_y_origin();
1013 
1014  // A coordinate of -2 means to center the window in its client area.
1015  if (metrics.x == -2) {
1016  metrics.x = 0.5 * (_pipe->get_display_width() - _properties.get_x_size());
1017  }
1018  if (metrics.y == -2) {
1019  metrics.y = 0.5 * (_pipe->get_display_height() - _properties.get_y_size());
1020  }
1021  _properties.set_origin(metrics.x, metrics.y);
1022 
1023  if (metrics.x == -1 && metrics.y == -1) {
1024  metrics.x = 0;
1025  metrics.y = 0;
1026  has_origin = false;
1027  }
1028  }
1029 
1030  metrics.width = _properties.get_x_size();
1031  metrics.height = _properties.get_y_size();
1032 
1033  if (!fullscreen){
1034  RECT win_rect;
1035  SetRect(&win_rect, metrics.x, metrics.y,
1036  metrics.x + metrics.width, metrics.y + metrics.height);
1037 
1038  // Compute window size based on desired client area size
1039  if (!AdjustWindowRect(&win_rect, window_style, FALSE)) {
1040  windisplay_cat.error()
1041  << "AdjustWindowRect failed!" << endl;
1042  return false;
1043  }
1044 
1045  if (has_origin) {
1046  metrics.x = win_rect.left;
1047  metrics.y = win_rect.top;
1048  } else {
1049  metrics.x = CW_USEDEFAULT;
1050  metrics.y = CW_USEDEFAULT;
1051  }
1052  metrics.width = win_rect.right - win_rect.left;
1053  metrics.height = win_rect.bottom - win_rect.top;
1054  }
1055 
1056  return true;
1057 }
1058 
1059 /**
1060  * Creates a regular or fullscreen window.
1061  */
1062 bool WinGraphicsWindow::
1063 open_graphic_window() {
1064  DWORD window_style = make_style(_properties);
1065 
1066  wstring title;
1067  if (_properties.has_title()) {
1068  TextEncoder encoder;
1069  title = encoder.decode_text(_properties.get_title());
1070  }
1071 
1072  if (!_properties.has_size()) {
1073  // Just fill in a conservative default size if one isn't specified.
1074  _properties.set_size(640, 480);
1075  }
1076 
1077  WINDOW_METRICS metrics;
1078  bool has_origin;
1079  if (!calculate_metrics(fullscreen, window_style, metrics, has_origin)){
1080  return false;
1081  }
1082 
1083  const WindowClass &wclass = register_window_class(_properties);
1084  HINSTANCE hinstance = GetModuleHandle(nullptr);
1085 
1086  _hparent = nullptr;
1087 
1088  if (!fullscreen){
1089  WindowHandle *window_handle = _properties.get_parent_window();
1090  if (window_handle != nullptr) {
1091  windisplay_cat.info()
1092  << "Got parent_window " << *window_handle << "\n";
1093  WindowHandle::OSHandle *os_handle = window_handle->get_os_handle();
1094  if (os_handle != nullptr) {
1095  windisplay_cat.info()
1096  << "os_handle type " << os_handle->get_type() << "\n";
1097 
1098  if (os_handle->is_of_type(NativeWindowHandle::WinHandle::get_class_type())) {
1099  NativeWindowHandle::WinHandle *win_handle = DCAST(NativeWindowHandle::WinHandle, os_handle);
1100  _hparent = win_handle->get_handle();
1101  } else if (os_handle->is_of_type(NativeWindowHandle::IntHandle::get_class_type())) {
1102  NativeWindowHandle::IntHandle *int_handle = DCAST(NativeWindowHandle::IntHandle, os_handle);
1103  _hparent = (HWND)int_handle->get_handle();
1104  }
1105  }
1106  }
1107  _parent_window_handle = window_handle;
1108  } else {
1109  _parent_window_handle = nullptr;
1110  }
1111 
1112  if (!_hparent) { // This can be a regular window or a fullscreen window
1113  _hWnd = CreateWindowW(wclass._name.c_str(), title.c_str(), window_style,
1114  metrics.x, metrics.y,
1115  metrics.width,
1116  metrics.height,
1117  nullptr, nullptr, hinstance, 0);
1118  } else { // This is a regular window with a parent
1119  int x_origin = 0;
1120  int y_origin = 0;
1121 
1122  if (!fullscreen && has_origin) {
1123  x_origin = _properties.get_x_origin();
1124  y_origin = _properties.get_y_origin();
1125  }
1126 
1127  _hWnd = CreateWindowW(wclass._name.c_str(), title.c_str(),
1128  WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS ,
1129  x_origin, y_origin,
1130  _properties.get_x_size(), _properties.get_y_size(),
1131  _hparent, nullptr, hinstance, 0);
1132 
1133  if (_hWnd) {
1134  // join our keyboard state with the parents
1135 
1136  // Actually, let's not. Is there really any reason to do this? It
1137  // causes problems with the browser plugin--it deadlocks when the parent
1138  // process is waiting on the child process.
1139  // AttachThreadInput(GetWindowThreadProcessId(_hparent,NULL),
1140  // GetCurrentThreadId(),TRUE);
1141 
1142  WindowProperties properties;
1143  properties.set_foreground(true);
1144  system_changed_properties(properties);
1145  }
1146  }
1147 
1148  if (!_hWnd) {
1149  windisplay_cat.error()
1150  << "CreateWindow() failed!" << endl;
1151  show_error_message();
1152  return false;
1153  }
1154 
1155  // I'd prefer to CreateWindow after DisplayChange in case it messes up GL
1156  // somehow, but I need the window's black background to cover up the desktop
1157  // during the mode change.
1158 
1159  if (fullscreen){
1160  if (!do_fullscreen_enable()){
1161  return false;
1162  }
1163  }
1164 
1165  return true;
1166 }
1167 
1168 /**
1169  * This is a low-level function that just puts Windows in fullscreen mode.
1170  * Not to confuse with do_fullscreen_switch().
1171  */
1172 bool WinGraphicsWindow::
1173 do_fullscreen_enable() {
1174 
1175  HWND hDesktopWindow = GetDesktopWindow();
1176  HDC scrnDC = GetDC(hDesktopWindow);
1177  DWORD cur_bitdepth = GetDeviceCaps(scrnDC, BITSPIXEL);
1178  // DWORD drvr_ver = GetDeviceCaps(scrnDC, DRIVERVERSION); DWORD
1179  // cur_scrnwidth = GetDeviceCaps(scrnDC, HORZRES); DWORD cur_scrnheight =
1180  // GetDeviceCaps(scrnDC, VERTRES);
1181  ReleaseDC(hDesktopWindow, scrnDC);
1182 
1183  DWORD dwWidth = _properties.get_x_size();
1184  DWORD dwHeight = _properties.get_y_size();
1185  DWORD dwFullScreenBitDepth = cur_bitdepth;
1186 
1187  DEVMODE dm;
1188  reconsider_fullscreen_size(dwWidth, dwHeight, dwFullScreenBitDepth);
1189  if (!find_acceptable_display_mode(dwWidth, dwHeight, dwFullScreenBitDepth, dm)) {
1190  windisplay_cat.error()
1191  << "Videocard has no supported display resolutions at specified res ("
1192  << dwWidth << " x " << dwHeight << " x " << dwFullScreenBitDepth <<")\n";
1193  return false;
1194  }
1195 
1196  dm.dmPelsWidth = dwWidth;
1197  dm.dmPelsHeight = dwHeight;
1198  dm.dmBitsPerPel = dwFullScreenBitDepth;
1199  int chg_result = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
1200 
1201  if (chg_result != DISP_CHANGE_SUCCESSFUL) {
1202  windisplay_cat.error()
1203  << "ChangeDisplaySettings failed (error code: "
1204  << chg_result << ") for specified res: "
1205  << dm.dmPelsWidth << " x " << dm.dmPelsHeight
1206  << " x " << dm.dmBitsPerPel << ", "
1207  << dm.dmDisplayFrequency << " Hz\n";
1208  return false;
1209  }
1210 
1211  _fullscreen_display_mode = dm;
1212 
1213  _properties.set_origin(0, 0);
1214  _properties.set_size(dwWidth, dwHeight);
1215 
1216  return true;
1217 
1218 }
1219 
1220 /**
1221  * This is a low-level function that just gets Windows out of fullscreen mode.
1222  * Not to confuse with do_windowed_switch().
1223  */
1224 bool WinGraphicsWindow::
1225 do_fullscreen_disable() {
1226  int chg_result = ChangeDisplaySettings(nullptr, 0x0);
1227  if (chg_result != DISP_CHANGE_SUCCESSFUL) {
1228  windisplay_cat.warning()
1229  << "ChangeDisplaySettings failed to restore Windowed mode\n";
1230  return false;
1231  }
1232  return true;
1233 }
1234 
1235 /**
1236  * Adjusts the Z-order of a window after it has been moved.
1237  */
1238 void WinGraphicsWindow::
1239 adjust_z_order() {
1240  WindowProperties::ZOrder z_order = _properties.get_z_order();
1241  adjust_z_order(z_order, z_order);
1242 }
1243 
1244 /**
1245  * Adjusts the Z-order of a window after it has been moved.
1246  */
1247 void WinGraphicsWindow::
1248 adjust_z_order(WindowProperties::ZOrder last_z_order,
1249  WindowProperties::ZOrder this_z_order) {
1250  HWND order;
1251  bool do_change = false;
1252 
1253  switch (this_z_order) {
1254  case WindowProperties::Z_bottom:
1255  order = HWND_BOTTOM;
1256  do_change = true;
1257  break;
1258 
1259  case WindowProperties::Z_normal:
1260  if ((last_z_order != WindowProperties::Z_normal) &&
1261  // If we aren't changing the window order, don't move it to the top.
1262  (last_z_order != WindowProperties::Z_bottom ||
1263  _properties.get_foreground())
1264  // If the window was previously on the bottom, but it doesn't have
1265  // focus now, don't move it to the top; it will get moved the next
1266  // time we get focus.
1267  ) {
1268  order = HWND_NOTOPMOST;
1269  do_change = true;
1270  }
1271  break;
1272 
1273  case WindowProperties::Z_top:
1274  order = HWND_TOPMOST;
1275  do_change = true;
1276  break;
1277  }
1278  if (do_change) {
1279  BOOL result = SetWindowPos(_hWnd, order, 0,0,0,0,
1280  SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE);
1281  if (!result) {
1282  windisplay_cat.warning()
1283  << "SetWindowPos failed.\n";
1284  }
1285  }
1286 }
1287 
1288 /**
1289  * Intended to be called whenever mouse motion is detected within the window,
1290  * this indicates that the mouse is within the window and tells Windows that
1291  * we want to be told when the mouse leaves the window.
1292  */
1293 void WinGraphicsWindow::
1294 track_mouse_leaving(HWND hwnd) {
1295  WinGraphicsPipe *winpipe;
1296  DCAST_INTO_V(winpipe, _pipe);
1297 
1298  TRACKMOUSEEVENT tme = {
1299  sizeof(TRACKMOUSEEVENT),
1300  TME_LEAVE,
1301  hwnd,
1302  0
1303  };
1304 
1305  // tell win32 to post WM_MOUSELEAVE msgs
1306  BOOL bSucceeded = TrackMouseEvent(&tme);
1307 
1308  if (!bSucceeded && windisplay_cat.is_debug()) {
1309  windisplay_cat.debug()
1310  << "TrackMouseEvent failed!, LastError=" << GetLastError() << endl;
1311  }
1312 
1313  _tracking_mouse_leaving = true;
1314 }
1315 
1316 /**
1317  * Confines the mouse cursor to the window.
1318  */
1319 bool WinGraphicsWindow::
1320 confine_cursor() {
1321  RECT clip;
1322  get_client_rect_screen(_hWnd, &clip);
1323 
1324  windisplay_cat.info()
1325  << "ClipCursor() to " << clip.left << "," << clip.top << " to "
1326  << clip.right << "," << clip.bottom << endl;
1327 
1328  if (!ClipCursor(&clip)) {
1329  windisplay_cat.warning()
1330  << "Failed to confine cursor to window.\n";
1331  return false;
1332  } else {
1333  return true;
1334  }
1335 }
1336 
1337 /**
1338  * Attempts to set this window as the "focus" window, so that keyboard events
1339  * come here.
1340  */
1341 void WinGraphicsWindow::
1342 set_focus() {
1343  if (SetFocus(_hWnd) == nullptr && GetLastError() != 0) {
1344  // If the SetFocus() request failed, maybe we're running in the plugin
1345  // environment on Vista, with UAC enabled. In this case, we're not
1346  // allowed to assign focus to the Panda window for some stupid reason. So
1347  // instead, we have to ask the parent window (in the browser process) to
1348  // proxy our keyboard events for us.
1349  if (_parent_window_handle != nullptr && _window_handle != nullptr) {
1350  _parent_window_handle->request_keyboard_focus(_window_handle);
1351  } else {
1352  // Otherwise, something is wrong.
1353  windisplay_cat.error()
1354  << "SetFocus failed: " << GetLastError() << "\n";
1355  }
1356  }
1357 }
1358 
1359 /**
1360  * This is called to receive a keyboard event generated by proxy by another
1361  * window in a parent process. This hacky system is used in the web plugin
1362  * system to allow the Panda window to receive keyboard events on Vista, which
1363  * doesn't allow the Panda window to set keyboard focus to itself.
1364  */
1366 receive_windows_message(unsigned int msg, int wparam, int lparam) {
1367  // Well, we'll just deliver this directly to window_proc(), supplying our
1368  // own window handle. For the most part, we don't care about the window
1369  // handle anyway, but this might become an issue for the IME. TODO:
1370  // investigate IME issues.
1371 
1372  window_proc(_hWnd, msg, wparam, lparam);
1373 }
1374 
1375 /**
1376  * This is the nonstatic window_proc function. It is called to handle window
1377  * events for this particular window.
1378  */
1380 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
1381  if (windisplay_cat.is_spam()) {
1382  windisplay_cat.spam()
1384  << " window_proc(" << (void *)this << ", " << hwnd << ", "
1385  << msg << ", " << wparam << ", " << lparam << ")\n";
1386  }
1387  WindowProperties properties;
1388 
1389  switch (msg) {
1390  case WM_MOUSEMOVE:
1391  if (!_tracking_mouse_leaving) {
1392  // need to re-call TrackMouseEvent every time mouse re-enters window
1393  track_mouse_leaving(hwnd);
1394  }
1395  set_cursor_in_window();
1396  if(handle_mouse_motion(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam))))
1397  return 0;
1398  break;
1399 
1400  case WM_INPUT:
1401  handle_raw_input((HRAWINPUT)lparam);
1402  break;
1403 
1404  case WM_MOUSELEAVE:
1405  _tracking_mouse_leaving = false;
1406  handle_mouse_exit();
1407  set_cursor_out_of_window();
1408  break;
1409 
1410  case WM_CREATE:
1411  {
1412  track_mouse_leaving(hwnd);
1413  ClearToBlack(hwnd, _properties);
1414 
1415  POINT cpos;
1416  GetCursorPos(&cpos);
1417  ScreenToClient(hwnd, &cpos);
1418  RECT clientRect;
1419  GetClientRect(hwnd, &clientRect);
1420  if (PtInRect(&clientRect,cpos)) {
1421  set_cursor_in_window(); // should window focus be true as well?
1422  } else {
1423  set_cursor_out_of_window();
1424  }
1425  }
1426  break;
1427 
1428  /*
1429  case WM_SHOWWINDOW:
1430  // You'd think WM_SHOWWINDOW would be just the thing for embedded windows,
1431  // but it turns out it's not sent to the child windows when the parent is
1432  // minimized. I guess it's only sent for an explicit call to ShowWindow,
1433  // phooey.
1434  {
1435  if (windisplay_cat.is_debug()) {
1436  windisplay_cat.debug()
1437  << "WM_SHOWWINDOW: " << hwnd << ", " << wparam << "\n";
1438  }
1439  if (wparam) {
1440  // Window is being shown.
1441  properties.set_minimized(false);
1442  } else {
1443  // Window is being hidden.
1444  properties.set_minimized(true);
1445  }
1446  system_changed_properties(properties);
1447  }
1448  break;
1449  */
1450 
1451  case WM_CLOSE:
1452  // This is a message from the system indicating that the user has
1453  // requested to close the window (e.g. alt-f4).
1454  {
1455  std::string close_request_event = get_close_request_event();
1456  if (!close_request_event.empty()) {
1457  // In this case, the app has indicated a desire to intercept the
1458  // request and process it directly.
1459  throw_event(close_request_event);
1460  return 0;
1461 
1462  } else {
1463  // In this case, the default case, the app does not intend to service
1464  // the request, so we do by closing the window.
1465  close_window();
1466  properties.set_open(false);
1467  system_changed_properties(properties);
1468 
1469  // TODO: make sure we release the GSG properly.
1470  }
1471  }
1472  break;
1473 
1474  case WM_CHILDACTIVATE:
1475  if (windisplay_cat.is_debug()) {
1476  windisplay_cat.debug()
1477  << "WM_CHILDACTIVATE: " << hwnd << "\n";
1478  }
1479  break;
1480 
1481  case WM_ACTIVATE:
1482  if (windisplay_cat.is_debug()) {
1483  windisplay_cat.debug()
1484  << "WM_ACTIVATE: " << hwnd << ", " << wparam << ", " << lparam << "\n";
1485  }
1486  properties.set_minimized((wparam & 0xffff0000) != 0);
1487  if ((wparam & 0xffff) != WA_INACTIVE)
1488  {
1489  properties.set_foreground(true);
1490  if (is_fullscreen())
1491  {
1492  // When a fullscreen window goes active, it automatically gets un-
1493  // minimized.
1494  int chg_result =
1495  ChangeDisplaySettings(&_fullscreen_display_mode, CDS_FULLSCREEN);
1496  if (chg_result != DISP_CHANGE_SUCCESSFUL) {
1497  const DEVMODE &dm = _fullscreen_display_mode;
1498  windisplay_cat.error()
1499  << "restore ChangeDisplaySettings failed (error code: "
1500  << chg_result << ") for specified res: "
1501  << dm.dmPelsWidth << " x " << dm.dmPelsHeight
1502  << " x " << dm.dmBitsPerPel << ", "
1503  << dm.dmDisplayFrequency << " Hz\n";
1504  }
1505 
1506  GdiFlush();
1507  SetWindowPos(_hWnd, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOOWNERZORDER);
1508  fullscreen_restored(properties);
1509  }
1510 
1511  // If we had a confined cursor, we must reconfine it upon activation.
1512  if (_properties.has_mouse_mode() &&
1513  _properties.get_mouse_mode() == WindowProperties::M_confined) {
1514  if (!confine_cursor()) {
1515  properties.set_mouse_mode(WindowProperties::M_absolute);
1516  }
1517  }
1518  }
1519  else
1520  {
1521  properties.set_foreground(false);
1522  if (is_fullscreen())
1523  {
1524  // When a fullscreen window goes inactive, it automatically gets
1525  // minimized.
1526  properties.set_minimized(true);
1527 
1528  // It seems order is important here. We must minimize the window
1529  // before restoring the display settings, or risk losing the
1530  // graphics context.
1531  ShowWindow(_hWnd, SW_MINIMIZE);
1532  GdiFlush();
1533  do_fullscreen_disable();
1534  fullscreen_minimized(properties);
1535  }
1536  }
1537 
1538  adjust_z_order();
1539  system_changed_properties(properties);
1540  break;
1541 
1542  case WM_SIZE:
1543  // Actually, since we don't return in WM_WINDOWPOSCHANGED, WM_SIZE won't
1544  // end up being called at all. This is more efficient according to MSDN.
1545  if (windisplay_cat.is_debug()) {
1546  windisplay_cat.debug()
1547  << "WM_SIZE: " << hwnd << ", " << wparam << "\n";
1548  }
1549  break;
1550 
1551  case WM_EXITSIZEMOVE:
1552  if (windisplay_cat.is_debug()) {
1553  windisplay_cat.debug()
1554  << "WM_EXITSIZEMOVE: " << hwnd << ", " << wparam << "\n";
1555  }
1556 
1557  // If we had a confined cursor, we must reconfine it upon a resize.
1558  if (_properties.has_mouse_mode() &&
1559  _properties.get_mouse_mode() == WindowProperties::M_confined) {
1560  confine_cursor();
1561  }
1562  break;
1563 
1564  case WM_WINDOWPOSCHANGED:
1565  if (windisplay_cat.is_debug()) {
1566  windisplay_cat.debug()
1567  << "WM_WINDOWPOSCHANGED: " << hwnd << ", " << wparam << "\n";
1568  }
1569  if (_hWnd != nullptr) {
1570  handle_reshape();
1571  }
1572  adjust_z_order();
1573  return 0;
1574 
1575  case WM_PAINT:
1576  // In response to WM_PAINT, we check to see if there are any update
1577  // regions at all; if there are, we declare the window exposed. This is
1578  // used to implement !_unexposed_draw.
1579  if (GetUpdateRect(_hWnd, nullptr, false)) {
1580  if (windisplay_cat.is_spam()) {
1581  windisplay_cat.spam()
1582  << "Got update regions: " << this << "\n";
1583  }
1584  _got_expose_event = true;
1585  }
1586  break;
1587 
1588  case WM_LBUTTONDOWN:
1589  if (_lost_keypresses) {
1590  resend_lost_keypresses();
1591  }
1592  SetCapture(hwnd);
1593  _input->set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam)));
1594  _input->button_down(MouseButton::button(0), get_message_time());
1595 
1596  // A button-click in the window means to grab the keyboard focus.
1597  set_focus();
1598  return 0;
1599 
1600  case WM_MBUTTONDOWN:
1601  if (_lost_keypresses) {
1602  resend_lost_keypresses();
1603  }
1604  SetCapture(hwnd);
1605  _input->set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam)));
1606  _input->button_down(MouseButton::button(1), get_message_time());
1607  // A button-click in the window means to grab the keyboard focus.
1608  set_focus();
1609  return 0;
1610 
1611  case WM_RBUTTONDOWN:
1612  if (_lost_keypresses) {
1613  resend_lost_keypresses();
1614  }
1615  SetCapture(hwnd);
1616  _input->set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam)));
1617  _input->button_down(MouseButton::button(2), get_message_time());
1618  // A button-click in the window means to grab the keyboard focus.
1619  set_focus();
1620  return 0;
1621 
1622  case WM_XBUTTONDOWN:
1623  {
1624  if (_lost_keypresses) {
1625  resend_lost_keypresses();
1626  }
1627  SetCapture(hwnd);
1628  int whichButton = GET_XBUTTON_WPARAM(wparam);
1629  _input->set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam)));
1630  if (whichButton == XBUTTON1) {
1631  _input->button_down(MouseButton::button(3), get_message_time());
1632  } else if (whichButton == XBUTTON2) {
1633  _input->button_down(MouseButton::button(4), get_message_time());
1634  }
1635  }
1636  return 0;
1637 
1638  case WM_LBUTTONUP:
1639  if (_lost_keypresses) {
1640  resend_lost_keypresses();
1641  }
1642  ReleaseCapture();
1643  _input->button_up(MouseButton::button(0), get_message_time());
1644  return 0;
1645 
1646  case WM_MBUTTONUP:
1647  if (_lost_keypresses) {
1648  resend_lost_keypresses();
1649  }
1650  ReleaseCapture();
1651  _input->button_up(MouseButton::button(1), get_message_time());
1652  return 0;
1653 
1654  case WM_RBUTTONUP:
1655  if (_lost_keypresses) {
1656  resend_lost_keypresses();
1657  }
1658  ReleaseCapture();
1659  _input->button_up(MouseButton::button(2), get_message_time());
1660  return 0;
1661 
1662  case WM_XBUTTONUP:
1663  {
1664  if (_lost_keypresses) {
1665  resend_lost_keypresses();
1666  }
1667  ReleaseCapture();
1668  int whichButton = GET_XBUTTON_WPARAM(wparam);
1669  if (whichButton == XBUTTON1) {
1670  _input->button_up(MouseButton::button(3), get_message_time());
1671  } else if (whichButton == XBUTTON2) {
1672  _input->button_up(MouseButton::button(4), get_message_time());
1673  }
1674  }
1675  return 0;
1676 
1677  case WM_MOUSEWHEEL:
1678  {
1679  int delta = GET_WHEEL_DELTA_WPARAM(wparam);
1680 
1681  POINT point;
1682  GetCursorPos(&point);
1683  ScreenToClient(hwnd, &point);
1684  double time = get_message_time();
1685 
1686  if (delta >= 0) {
1687  while (delta > 0) {
1688  handle_keypress(MouseButton::wheel_up(), point.x, point.y, time);
1689  handle_keyrelease(MouseButton::wheel_up(), time);
1690  delta -= WHEEL_DELTA;
1691  }
1692  } else {
1693  while (delta < 0) {
1694  handle_keypress(MouseButton::wheel_down(), point.x, point.y, time);
1695  handle_keyrelease(MouseButton::wheel_down(), time);
1696  delta += WHEEL_DELTA;
1697  }
1698  }
1699  return 0;
1700  }
1701  break;
1702 
1703 
1704  case WM_IME_SETCONTEXT:
1705  if (!ime_hide)
1706  break;
1707 
1708  windisplay_cat.debug() << "hwnd = " << hwnd << " and GetFocus = " << GetFocus() << endl;
1709  _ime_hWnd = ImmGetDefaultIMEWnd(hwnd);
1710  if (::SendMessage(_ime_hWnd, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0))
1711  // if (::SendMessage(hwnd, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0))
1712  windisplay_cat.debug() << "SendMessage failed for " << _ime_hWnd << endl;
1713  else
1714  windisplay_cat.debug() << "SendMessage Succeeded for " << _ime_hWnd << endl;
1715 
1716  windisplay_cat.debug() << "wparam is " << wparam << ", lparam is " << lparam << endl;
1717  lparam &= ~ISC_SHOWUIALL;
1718  if (ImmIsUIMessage(_ime_hWnd, msg, wparam, lparam))
1719  windisplay_cat.debug() << "wparam is " << wparam << ", lparam is " << lparam << endl;
1720 
1721  break;
1722 
1723 
1724  case WM_IME_NOTIFY:
1725  if (wparam == IMN_SETOPENSTATUS) {
1726  HIMC hIMC = ImmGetContext(hwnd);
1727  nassertr(hIMC != 0, 0);
1728  _ime_open = (ImmGetOpenStatus(hIMC) != 0);
1729  if (!_ime_open) {
1730  _ime_active = false; // Sanity enforcement.
1731  }
1732  if (ime_hide) {
1733  // if (0) {
1734  COMPOSITIONFORM comf;
1735  CANDIDATEFORM canf;
1736  ImmGetCompositionWindow(hIMC, &comf);
1737  ImmGetCandidateWindow(hIMC, 0, &canf);
1738  windisplay_cat.debug() <<
1739  "comf style " << comf.dwStyle <<
1740  " comf point: x" << comf.ptCurrentPos.x << ",y " << comf.ptCurrentPos.y <<
1741  " comf rect: l " << comf.rcArea.left << ",t " << comf.rcArea.top << ",r " <<
1742  comf.rcArea.right << ",b " << comf.rcArea.bottom << endl;
1743  windisplay_cat.debug() <<
1744  "canf style " << canf.dwStyle <<
1745  " canf point: x" << canf.ptCurrentPos.x << ",y " << canf.ptCurrentPos.y <<
1746  " canf rect: l " << canf.rcArea.left << ",t " << canf.rcArea.top << ",r " <<
1747  canf.rcArea.right << ",b " << canf.rcArea.bottom << endl;
1748  comf.dwStyle = CFS_POINT;
1749  comf.ptCurrentPos.x = 2000;
1750  comf.ptCurrentPos.y = 2000;
1751 
1752  canf.dwStyle = CFS_EXCLUDE;
1753  canf.dwIndex = 0;
1754  canf.ptCurrentPos.x = 0;
1755  canf.ptCurrentPos.y = 0;
1756  canf.rcArea.left = 0;
1757  canf.rcArea.top = 0;
1758  canf.rcArea.right = 640;
1759  canf.rcArea.bottom = 480;
1760 
1761 #if 0
1762  comf.rcArea.left = 200;
1763  comf.rcArea.top = 200;
1764  comf.rcArea.right = 0;
1765  comf.rcArea.bottom = 0;
1766 #endif
1767 
1768  if (ImmSetCompositionWindow(hIMC, &comf))
1769  windisplay_cat.debug() << "set composition form: success\n";
1770  for (int i=0; i<3; ++i) {
1771  if (ImmSetCandidateWindow(hIMC, &canf))
1772  windisplay_cat.debug() << "set candidate form: success\n";
1773  canf.dwIndex++;
1774  }
1775  }
1776 
1777  ImmReleaseContext(hwnd, hIMC);
1778  }
1779  break;
1780 
1781  case WM_IME_STARTCOMPOSITION:
1782  support_overlay_window(true);
1783  _ime_active = true;
1784  break;
1785 
1786  case WM_IME_ENDCOMPOSITION:
1787  support_overlay_window(false);
1788  _ime_active = false;
1789 
1790  if (ime_aware) {
1791  wstring ws;
1792  _input->candidate(ws, 0, 0, 0);
1793  }
1794 
1795  break;
1796 
1797  case WM_IME_COMPOSITION:
1798  if (ime_aware) {
1799 
1800  // If the ime window is not marked as active at this point, we must be
1801  // in the process of closing it down (in close_ime), and we don't want
1802  // to send the current composition string in that case. But we do need
1803  // to return 0 to tell windows not to try to send the composition string
1804  // through WM_CHAR messages.
1805  if (!_ime_active) {
1806  return 0;
1807  }
1808 
1809  HIMC hIMC = ImmGetContext(hwnd);
1810  nassertr(hIMC != 0, 0);
1811 
1812  DWORD result_size = 0;
1813  static const int ime_buffer_size = 256;
1814  static const int ime_buffer_size_bytes = ime_buffer_size / sizeof(wchar_t);
1815  wchar_t ime_buffer[ime_buffer_size];
1816  size_t cursor_pos, delta_start;
1817 
1818  if (lparam & GCS_RESULTSTR) {
1819  result_size = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR,
1820  ime_buffer, ime_buffer_size_bytes);
1821  size_t num_chars = result_size / sizeof(wchar_t);
1822  for (size_t i = 0; i < num_chars; ++i) {
1823  _input->keystroke(ime_buffer[i]);
1824  }
1825  }
1826 
1827  if (lparam & GCS_COMPSTR) {
1828  result_size = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, nullptr, 0);
1829  cursor_pos = result_size & 0xffff;
1830 
1831  result_size = ImmGetCompositionStringW(hIMC, GCS_DELTASTART, nullptr, 0);
1832  delta_start = result_size & 0xffff;
1833  result_size = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, ime_buffer, ime_buffer_size);
1834  size_t num_chars = result_size / sizeof(wchar_t);
1835 
1836  _input->candidate(wstring(ime_buffer, num_chars),
1837  std::min(cursor_pos, delta_start),
1838  std::max(cursor_pos, delta_start),
1839  cursor_pos);
1840  }
1841  ImmReleaseContext(hwnd, hIMC);
1842  }
1843  break;
1844 
1845  case WM_CHAR:
1846  // Ignore WM_CHAR messages if we have the IME open, since everything will
1847  // come in through WM_IME_COMPOSITION. (It's supposed to come in through
1848  // WM_CHAR, too, but there seems to be a bug in Win2000 in that it only
1849  // sends question mark characters through here.)
1850 
1851  // Actually, probably that "bug" was due to the fact that we were
1852  // previously using the ANSI versions of RegisterClass etc., in which case
1853  // the actual value passed to WM_CHAR seems to be poorly defined. Now we
1854  // are using RegisterClassW etc., which means WM_CHAR is absolutely
1855  // supposed to be utf-16.
1856  if (!_ime_open) {
1857  _input->keystroke(wparam);
1858  }
1859  break;
1860 
1861  case WM_SYSKEYDOWN:
1862  if (_lost_keypresses) {
1863  resend_lost_keypresses();
1864  }
1865  if (windisplay_cat.is_debug()) {
1866  windisplay_cat.debug()
1867  << "syskeydown: " << wparam << " (" << lookup_key(wparam) << ")\n";
1868  }
1869  {
1870  // Alt and F10 are sent as WM_SYSKEYDOWN instead of WM_KEYDOWN want to
1871  // use defwindproc on Alt syskey so std windows cmd Alt-F4 works, etc
1872  POINT point;
1873  GetCursorPos(&point);
1874  ScreenToClient(hwnd, &point);
1875  handle_keypress(lookup_key(wparam), point.x, point.y,
1876  get_message_time());
1877 
1878  if ((lparam & 0x40000000) == 0) {
1879  handle_raw_keypress(lookup_raw_key(lparam), get_message_time());
1880  }
1881 
1882 /*
1883  * wparam does not contain leftright information for SHIFT, CONTROL, or ALT,
1884  * so we have to track their status and do the right thing. We'll send the
1885  * leftright specific key event along with the general key event. Key
1886  * repeating is not being handled consistently for LALT and RALT, but from
1887  * comments below, it's only being handled the way it is for backspace, so
1888  * we'll leave it as is.
1889  */
1890  if (wparam == VK_MENU) {
1891  if ((GetKeyState(VK_LMENU) & 0x8000) != 0 && ! _lalt_down) {
1892  handle_keypress(KeyboardButton::lalt(), point.x, point.y,
1893  get_message_time());
1894  _lalt_down = true;
1895  }
1896  if ((GetKeyState(VK_RMENU) & 0x8000) != 0 && ! _ralt_down) {
1897  handle_keypress(KeyboardButton::ralt(), point.x, point.y,
1898  get_message_time());
1899  _ralt_down = true;
1900  }
1901  }
1902  if (wparam == VK_F10) {
1903  // bypass default windproc F10 behavior (it activates the main menu,
1904  // but we have none)
1905  return 0;
1906  }
1907  }
1908  break;
1909 
1910  case WM_SYSCOMMAND:
1911  if (wparam == SC_KEYMENU) {
1912  // if Alt is released (alone wo other keys), defwindproc will send this
1913  // command, which will 'activate' the title bar menu (we have none) and
1914  // give focus to it. we don't want this to happen, so kill this msg.
1915 
1916  // Note that the WM_SYSKEYUP message for Alt has already been sent (if
1917  // it is going to be), so ignoring this special message does no harm.
1918  return 0;
1919  }
1920  break;
1921 
1922  case WM_KEYDOWN:
1923  if (_lost_keypresses) {
1924  resend_lost_keypresses();
1925  }
1926  if (windisplay_cat.is_debug()) {
1927  windisplay_cat.debug()
1928  << "keydown: " << wparam << " (" << lookup_key(wparam) << ")\n";
1929  }
1930 
1931  // If this bit is not zero, this is just a keyrepeat echo; we ignore these
1932  // for handle_keypress (we respect keyrepeat only for handle_keystroke).
1933  if ((lparam & 0x40000000) == 0) {
1934  POINT point;
1935  GetCursorPos(&point);
1936  ScreenToClient(hwnd, &point);
1937  handle_keypress(lookup_key(wparam), point.x, point.y,
1938  get_message_time());
1939  handle_raw_keypress(lookup_raw_key(lparam), get_message_time());
1940 
1941  // wparam does not contain leftright information for SHIFT, CONTROL, or
1942  // ALT, so we have to track their status and do the right thing. We'll
1943  // send the leftright specific key event along with the general key
1944  // event.
1945  if (wparam == VK_SHIFT) {
1946  if ((GetKeyState(VK_LSHIFT) & 0x8000) != 0 && ! _lshift_down) {
1947  handle_keypress(KeyboardButton::lshift(), point.x, point.y,
1948  get_message_time());
1949  _lshift_down = true;
1950  }
1951  if ((GetKeyState(VK_RSHIFT) & 0x8000) != 0 && ! _rshift_down) {
1952  handle_keypress(KeyboardButton::rshift(), point.x, point.y,
1953  get_message_time());
1954  _rshift_down = true;
1955  }
1956  } else if(wparam == VK_CONTROL) {
1957  if ((GetKeyState(VK_LCONTROL) & 0x8000) != 0 && ! _lcontrol_down) {
1958  handle_keypress(KeyboardButton::lcontrol(), point.x, point.y,
1959  get_message_time());
1960  _lcontrol_down = true;
1961  }
1962  if ((GetKeyState(VK_RCONTROL) & 0x8000) != 0 && ! _rcontrol_down) {
1963  handle_keypress(KeyboardButton::rcontrol(), point.x, point.y,
1964  get_message_time());
1965  _rcontrol_down = true;
1966  }
1967  }
1968 
1969  // Handle Cntrl-V paste from clipboard. Is there a better way to detect
1970  // this hotkey?
1971  if ((wparam=='V') && (GetKeyState(VK_CONTROL) < 0) &&
1972  !_input_devices.empty() && paste_emit_keystrokes) {
1973  HGLOBAL hglb;
1974  char *lptstr;
1975 
1976  if (IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(nullptr)) {
1977  // Maybe we should support CF_UNICODETEXT if it is available too?
1978  hglb = GetClipboardData(CF_TEXT);
1979  if (hglb!=nullptr) {
1980  lptstr = (char *) GlobalLock(hglb);
1981  if (lptstr != nullptr) {
1982  char *pChar;
1983  for (pChar = lptstr; *pChar; pChar++) {
1984  _input->keystroke((uchar)*pChar);
1985  }
1986  GlobalUnlock(hglb);
1987  }
1988  }
1989  CloseClipboard();
1990  }
1991  }
1992  } else {
1993  // Actually, for now we'll respect the repeat anyway, just so we support
1994  // backspace properly. Rethink later.
1995  POINT point;
1996  GetCursorPos(&point);
1997  ScreenToClient(hwnd, &point);
1998  handle_keypress(lookup_key(wparam), point.x, point.y,
1999  get_message_time());
2000 
2001 /*
2002  * wparam does not contain leftright information for SHIFT, CONTROL, or ALT,
2003  * so we have to track their status and do the right thing. We'll send the
2004  * leftright specific key event along with the general key event. If the user
2005  * presses LSHIFT and then RSHIFT, the RSHIFT event will come in with the
2006  * keyrepeat flag on (i.e. it will end up in this block). The logic below
2007  * should detect this correctly and only send the RSHIFT event. Note that the
2008  * CONTROL event will be sent twice, once for each keypress. Since keyrepeats
2009  * are currently being sent simply as additional keypress events, that should
2010  * be okay for now.
2011  */
2012  if (wparam == VK_SHIFT) {
2013  if (((GetKeyState(VK_LSHIFT) & 0x8000) != 0) && ! _lshift_down ) {
2014  handle_keypress(KeyboardButton::lshift(), point.x, point.y,
2015  get_message_time());
2016  _lshift_down = true;
2017  } else if (((GetKeyState(VK_RSHIFT) & 0x8000) != 0) && ! _rshift_down ) {
2018  handle_keypress(KeyboardButton::rshift(), point.x, point.y,
2019  get_message_time());
2020  _rshift_down = true;
2021  } else {
2022  if ((GetKeyState(VK_LSHIFT) & 0x8000) != 0) {
2023  handle_keypress(KeyboardButton::lshift(), point.x, point.y,
2024  get_message_time());
2025  }
2026  if ((GetKeyState(VK_RSHIFT) & 0x8000) != 0) {
2027  handle_keypress(KeyboardButton::rshift(), point.x, point.y,
2028  get_message_time());
2029  }
2030  }
2031  } else if(wparam == VK_CONTROL) {
2032  if (((GetKeyState(VK_LCONTROL) & 0x8000) != 0) && ! _lcontrol_down ) {
2033  handle_keypress(KeyboardButton::lcontrol(), point.x, point.y,
2034  get_message_time());
2035  _lcontrol_down = true;
2036  } else if (((GetKeyState(VK_RCONTROL) & 0x8000) != 0) && ! _rcontrol_down ) {
2037  handle_keypress(KeyboardButton::rcontrol(), point.x, point.y,
2038  get_message_time());
2039  _rcontrol_down = true;
2040  } else {
2041  if ((GetKeyState(VK_LCONTROL) & 0x8000) != 0) {
2042  handle_keypress(KeyboardButton::lcontrol(), point.x, point.y,
2043  get_message_time());
2044  }
2045  if ((GetKeyState(VK_RCONTROL) & 0x8000) != 0) {
2046  handle_keypress(KeyboardButton::rcontrol(), point.x, point.y,
2047  get_message_time());
2048  }
2049  }
2050  }
2051  }
2052  break;
2053 
2054  case WM_SYSKEYUP:
2055  case WM_KEYUP:
2056  if (_lost_keypresses) {
2057  resend_lost_keypresses();
2058  }
2059  if (windisplay_cat.is_debug()) {
2060  windisplay_cat.debug()
2061  << "keyup: " << wparam << " (" << lookup_key(wparam) << ")\n";
2062  }
2063  handle_keyrelease(lookup_key(wparam), get_message_time());
2064  handle_raw_keyrelease(lookup_raw_key(lparam), get_message_time());
2065 
2066  // wparam does not contain leftright information for SHIFT, CONTROL, or
2067  // ALT, so we have to track their status and do the right thing. We'll
2068  // send the leftright specific key event along with the general key event.
2069  if (wparam == VK_SHIFT) {
2070  if ((GetKeyState(VK_LSHIFT) & 0x8000) == 0 && _lshift_down) {
2071  handle_keyrelease(KeyboardButton::lshift(), get_message_time());
2072  _lshift_down = false;
2073  }
2074  if ((GetKeyState(VK_RSHIFT) & 0x8000) == 0 && _rshift_down) {
2075  handle_keyrelease(KeyboardButton::rshift(), get_message_time());
2076  _rshift_down = false;
2077  }
2078  } else if(wparam == VK_CONTROL) {
2079  if ((GetKeyState(VK_LCONTROL) & 0x8000) == 0 && _lcontrol_down) {
2080  handle_keyrelease(KeyboardButton::lcontrol(), get_message_time());
2081  _lcontrol_down = false;
2082  }
2083  if ((GetKeyState(VK_RCONTROL) & 0x8000) == 0 && _rcontrol_down) {
2084  handle_keyrelease(KeyboardButton::rcontrol(), get_message_time());
2085  _rcontrol_down = false;
2086  }
2087  } else if(wparam == VK_MENU) {
2088  if ((GetKeyState(VK_LMENU) & 0x8000) == 0 && _lalt_down) {
2089  handle_keyrelease(KeyboardButton::lalt(), get_message_time());
2090  _lalt_down = false;
2091  }
2092  if ((GetKeyState(VK_RMENU) & 0x8000) == 0 && _ralt_down) {
2093  handle_keyrelease(KeyboardButton::ralt(), get_message_time());
2094  _ralt_down = false;
2095  }
2096  }
2097  break;
2098 
2099  case WM_KILLFOCUS:
2100  if (windisplay_cat.is_debug()) {
2101  windisplay_cat.debug()
2102  << "killfocus\n";
2103  }
2104 
2105  _input->focus_lost(get_message_time());
2106  properties.set_foreground(false);
2107  system_changed_properties(properties);
2108  break;
2109 
2110  case WM_SETFOCUS:
2111  // You would think that this would be a good time to call
2112  // resend_lost_keypresses(), but it turns out that we get WM_SETFOCUS
2113  // slightly before Windows starts resending key updown events to us.
2114 
2115 /*
2116  * In particular, if the user restored focus using alt-tab, then at this point
2117  * the keyboard state will indicate that both the alt and tab keys are held
2118  * down. However, there is a small window of opportunity for the user to
2119  * release these keys before Windows starts telling us about keyup events.
2120  * Thus, if we record the fact that alt and tab are being held down now, we
2121  * may miss the keyup events for them, and they can get "stuck" down.
2122  */
2123 
2124  // So we have to defer calling resend_lost_keypresses() until we know
2125  // Windows is ready to send us key updown events. I don't know when we
2126  // can guarantee that, except when we actually do start to receive key
2127  // updown events, so that call is made there.
2128 
2129  if (windisplay_cat.is_debug()) {
2130  windisplay_cat.debug()
2131  << "setfocus\n";
2132  }
2133 
2134  if (_lost_keypresses) {
2135  resend_lost_keypresses();
2136  }
2137 
2138  properties.set_foreground(true);
2139  system_changed_properties(properties);
2140  break;
2141 
2142  case PM_ACTIVE:
2143  if (windisplay_cat.is_debug()) {
2144  windisplay_cat.debug()
2145  << "PM_ACTIVE\n";
2146  }
2147  properties.set_foreground(true);
2148  system_changed_properties(properties);
2149  break;
2150 
2151  case PM_INACTIVE:
2152  if (windisplay_cat.is_debug()) {
2153  windisplay_cat.debug()
2154  << "PM_INACTIVE\n";
2155  }
2156  properties.set_foreground(false);
2157  system_changed_properties(properties);
2158  break;
2159 
2160  case WM_DPICHANGED:
2161  // The window moved to a monitor of different DPI, or someone changed the
2162  // DPI setting in the configuration panel.
2163  if (windisplay_cat.is_debug()) {
2164  windisplay_cat.debug() << "DPI changed to " << LOWORD(wparam);
2165 
2166  if (LOWORD(wparam) != HIWORD(wparam)) {
2167  windisplay_cat.debug(false) << "x" << HIWORD(wparam) << "\n";
2168  } else {
2169  windisplay_cat.debug(false) << "\n";
2170  }
2171  }
2172  // Resize the window if requested to match the new DPI. Obviously, don't
2173  // do this if a fixed size was requested.
2174  if (!_properties.get_fixed_size() && dpi_window_resize) {
2175  RECT &rect = *(LPRECT)lparam;
2176  SetWindowPos(_hWnd, HWND_TOP, rect.left, rect.top,
2177  rect.right - rect.left, rect.bottom - rect.top,
2178  SWP_NOZORDER | SWP_NOACTIVATE);
2179  }
2180  break;
2181 
2182  case WM_TOUCH:
2183  _num_touches = LOWORD(wparam);
2184  if (_num_touches > MAX_TOUCHES) {
2185  _num_touches = MAX_TOUCHES;
2186  }
2187  if (pGetTouchInputInfo != 0) {
2188  pGetTouchInputInfo((HTOUCHINPUT)lparam, _num_touches, _touches, sizeof(TOUCHINPUT));
2189  pCloseTouchInputHandle((HTOUCHINPUT)lparam);
2190  }
2191  break;
2192  }
2193 
2194  // do custom messages processing if any has been set
2195  for ( WinProcClasses::iterator it=_window_proc_classes.begin() ; it != _window_proc_classes.end(); it++ ){
2196  (*it)->wnd_proc(this, hwnd, msg, wparam, lparam);
2197  }
2198 
2199  return DefWindowProcW(hwnd, msg, wparam, lparam);
2200 }
2201 
2202 
2203 /**
2204  * This is attached to the window class for all WinGraphicsWindow windows; it
2205  * is called to handle window events.
2206  */
2208 static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
2209  // Look up the window in our global map.
2210  WindowHandles::const_iterator wi;
2211  wi = _window_handles.find(hwnd);
2212  if (wi != _window_handles.end()) {
2213  // We found the window.
2214  return (*wi).second->window_proc(hwnd, msg, wparam, lparam);
2215  }
2216 
2217  // The window wasn't in the map; we must be creating it right now.
2218  if (_creating_window != nullptr) {
2219  return _creating_window->window_proc(hwnd, msg, wparam, lparam);
2220  }
2221 
2222  // Oops, we weren't creating a window! Don't know how to handle the
2223  // message, so just pass it on to Windows to deal with it.
2224  return DefWindowProcW(hwnd, msg, wparam, lparam);
2225 }
2226 
2227 /**
2228  * Handles one event from the message queue.
2229  */
2230 void WinGraphicsWindow::
2231 process_1_event() {
2232  MSG msg;
2233 
2234  if (!GetMessage(&msg, nullptr, 0, 0)) {
2235  // WM_QUIT received. We need a cleaner way to deal with this.
2236  // DestroyAllWindows(false);
2237  exit(msg.wParam); // this will invoke AtExitFn
2238  }
2239 
2240  // Translate virtual key messages
2241  TranslateMessage(&msg);
2242  // Call window_proc
2243  DispatchMessage(&msg);
2244 }
2245 
2246 /**
2247  * Called when the keyboard focus has been restored to the window after it has
2248  * been lost for a time, this rechecks the keyboard state and generates key
2249  * up/down messages for keys that have changed state in the meantime.
2250  */
2251 void WinGraphicsWindow::
2252 resend_lost_keypresses() {
2253  nassertv(_lost_keypresses);
2254  // This is now a no-op. Not sure we really want to generate new "down" or
2255  // "resume" events for keys that were held while the window focus is
2256  // restored.
2257 
2258  _lost_keypresses = false;
2259 }
2260 
2261 /**
2262  * Changes _cursor_window from its current value to the indicated value. This
2263  * also changes the cursor properties appropriately.
2264  */
2265 void WinGraphicsWindow::
2266 update_cursor_window(WinGraphicsWindow *to_window) {
2267  bool hide_cursor = false;
2268  if (to_window == nullptr) {
2269  // We are leaving a graphics window; we should restore the Win2000
2270  // effects.
2271  if (_got_saved_params) {
2272  SystemParametersInfo(SPI_SETMOUSETRAILS, _saved_mouse_trails,
2273  0, 0);
2274  SystemParametersInfo(SPI_SETCURSORSHADOW, 0,
2275  _saved_cursor_shadow ? (PVOID)1 : nullptr, 0);
2276  SystemParametersInfo(SPI_SETMOUSEVANISH, 0,
2277  _saved_mouse_vanish ? (PVOID)1 : nullptr, 0);
2278  _got_saved_params = false;
2279  }
2280 
2281  } else {
2282  const WindowProperties &to_props = to_window->get_properties();
2283  hide_cursor = to_props.get_cursor_hidden();
2284 
2285  // We are entering a graphics window; we should save and disable the
2286  // Win2000 effects. These don't work at all well over a 3-D window.
2287 
2288  // These parameters are only defined for Win2000XP, but they should just
2289  // cause a silent error on earlier OS's, which is OK.
2290  if (!_got_saved_params) {
2291  SystemParametersInfo(SPI_GETMOUSETRAILS, 0,
2292  &_saved_mouse_trails, 0);
2293  SystemParametersInfo(SPI_GETCURSORSHADOW, 0,
2294  &_saved_cursor_shadow, 0);
2295  SystemParametersInfo(SPI_GETMOUSEVANISH, 0,
2296  &_saved_mouse_vanish, 0);
2297  _got_saved_params = true;
2298 
2299  SystemParametersInfo(SPI_SETMOUSETRAILS, 0, (PVOID)0, 0);
2300  SystemParametersInfo(SPI_SETCURSORSHADOW, 0, (PVOID)false, 0);
2301  SystemParametersInfo(SPI_SETMOUSEVANISH, 0, (PVOID)false, 0);
2302  }
2303 
2304  SetCursor(to_window->_cursor);
2305  }
2306 
2307  hide_or_show_cursor(hide_cursor);
2308 
2309  _cursor_window = to_window;
2310 }
2311 
2312 /**
2313  * Hides or shows the mouse cursor according to the indicated parameter. This
2314  * is normally called when the mouse wanders into or out of a window with the
2315  * cursor_hidden properties.
2316  */
2317 void WinGraphicsWindow::
2318 hide_or_show_cursor(bool hide_cursor) {
2319  if (hide_cursor) {
2320  if (!_cursor_hidden) {
2321  ShowCursor(false);
2322  _cursor_hidden = true;
2323  }
2324  } else {
2325  if (_cursor_hidden) {
2326  ShowCursor(true);
2327  _cursor_hidden = false;
2328  }
2329  }
2330 }
2331 
2332 // don't pick any video modes < MIN_REFRESH_RATE Hz
2333 #define MIN_REFRESH_RATE 60
2334 // EnumDisplaySettings may indicate 0 or 1 for refresh rate, which means use
2335 // driver default rate (assume its >min_refresh_rate)
2336 #define ACCEPTABLE_REFRESH_RATE(RATE) ((RATE >= MIN_REFRESH_RATE) || (RATE==0) || (RATE==1))
2337 
2338 /**
2339  * Looks for a fullscreen mode that meets the specified size and bitdepth
2340  * requirements. Returns true if a suitable mode is found, false otherwise.
2341  */
2342 bool WinGraphicsWindow::
2343 find_acceptable_display_mode(DWORD dwWidth, DWORD dwHeight, DWORD bpp,
2344  DEVMODE &dm) {
2345 
2346  // Get the current mode. We'll try to match the refresh rate.
2347  DEVMODE cur_dm;
2348  ZeroMemory(&cur_dm, sizeof(cur_dm));
2349  cur_dm.dmSize = sizeof(cur_dm);
2350  EnumDisplaySettings(nullptr, ENUM_CURRENT_SETTINGS, &cur_dm);
2351 
2352  int modenum = 0;
2353  int saved_modenum = -1;
2354 
2355  while (1) {
2356  ZeroMemory(&dm, sizeof(dm));
2357  dm.dmSize = sizeof(dm);
2358 
2359  if (!EnumDisplaySettings(nullptr, modenum, &dm)) {
2360  break;
2361  }
2362 
2363  if ((dm.dmPelsWidth == dwWidth) && (dm.dmPelsHeight == dwHeight) &&
2364  (dm.dmBitsPerPel == bpp)) {
2365  // If this also matches in refresh rate, we're done here. Otherwise,
2366  // save this as a second choice for later.
2367  if (dm.dmDisplayFrequency == cur_dm.dmDisplayFrequency) {
2368  return true;
2369  } else if (saved_modenum == -1) {
2370  saved_modenum = modenum;
2371  }
2372  }
2373  modenum++;
2374  }
2375 
2376  // Failed to find an exact match, but we do have a match that didn't match
2377  // the refresh rate.
2378  if (saved_modenum != -1) {
2379  ZeroMemory(&dm, sizeof(dm));
2380  dm.dmSize = sizeof(dm);
2381 
2382  if (EnumDisplaySettings(nullptr, saved_modenum, &dm)) {
2383  return true;
2384  }
2385  }
2386 
2387  return false;
2388 }
2389 
2390 /**
2391  * Pops up a dialog box with the indicated Windows error message ID (or the
2392  * last error message generated) for meaningful display to the user.
2393  */
2394 void WinGraphicsWindow::
2395 show_error_message(DWORD message_id) {
2396  LPTSTR message_buffer;
2397 
2398  if (message_id == 0) {
2399  message_id = GetLastError();
2400  }
2401 
2402  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
2403  nullptr, message_id,
2404  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //The user default language
2405  (LPTSTR)&message_buffer, // the weird ptrptr->ptr cast is intentional, see FORMAT_MESSAGE_ALLOCATE_BUFFER
2406  1024, nullptr);
2407  MessageBox(GetDesktopWindow(), message_buffer, _T(errorbox_title), MB_OK);
2408  windisplay_cat.fatal() << "System error msg: " << message_buffer << endl;
2409  LocalFree(message_buffer);
2410 }
2411 
2412 /**
2413  *
2414  */
2415 void WinGraphicsWindow::
2416 handle_keypress(ButtonHandle key, int x, int y, double time) {
2417  _input->set_pointer_in_window(x, y);
2418  if (key != ButtonHandle::none()) {
2419  _input->button_down(key, time);
2420  }
2421 }
2422 
2423 /**
2424  * Indicates we detected a key was already down when the focus is restored to
2425  * the window. Mainly useful for tracking the state of modifier keys.
2426  */
2427 void WinGraphicsWindow::
2428 handle_keyresume(ButtonHandle key, double time) {
2429  if (key != ButtonHandle::none()) {
2430  _input->button_resume_down(key, time);
2431  }
2432 }
2433 
2434 /**
2435  *
2436  */
2437 void WinGraphicsWindow::
2438 handle_keyrelease(ButtonHandle key, double time) {
2439  if (key != ButtonHandle::none()) {
2440  _input->button_up(key, time);
2441  }
2442 }
2443 
2444 /**
2445  *
2446  */
2447 void WinGraphicsWindow::
2448 handle_raw_keypress(ButtonHandle key, double time) {
2449  if (key != ButtonHandle::none()) {
2450  _input->raw_button_down(key, time);
2451  }
2452 }
2453 
2454 /**
2455  *
2456  */
2457 void WinGraphicsWindow::
2458 handle_raw_keyrelease(ButtonHandle key, double time) {
2459  if (key != ButtonHandle::none()) {
2460  _input->raw_button_up(key, time);
2461  }
2462 }
2463 
2464 /**
2465  * Translates the keycode reported by Windows to an appropriate Panda
2466  * ButtonHandle.
2467  */
2468 ButtonHandle WinGraphicsWindow::
2469 lookup_key(WPARAM wparam) const {
2470  // First, check for a few buttons that we filter out when the IME window is
2471  // open.
2472  if (!_ime_active) {
2473  switch(wparam) {
2474  case VK_BACK: return KeyboardButton::backspace();
2475  case VK_DELETE: return KeyboardButton::del();
2476  case VK_ESCAPE: return KeyboardButton::escape();
2477  case VK_SPACE: return KeyboardButton::space();
2478  case VK_UP: return KeyboardButton::up();
2479  case VK_DOWN: return KeyboardButton::down();
2480  case VK_LEFT: return KeyboardButton::left();
2481  case VK_RIGHT: return KeyboardButton::right();
2482  }
2483  }
2484 
2485  // Now check for the rest of the buttons, including the ones that we allow
2486  // through even when the IME window is open.
2487  switch(wparam) {
2488  case VK_TAB: return KeyboardButton::tab();
2489  case VK_PRIOR: return KeyboardButton::page_up();
2490  case VK_NEXT: return KeyboardButton::page_down();
2491  case VK_HOME: return KeyboardButton::home();
2492  case VK_END: return KeyboardButton::end();
2493  case VK_F1: return KeyboardButton::f1();
2494  case VK_F2: return KeyboardButton::f2();
2495  case VK_F3: return KeyboardButton::f3();
2496  case VK_F4: return KeyboardButton::f4();
2497  case VK_F5: return KeyboardButton::f5();
2498  case VK_F6: return KeyboardButton::f6();
2499  case VK_F7: return KeyboardButton::f7();
2500  case VK_F8: return KeyboardButton::f8();
2501  case VK_F9: return KeyboardButton::f9();
2502  case VK_F10: return KeyboardButton::f10();
2503  case VK_F11: return KeyboardButton::f11();
2504  case VK_F12: return KeyboardButton::f12();
2505  case VK_INSERT: return KeyboardButton::insert();
2506  case VK_CAPITAL: return KeyboardButton::caps_lock();
2507  case VK_NUMLOCK: return KeyboardButton::num_lock();
2508  case VK_SCROLL: return KeyboardButton::scroll_lock();
2509  case VK_PAUSE: return KeyboardButton::pause();
2510  case VK_SNAPSHOT: return KeyboardButton::print_screen();
2511 
2512  case VK_SHIFT: return KeyboardButton::shift();
2513  case VK_LSHIFT: return KeyboardButton::lshift();
2514  case VK_RSHIFT: return KeyboardButton::rshift();
2515 
2516  case VK_CONTROL: return KeyboardButton::control();
2517  case VK_LCONTROL: return KeyboardButton::lcontrol();
2518  case VK_RCONTROL: return KeyboardButton::rcontrol();
2519 
2520  case VK_MENU: return KeyboardButton::alt();
2521  case VK_LMENU: return KeyboardButton::lalt();
2522  case VK_RMENU: return KeyboardButton::ralt();
2523 
2524  default:
2525  int key = MapVirtualKey(wparam, 2);
2526  if (isascii(key) && key != 0) {
2527  // We used to try to remap lowercase to uppercase keys here based on the
2528  // state of the shift andor caps lock keys. But that's a mistake, and
2529  // doesn't allow for international or user-defined keyboards; let
2530  // Windows do that mapping.
2531 
2532  // Nowadays, we make a distinction between a "button" and a "keystroke".
2533  // A button corresponds to a physical button on the keyboard and has a
2534  // down and up event associated. A keystroke may or may not correspond
2535  // to a physical button, but will be some Unicode character and will not
2536  // have a corresponding up event.
2537  return KeyboardButton::ascii_key(tolower(key));
2538  }
2539  break;
2540  }
2541  return ButtonHandle::none();
2542 }
2543 
2544 /**
2545  * Translates the scancode reported by Windows to an appropriate Panda
2546  * ButtonHandle.
2547  */
2548 ButtonHandle WinGraphicsWindow::
2549 lookup_raw_key(LPARAM lparam) const {
2550  unsigned char vsc = (lparam & 0xff0000) >> 16;
2551 
2552  if (lparam & 0x1000000) {
2553  // Extended keys
2554  switch (vsc) {
2555  case 28: return KeyboardButton::enter();
2556  case 29: return KeyboardButton::rcontrol();
2557  case 53: return KeyboardButton::ascii_key('/');
2558  case 55: return KeyboardButton::print_screen();
2559  case 56: return KeyboardButton::ralt();
2560  case 69: return KeyboardButton::num_lock();
2561  case 71: return KeyboardButton::home();
2562  case 72: return KeyboardButton::up();
2563  case 73: return KeyboardButton::page_up();
2564  case 75: return KeyboardButton::left();
2565  case 77: return KeyboardButton::right();
2566  case 79: return KeyboardButton::end();
2567  case 80: return KeyboardButton::down();
2568  case 81: return KeyboardButton::page_down();
2569  case 82: return KeyboardButton::insert();
2570  case 83: return KeyboardButton::del();
2571  case 91: return KeyboardButton::lmeta();
2572  case 92: return KeyboardButton::rmeta();
2573  case 93: return KeyboardButton::menu();
2574  }
2575  }
2576 
2577  if (vsc <= 83) {
2578  static ButtonHandle raw_map[] = {
2579  ButtonHandle::none(),
2580  KeyboardButton::escape(),
2593  KeyboardButton::backspace(),
2594  KeyboardButton::tab(),
2607  KeyboardButton::enter(),
2608  KeyboardButton::lcontrol(),
2621  KeyboardButton::lshift(),
2633  KeyboardButton::rshift(),
2635  KeyboardButton::lalt(),
2636  KeyboardButton::space(),
2637  KeyboardButton::caps_lock(),
2638  KeyboardButton::f1(),
2639  KeyboardButton::f2(),
2640  KeyboardButton::f3(),
2641  KeyboardButton::f4(),
2642  KeyboardButton::f5(),
2643  KeyboardButton::f6(),
2644  KeyboardButton::f7(),
2645  KeyboardButton::f8(),
2646  KeyboardButton::f9(),
2647  KeyboardButton::f10(),
2648  KeyboardButton::pause(),
2649  KeyboardButton::scroll_lock(),
2663  };
2664  return raw_map[vsc];
2665  }
2666 
2667  // A few additional keys don't fit well in the above table.
2668  switch (vsc) {
2669  case 87: return KeyboardButton::f11();
2670  case 88: return KeyboardButton::f12();
2671  default: return ButtonHandle::none();
2672  }
2673 }
2674 
2675 /**
2676  * Returns a ButtonMap containing the association between raw buttons and
2677  * virtual buttons.
2678  *
2679  * Note that on Windows, the pause button and numpad keys are not mapped
2680  * reliably.
2681  */
2682 ButtonMap *WinGraphicsWindow::
2683 get_keyboard_map() const {
2684  ButtonMap *map = new ButtonMap;
2685 
2686  wchar_t text[256];
2687  UINT vsc = 0;
2688  unsigned short ex_vsc[] = {0x57, 0x58,
2689  0x011c, 0x011d, 0x0135, 0x0137, 0x0138, 0x0145, 0x0147, 0x0148, 0x0149, 0x014b, 0x014d, 0x014f, 0x0150, 0x0151, 0x0152, 0x0153, 0x015b, 0x015c, 0x015d};
2690 
2691  for (int k = 1; k < 84 + 17; ++k) {
2692  if (k >= 84) {
2693  vsc = ex_vsc[k - 84];
2694  } else {
2695  vsc = k;
2696  }
2697 
2698  UINT lparam = vsc << 16;
2699  ButtonHandle raw_button = lookup_raw_key(lparam);
2700  if (raw_button == ButtonHandle::none()) {
2701  continue;
2702  }
2703 
2704  ButtonHandle button;
2705  if (vsc == 0x45) {
2706  button = KeyboardButton::pause();
2707 
2708  } else if (vsc >= 0x47 && vsc <= 0x53) {
2709  // The numpad keys are not mapped correctly, see KB72583
2710  button = raw_button;
2711 
2712  } else {
2713  if (vsc == 0x145) {
2714  // Don't ask why - I have no idea.
2715  vsc = 0x45;
2716  }
2717  if (vsc & 0x0100) {
2718  // MapVirtualKey recognizes extended codes differently.
2719  vsc ^= 0xe100;
2720  }
2721 
2722  UINT vk = MapVirtualKeyA(vsc, MAPVK_VSC_TO_VK_EX);
2723  button = lookup_key(vk);
2724  //if (button == ButtonHandle::none()) {
2725  // continue;
2726  //}
2727  }
2728 
2729  int len = GetKeyNameTextW(lparam, text, 256);
2730  TextEncoder enc;
2731  enc.set_wtext(wstring(text, len));
2732  map->map_button(raw_button, button, enc.get_text());
2733  }
2734 
2735  return map;
2736 }
2737 
2738 /**
2739  *
2740  */
2741 void WinGraphicsWindow::
2742 handle_raw_input(HRAWINPUT hraw) {
2743  LPBYTE lpb;
2744  UINT dwSize;
2745 
2746  if (hraw == 0) {
2747  return;
2748  }
2749  if (GetRawInputData(hraw, RID_INPUT, nullptr, &dwSize, sizeof(RAWINPUTHEADER)) == -1) {
2750  return;
2751  }
2752 
2753  lpb = (LPBYTE)alloca(sizeof(LPBYTE) * dwSize);
2754  if (lpb == nullptr) {
2755  return;
2756  }
2757 
2758  if (GetRawInputData(hraw, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) {
2759  return;
2760  }
2761 
2762  RAWINPUT *raw = (RAWINPUT *)lpb;
2763  if (raw->header.hDevice == 0) {
2764  return;
2765  }
2766 
2767  for (size_t i = 1; i < _input_devices.size(); ++i) {
2768  if (_input_device_handle[i] == raw->header.hDevice) {
2769  PT(GraphicsWindowInputDevice) input =
2770  DCAST(GraphicsWindowInputDevice, _input_devices[i]);
2771 
2772  int adjx = raw->data.mouse.lLastX;
2773  int adjy = raw->data.mouse.lLastY;
2774 
2775  if (raw->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) {
2776  input->set_pointer_in_window(adjx, adjy);
2777  } else {
2778  input->pointer_moved(adjx, adjy);
2779  }
2780 
2781  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) {
2782  input->button_down(MouseButton::button(0), get_message_time());
2783  }
2784  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP) {
2785  input->button_up(MouseButton::button(0), get_message_time());
2786  }
2787  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) {
2788  input->button_down(MouseButton::button(2), get_message_time());
2789  }
2790  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP) {
2791  input->button_up(MouseButton::button(2), get_message_time());
2792  }
2793  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) {
2794  input->button_down(MouseButton::button(1), get_message_time());
2795  }
2796  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP) {
2797  input->button_up(MouseButton::button(1), get_message_time());
2798  }
2799  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) {
2800  input->button_down(MouseButton::button(3), get_message_time());
2801  }
2802  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP) {
2803  input->button_up(MouseButton::button(3), get_message_time());
2804  }
2805  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) {
2806  input->button_down(MouseButton::button(4), get_message_time());
2807  }
2808  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP) {
2809  input->button_up(MouseButton::button(4), get_message_time());
2810  }
2811  }
2812  }
2813 }
2814 
2815 /**
2816  *
2817  */
2818 bool WinGraphicsWindow::
2819 handle_mouse_motion(int x, int y) {
2820  _input->set_pointer_in_window(x, y);
2821  return false;
2822 }
2823 
2824 /**
2825  *
2826  */
2827 void WinGraphicsWindow::
2828 handle_mouse_exit() {
2829  // note: 'mouse_motion' is considered the 'entry' event
2830  _input->set_pointer_out_of_window();
2831 }
2832 
2833 /**
2834  * Loads and returns an HICON corresponding to the indicated filename. If the
2835  * file cannot be loaded, returns 0.
2836  */
2837 HICON WinGraphicsWindow::
2838 get_icon(const Filename &filename) {
2839  // First, look for the unresolved filename in our index.
2840  IconFilenames::iterator fi = _icon_filenames.find(filename);
2841  if (fi != _icon_filenames.end()) {
2842  return (HICON)((*fi).second);
2843  }
2844 
2845  // If it wasn't found, resolve the filename and search for that.
2846 
2847  // Since we have to use a Windows call to load the image from a filename, we
2848  // can't load a virtual file and we can't use the virtual file system.
2849  Filename resolved = filename;
2850  if (!resolved.resolve_filename(get_model_path())) {
2851  // The filename doesn't exist along the search path.
2852  if (resolved.is_fully_qualified() && resolved.exists()) {
2853  // But it does exist locally, so accept it.
2854 
2855  } else {
2856  windisplay_cat.warning()
2857  << "Could not find icon filename " << filename << "\n";
2858  return 0;
2859  }
2860  }
2861  fi = _icon_filenames.find(resolved);
2862  if (fi != _icon_filenames.end()) {
2863  _icon_filenames[filename] = (*fi).second;
2864  return (HICON)((*fi).second);
2865  }
2866 
2867  Filename os = resolved.to_os_specific();
2868 
2869  HANDLE h = LoadImage(nullptr, os.c_str(),
2870  IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
2871  if (h == 0) {
2872  windisplay_cat.warning()
2873  << "windows icon filename '" << os << "' could not be loaded!!\n";
2874  }
2875 
2876  _icon_filenames[filename] = h;
2877  _icon_filenames[resolved] = h;
2878  return (HICON)h;
2879 }
2880 
2881 /**
2882  * Loads and returns an HCURSOR corresponding to the indicated filename. If
2883  * the file cannot be loaded, returns 0.
2884  */
2885 HCURSOR WinGraphicsWindow::
2886 get_cursor(const Filename &filename) {
2887  // The empty filename means to disable a custom cursor.
2888  if (filename.empty()) {
2889  return 0;
2890  }
2891 
2892  // First, look for the unresolved filename in our index.
2893  IconFilenames::iterator fi = _cursor_filenames.find(filename);
2894  if (fi != _cursor_filenames.end()) {
2895  return (HCURSOR)((*fi).second);
2896  }
2897 
2898  // If it wasn't found, resolve the filename and search for that.
2899 
2900  // Since we have to use a Windows call to load the image from a filename, we
2901  // can't load a virtual file and we can't use the virtual file system.
2902  Filename resolved = filename;
2903  if (!resolved.resolve_filename(get_model_path())) {
2904  // The filename doesn't exist.
2905  windisplay_cat.warning()
2906  << "Could not find cursor filename " << filename << "\n";
2907  return 0;
2908  }
2909  fi = _cursor_filenames.find(resolved);
2910  if (fi != _cursor_filenames.end()) {
2911  _cursor_filenames[filename] = (*fi).second;
2912  return (HCURSOR)((*fi).second);
2913  }
2914 
2915  Filename os = resolved.to_os_specific();
2916 
2917  HANDLE h = LoadImage(nullptr, os.c_str(),
2918  IMAGE_CURSOR, 0, 0, LR_LOADFROMFILE);
2919  if (h == 0) {
2920  windisplay_cat.warning()
2921  << "windows cursor filename '" << os << "' could not be loaded!!\n";
2922  show_error_message();
2923  }
2924 
2925  _cursor_filenames[filename] = h;
2926  _cursor_filenames[resolved] = h;
2927  return (HCURSOR)h;
2928 }
2929 
2930 static HCURSOR get_cursor(const Filename &filename);
2931 
2932 /**
2933  * Registers a Window class appropriate for the indicated properties. This
2934  * class may be shared by multiple windows.
2935  */
2936 const WinGraphicsWindow::WindowClass &WinGraphicsWindow::
2937 register_window_class(const WindowProperties &props) {
2938  WindowClass wcreg(props);
2939  std::wostringstream wclass_name;
2940  wclass_name << L"WinGraphicsWindow" << _window_class_index;
2941  wcreg._name = wclass_name.str();
2942 
2943  std::pair<WindowClasses::iterator, bool> found = _window_classes.insert(wcreg);
2944  const WindowClass &wclass = (*found.first);
2945 
2946  if (!found.second) {
2947  // We have already created a window class.
2948  return wclass;
2949  }
2950 
2951  // We have not yet created this window class.
2952  _window_class_index++;
2953 
2954  WNDCLASSW wc;
2955 
2956  HINSTANCE instance = GetModuleHandle(nullptr);
2957 
2958  // Clear before filling in window structure!
2959  ZeroMemory(&wc, sizeof(wc));
2960  wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
2961  wc.lpfnWndProc = (WNDPROC)static_window_proc;
2962  wc.hInstance = instance;
2963 
2964  wc.hIcon = wclass._icon;
2965 
2966  wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
2967  wc.lpszMenuName = nullptr;
2968  wc.lpszClassName = wclass._name.c_str();
2969 
2970  if (!RegisterClassW(&wc)) {
2971  windisplay_cat.error()
2972  << "could not register window class " << wclass._name << "!" << endl;
2973  return wclass;
2974  }
2975 
2976  return wclass;
2977 }
2978 
2979 /**
2980  *
2981  */
2982 WinGraphicsWindow::WinWindowHandle::
2983 WinWindowHandle(WinGraphicsWindow *window, const WindowHandle &copy) :
2984  WindowHandle(copy),
2985  _window(window)
2986 {
2987 }
2988 
2989 /**
2990  * Should be called by the WinGraphicsWindow's destructor, so that we don't
2991  * end up with a floating pointer should this object persist beyond the
2992  * lifespan of its window.
2993  */
2994 void WinGraphicsWindow::WinWindowHandle::
2995 clear_window() {
2996  _window = nullptr;
2997 }
2998 
2999 /**
3000  * Called on a child handle to deliver a keyboard button event generated in
3001  * the parent window.
3002  */
3003 void WinGraphicsWindow::WinWindowHandle::
3004 receive_windows_message(unsigned int msg, int wparam, int lparam) {
3005  if (_window != nullptr) {
3006  _window->receive_windows_message(msg, wparam, lparam);
3007  }
3008 }
3009 
3010 
3011 // pops up MsgBox wsystem error msg
3012 void PrintErrorMessage(DWORD msgID) {
3013  LPTSTR pMessageBuffer;
3014 
3015  if (msgID==PRINT_LAST_ERROR)
3016  msgID=GetLastError();
3017 
3018  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
3019  nullptr,msgID,
3020  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //The user default language
3021  (LPTSTR) &pMessageBuffer, // the weird ptrptr->ptr cast is intentional, see FORMAT_MESSAGE_ALLOCATE_BUFFER
3022  1024, nullptr);
3023  MessageBox(GetDesktopWindow(),pMessageBuffer,_T(errorbox_title),MB_OK);
3024  windisplay_cat.fatal() << "System error msg: " << pMessageBuffer << endl;
3025  LocalFree( pMessageBuffer );
3026 }
3027 
3028 void
3029 ClearToBlack(HWND hWnd, const WindowProperties &props) {
3030  if (!props.has_origin()) {
3031  if (windisplay_cat.is_debug()) {
3032  windisplay_cat.debug()
3033  << "Skipping ClearToBlack, no origin specified yet.\n";
3034  }
3035  return;
3036  }
3037 
3038  if (windisplay_cat.is_debug()) {
3039  windisplay_cat.debug()
3040  << "ClearToBlack(" << hWnd << ", " << props << ")\n";
3041  }
3042  // clear to black
3043  HDC hDC=GetDC(hWnd); // GetDC is not particularly fast. if this needs to be super-quick, we should cache GetDC's hDC
3044  RECT clrRect = {
3045  props.get_x_origin(), props.get_y_origin(),
3046  props.get_x_origin() + props.get_x_size(),
3047  props.get_y_origin() + props.get_y_size()
3048  };
3049  FillRect(hDC,&clrRect,(HBRUSH)GetStockObject(BLACK_BRUSH));
3050  ReleaseDC(hWnd,hDC);
3051  GdiFlush();
3052 }
3053 
3054 /**
3055  * Fills view_rect with the coordinates of the client area of the indicated
3056  * window, converted to screen coordinates.
3057  */
3058 void get_client_rect_screen(HWND hwnd, RECT *view_rect) {
3059  GetClientRect(hwnd, view_rect);
3060 
3061  POINT ul, lr;
3062  ul.x = view_rect->left;
3063  ul.y = view_rect->top;
3064  lr.x = view_rect->right;
3065  lr.y = view_rect->bottom;
3066 
3067  ClientToScreen(hwnd, &ul);
3068  ClientToScreen(hwnd, &lr);
3069 
3070  view_rect->left = ul.x;
3071  view_rect->top = ul.y;
3072  view_rect->right = lr.x;
3073  view_rect->bottom = lr.y;
3074 }
3075 
3076 /**
3077  * Adds the specified Windows proc event handler to be called whenever a
3078  * Windows event occurs.
3079  *
3080  */
3082  nassertv(wnd_proc != nullptr);
3083  _window_proc_classes.insert( (GraphicsWindowProc*)wnd_proc );
3084 }
3085 
3086 /**
3087  * Removes the specified Windows proc event handler.
3088  *
3089  */
3091  nassertv(wnd_proc != nullptr);
3092  _window_proc_classes.erase( (GraphicsWindowProc*)wnd_proc );
3093 }
3094 
3095 /**
3096  * Removes all Windows proc event handlers.
3097  *
3098  */
3100  _window_proc_classes.clear();
3101 }
3102 
3103 /**
3104  * Returns whether this window supports adding of windows proc handlers.
3105  *
3106  */
3108  return true;
3109 }
3110 
3111 /**
3112  * Returns whether the specified event msg is a touch message.
3113  *
3114  */
3117  return callbackData->get_msg() == WM_TOUCH;
3118 }
3119 
3120 /**
3121  * Returns the current number of touches on this window.
3122  *
3123  */
3125 get_num_touches(){
3126  return _num_touches;
3127 }
3128 
3129 /**
3130  * Returns the TouchInfo object describing the specified touch.
3131  *
3132  */
3134 get_touch_info(int index) {
3135  nassertr(index >= 0 && index < MAX_TOUCHES, TouchInfo());
3136 
3137  TOUCHINPUT ti = _touches[index];
3138  POINT point;
3139  point.x = TOUCH_COORD_TO_PIXEL(ti.x);
3140  point.y = TOUCH_COORD_TO_PIXEL(ti.y);
3141  ScreenToClient(_hWnd, &point);
3142 
3143  TouchInfo ret = TouchInfo();
3144  ret.set_x(point.x);
3145  ret.set_y(point.y);
3146  ret.set_id(ti.dwID);
3147  ret.set_flags(ti.dwFlags);
3148  return ret;
3149 }
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:3090
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:3134
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:1380
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:3125
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:1366
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:2208
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:3058
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:3081
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:3107
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:3116
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:3099
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