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