Panda3D
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 
2361  // Get the current mode. We'll try to match the refresh rate.
2362  DEVMODE cur_dm;
2363  ZeroMemory(&cur_dm, sizeof(cur_dm));
2364  cur_dm.dmSize = sizeof(cur_dm);
2365  EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &cur_dm);
2366 
2367  int modenum = 0;
2368  int saved_modenum = -1;
2369 
2370  while (1) {
2371  ZeroMemory(&dm, sizeof(dm));
2372  dm.dmSize = sizeof(dm);
2373 
2374  if (!EnumDisplaySettings(NULL, modenum, &dm)) {
2375  break;
2376  }
2377 
2378  if ((dm.dmPelsWidth == dwWidth) && (dm.dmPelsHeight == dwHeight) &&
2379  (dm.dmBitsPerPel == bpp)) {
2380  // If this also matches in refresh rate, we're done here. Otherwise,
2381  // save this as a second choice for later.
2382  if (dm.dmDisplayFrequency == cur_dm.dmDisplayFrequency) {
2383  return true;
2384  } else if (saved_modenum == -1) {
2385  saved_modenum = modenum;
2386  }
2387  }
2388  modenum++;
2389  }
2390 
2391  // Failed to find an exact match, but we do have a match that didn't match
2392  // the refresh rate.
2393  if (saved_modenum != -1) {
2394  ZeroMemory(&dm, sizeof(dm));
2395  dm.dmSize = sizeof(dm);
2396 
2397  if (EnumDisplaySettings(NULL, saved_modenum, &dm)) {
2398  return true;
2399  }
2400  }
2401 
2402  return false;
2403 }
2404 
2405 ////////////////////////////////////////////////////////////////////
2406 // Function: WinGraphicsWindow::show_error_message
2407 // Access: Private, Static
2408 // Description: Pops up a dialog box with the indicated Windows error
2409 // message ID (or the last error message generated) for
2410 // meaningful display to the user.
2411 ////////////////////////////////////////////////////////////////////
2412 void WinGraphicsWindow::
2413 show_error_message(DWORD message_id) {
2414  LPTSTR message_buffer;
2415 
2416  if (message_id == 0) {
2417  message_id = GetLastError();
2418  }
2419 
2420  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
2421  NULL, message_id,
2422  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //The user default language
2423  (LPTSTR)&message_buffer, // the weird ptrptr->ptr cast is intentional, see FORMAT_MESSAGE_ALLOCATE_BUFFER
2424  1024, NULL);
2425  MessageBox(GetDesktopWindow(), message_buffer, _T(errorbox_title), MB_OK);
2426  windisplay_cat.fatal() << "System error msg: " << message_buffer << endl;
2427  LocalFree(message_buffer);
2428 }
2429 
2430 ////////////////////////////////////////////////////////////////////
2431 // Function: WinGraphicsWindow::handle_keypress
2432 // Access: Private
2433 // Description:
2434 ////////////////////////////////////////////////////////////////////
2435 void WinGraphicsWindow::
2436 handle_keypress(ButtonHandle key, int x, int y, double time) {
2437  _input_devices[0].set_pointer_in_window(x, y);
2438  if (key != ButtonHandle::none()) {
2439  _input_devices[0].button_down(key, time);
2440  }
2441 }
2442 
2443 ////////////////////////////////////////////////////////////////////
2444 // Function: WinGraphicsWindow::handle_keyresume
2445 // Access: Private
2446 // Description: Indicates we detected a key was already down when the
2447 // focus is restored to the window. Mainly useful for
2448 // tracking the state of modifier keys.
2449 ////////////////////////////////////////////////////////////////////
2450 void WinGraphicsWindow::
2451 handle_keyresume(ButtonHandle key, double time) {
2452  if (key != ButtonHandle::none()) {
2453  _input_devices[0].button_resume_down(key, time);
2454  }
2455 }
2456 
2457 ////////////////////////////////////////////////////////////////////
2458 // Function: WinGraphicsWindow::handle_keyrelease
2459 // Access: Private
2460 // Description:
2461 ////////////////////////////////////////////////////////////////////
2462 void WinGraphicsWindow::
2463 handle_keyrelease(ButtonHandle key, double time) {
2464  if (key != ButtonHandle::none()) {
2465  _input_devices[0].button_up(key, time);
2466  }
2467 }
2468 
2469 ////////////////////////////////////////////////////////////////////
2470 // Function: WinGraphicsWindow::handle_raw_keypress
2471 // Access: Private
2472 // Description:
2473 ////////////////////////////////////////////////////////////////////
2474 void WinGraphicsWindow::
2475 handle_raw_keypress(ButtonHandle key, double time) {
2476  if (key != ButtonHandle::none()) {
2477  _input_devices[0].raw_button_down(key, time);
2478  }
2479 }
2480 
2481 ////////////////////////////////////////////////////////////////////
2482 // Function: WinGraphicsWindow::handle_raw_keyrelease
2483 // Access: Private
2484 // Description:
2485 ////////////////////////////////////////////////////////////////////
2486 void WinGraphicsWindow::
2487 handle_raw_keyrelease(ButtonHandle key, double time) {
2488  if (key != ButtonHandle::none()) {
2489  _input_devices[0].raw_button_up(key, time);
2490  }
2491 }
2492 
2493 ////////////////////////////////////////////////////////////////////
2494 // Function: WinGraphicsWindow::lookup_key
2495 // Access: Private
2496 // Description: Translates the keycode reported by Windows to an
2497 // appropriate Panda ButtonHandle.
2498 ////////////////////////////////////////////////////////////////////
2499 ButtonHandle WinGraphicsWindow::
2500 lookup_key(WPARAM wparam) const {
2501  // First, check for a few buttons that we filter out when the IME
2502  // window is open.
2503  if (!_ime_active) {
2504  switch(wparam) {
2505  case VK_BACK: return KeyboardButton::backspace();
2506  case VK_DELETE: return KeyboardButton::del();
2507  case VK_ESCAPE: return KeyboardButton::escape();
2508  case VK_SPACE: return KeyboardButton::space();
2509  case VK_UP: return KeyboardButton::up();
2510  case VK_DOWN: return KeyboardButton::down();
2511  case VK_LEFT: return KeyboardButton::left();
2512  case VK_RIGHT: return KeyboardButton::right();
2513  }
2514  }
2515 
2516  // Now check for the rest of the buttons, including the ones that
2517  // we allow through even when the IME window is open.
2518  switch(wparam) {
2519  case VK_TAB: return KeyboardButton::tab();
2520  case VK_PRIOR: return KeyboardButton::page_up();
2521  case VK_NEXT: return KeyboardButton::page_down();
2522  case VK_HOME: return KeyboardButton::home();
2523  case VK_END: return KeyboardButton::end();
2524  case VK_F1: return KeyboardButton::f1();
2525  case VK_F2: return KeyboardButton::f2();
2526  case VK_F3: return KeyboardButton::f3();
2527  case VK_F4: return KeyboardButton::f4();
2528  case VK_F5: return KeyboardButton::f5();
2529  case VK_F6: return KeyboardButton::f6();
2530  case VK_F7: return KeyboardButton::f7();
2531  case VK_F8: return KeyboardButton::f8();
2532  case VK_F9: return KeyboardButton::f9();
2533  case VK_F10: return KeyboardButton::f10();
2534  case VK_F11: return KeyboardButton::f11();
2535  case VK_F12: return KeyboardButton::f12();
2536  case VK_INSERT: return KeyboardButton::insert();
2537  case VK_CAPITAL: return KeyboardButton::caps_lock();
2538  case VK_NUMLOCK: return KeyboardButton::num_lock();
2539  case VK_SCROLL: return KeyboardButton::scroll_lock();
2540  case VK_PAUSE: return KeyboardButton::pause();
2541  case VK_SNAPSHOT: return KeyboardButton::print_screen();
2542 
2543  case VK_SHIFT: return KeyboardButton::shift();
2544  case VK_LSHIFT: return KeyboardButton::lshift();
2545  case VK_RSHIFT: return KeyboardButton::rshift();
2546 
2547  case VK_CONTROL: return KeyboardButton::control();
2548  case VK_LCONTROL: return KeyboardButton::lcontrol();
2549  case VK_RCONTROL: return KeyboardButton::rcontrol();
2550 
2551  case VK_MENU: return KeyboardButton::alt();
2552  case VK_LMENU: return KeyboardButton::lalt();
2553  case VK_RMENU: return KeyboardButton::ralt();
2554 
2555  default:
2556  int key = MapVirtualKey(wparam, 2);
2557  if (isascii(key) && key != 0) {
2558  // We used to try to remap lowercase to uppercase keys
2559  // here based on the state of the shift and/or caps lock
2560  // keys. But that's a mistake, and doesn't allow for
2561  // international or user-defined keyboards; let Windows
2562  // do that mapping.
2563 
2564  // Nowadays, we make a distinction between a "button"
2565  // and a "keystroke". A button corresponds to a
2566  // physical button on the keyboard and has a down and up
2567  // event associated. A keystroke may or may not
2568  // correspond to a physical button, but will be some
2569  // Unicode character and will not have a corresponding
2570  // up event.
2571  return KeyboardButton::ascii_key(tolower(key));
2572  }
2573  break;
2574  }
2575  return ButtonHandle::none();
2576 }
2577 
2578 ////////////////////////////////////////////////////////////////////
2579 // Function: WinGraphicsWindow::lookup_raw_key
2580 // Access: Private
2581 // Description: Translates the scancode reported by Windows to an
2582 // appropriate Panda ButtonHandle.
2583 ////////////////////////////////////////////////////////////////////
2584 ButtonHandle WinGraphicsWindow::
2585 lookup_raw_key(LPARAM lparam) const {
2586  unsigned char vsc = (lparam & 0xff0000) >> 16;
2587 
2588  if (lparam & 0x1000000) {
2589  // Extended keys
2590  switch (vsc) {
2591  case 28: return KeyboardButton::enter();
2592  case 29: return KeyboardButton::rcontrol();
2593  case 53: return KeyboardButton::ascii_key('/');
2594  case 55: return KeyboardButton::print_screen();
2595  case 56: return KeyboardButton::ralt();
2596  case 69: return KeyboardButton::num_lock();
2597  case 71: return KeyboardButton::home();
2598  case 72: return KeyboardButton::up();
2599  case 73: return KeyboardButton::page_up();
2600  case 75: return KeyboardButton::left();
2601  case 77: return KeyboardButton::right();
2602  case 79: return KeyboardButton::end();
2603  case 80: return KeyboardButton::down();
2604  case 81: return KeyboardButton::page_down();
2605  case 82: return KeyboardButton::insert();
2606  case 83: return KeyboardButton::del();
2607  case 91: return KeyboardButton::lmeta();
2608  case 92: return KeyboardButton::rmeta();
2609  case 93: return KeyboardButton::menu();
2610  }
2611  }
2612 
2613  if (vsc <= 83) {
2614  static ButtonHandle raw_map[] = {
2616  KeyboardButton::escape(),
2629  KeyboardButton::backspace(),
2630  KeyboardButton::tab(),
2643  KeyboardButton::enter(),
2644  KeyboardButton::lcontrol(),
2657  KeyboardButton::lshift(),
2669  KeyboardButton::rshift(),
2671  KeyboardButton::lalt(),
2672  KeyboardButton::space(),
2673  KeyboardButton::caps_lock(),
2674  KeyboardButton::f1(),
2675  KeyboardButton::f2(),
2676  KeyboardButton::f3(),
2677  KeyboardButton::f4(),
2678  KeyboardButton::f5(),
2679  KeyboardButton::f6(),
2680  KeyboardButton::f7(),
2681  KeyboardButton::f8(),
2682  KeyboardButton::f9(),
2683  KeyboardButton::f10(),
2684  KeyboardButton::pause(),
2685  KeyboardButton::scroll_lock(),
2699  };
2700  return raw_map[vsc];
2701  }
2702 
2703  // A few additional keys don't fit well in the above table.
2704  switch (vsc) {
2705  case 87: return KeyboardButton::f11();
2706  case 88: return KeyboardButton::f12();
2707  default: return ButtonHandle::none();
2708  }
2709 }
2710 
2711 ////////////////////////////////////////////////////////////////////
2712 // Function: WinGraphicsWindow::get_keyboard_map
2713 // Access: Published, Virtual
2714 // Description: Returns a ButtonMap containing the association
2715 // between raw buttons and virtual buttons.
2716 //
2717 // Note that on Windows, the pause button and numpad
2718 // keys are not mapped reliably.
2719 ////////////////////////////////////////////////////////////////////
2720 ButtonMap *WinGraphicsWindow::
2721 get_keyboard_map() const {
2722  ButtonMap *map = new ButtonMap;
2723 
2724  wchar_t text[256];
2725  UINT vsc = 0;
2726  unsigned short ex_vsc[] = {0x57, 0x58,
2727  0x011c, 0x011d, 0x0135, 0x0137, 0x0138, 0x0145, 0x0147, 0x0148, 0x0149, 0x014b, 0x014d, 0x014f, 0x0150, 0x0151, 0x0152, 0x0153, 0x015b, 0x015c, 0x015d};
2728 
2729  for (int k = 1; k < 84 + 17; ++k) {
2730  if (k >= 84) {
2731  vsc = ex_vsc[k - 84];
2732  } else {
2733  vsc = k;
2734  }
2735 
2736  UINT lparam = vsc << 16;
2737  ButtonHandle raw_button = lookup_raw_key(lparam);
2738  if (raw_button == ButtonHandle::none()) {
2739  continue;
2740  }
2741 
2742  ButtonHandle button;
2743  if (vsc == 0x45) {
2744  button = KeyboardButton::pause();
2745 
2746  } else if (vsc >= 0x47 && vsc <= 0x53) {
2747  // The numpad keys are not mapped correctly, see KB72583
2748  button = raw_button;
2749 
2750  } else {
2751  if (vsc == 0x145) {
2752  // Don't ask why - I have no idea.
2753  vsc = 0x45;
2754  }
2755  if (vsc & 0x0100) {
2756  // MapVirtualKey recognizes extended codes differently.
2757  vsc ^= 0xe100;
2758  }
2759 
2760  UINT vk = MapVirtualKeyA(vsc, MAPVK_VSC_TO_VK_EX);
2761  button = lookup_key(vk);
2762  //if (button == ButtonHandle::none()) {
2763  // continue;
2764  //}
2765  }
2766 
2767  int len = GetKeyNameTextW(lparam, text, 256);
2768  TextEncoder enc;
2769  enc.set_wtext(wstring(text, len));
2770  map->map_button(raw_button, button, enc.get_text());
2771  }
2772 
2773  return map;
2774 }
2775 
2776 ////////////////////////////////////////////////////////////////////
2777 // Function: WinGraphicsWindow::handle_raw_input
2778 // Access: Private
2779 // Description:
2780 ////////////////////////////////////////////////////////////////////
2781 void WinGraphicsWindow::
2782 handle_raw_input(HRAWINPUT hraw) {
2783  LPBYTE lpb;
2784  UINT dwSize;
2785 
2786  if (hraw == 0) {
2787  return;
2788  }
2789  if (pGetRawInputData(hraw, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)) == -1) {
2790  return;
2791  }
2792 
2793  lpb = (LPBYTE)alloca(sizeof(LPBYTE) * dwSize);
2794  if (lpb == NULL) {
2795  return;
2796  }
2797 
2798  if (pGetRawInputData(hraw, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) {
2799  return;
2800  }
2801 
2802  RAWINPUT *raw = (RAWINPUT *)lpb;
2803  if (raw->header.hDevice == 0) {
2804  return;
2805  }
2806 
2807  for (int i = 1; i < (int)(_input_devices.size()); ++i) {
2808  if (_input_device_handle[i] == raw->header.hDevice) {
2809  int adjx = raw->data.mouse.lLastX;
2810  int adjy = raw->data.mouse.lLastY;
2811 
2812  if (raw->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) {
2813  _input_devices[i].set_pointer_in_window(adjx, adjy);
2814  } else {
2815  int oldx = _input_devices[i].get_raw_pointer().get_x();
2816  int oldy = _input_devices[i].get_raw_pointer().get_y();
2817  _input_devices[i].set_pointer_in_window(oldx + adjx, oldy + adjy);
2818  }
2819 
2820  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) {
2821  _input_devices[i].button_down(MouseButton::button(0), get_message_time());
2822  }
2823  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP) {
2824  _input_devices[i].button_up(MouseButton::button(0), get_message_time());
2825  }
2826  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) {
2827  _input_devices[i].button_down(MouseButton::button(2), get_message_time());
2828  }
2829  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP) {
2830  _input_devices[i].button_up(MouseButton::button(2), get_message_time());
2831  }
2832  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) {
2833  _input_devices[i].button_down(MouseButton::button(1), get_message_time());
2834  }
2835  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP) {
2836  _input_devices[i].button_up(MouseButton::button(1), get_message_time());
2837  }
2838  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) {
2839  _input_devices[i].button_down(MouseButton::button(3), get_message_time());
2840  }
2841  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP) {
2842  _input_devices[i].button_up(MouseButton::button(3), get_message_time());
2843  }
2844  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) {
2845  _input_devices[i].button_down(MouseButton::button(4), get_message_time());
2846  }
2847  if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP) {
2848  _input_devices[i].button_up(MouseButton::button(4), get_message_time());
2849  }
2850  }
2851  }
2852 }
2853 
2854 ////////////////////////////////////////////////////////////////////
2855 // Function: WinGraphicsWindow::handle_mouse_motion
2856 // Access: Private
2857 // Description:
2858 ////////////////////////////////////////////////////////////////////
2859 bool WinGraphicsWindow::
2860 handle_mouse_motion(int x, int y) {
2861  _input_devices[0].set_pointer_in_window(x, y);
2862  return false;
2863 }
2864 
2865 ////////////////////////////////////////////////////////////////////
2866 // Function: WinGraphicsWindow::handle_mouse_exit
2867 // Access: Private
2868 // Description:
2869 ////////////////////////////////////////////////////////////////////
2870 void WinGraphicsWindow::
2871 handle_mouse_exit() {
2872  // note: 'mouse_motion' is considered the 'entry' event
2873  _input_devices[0].set_pointer_out_of_window();
2874 }
2875 
2876 ////////////////////////////////////////////////////////////////////
2877 // Function: WinGraphicsWindow::get_icon
2878 // Access: Private, Static
2879 // Description: Loads and returns an HICON corresponding to the
2880 // indicated filename. If the file cannot be loaded,
2881 // returns 0.
2882 ////////////////////////////////////////////////////////////////////
2883 HICON WinGraphicsWindow::
2884 get_icon(const Filename &filename) {
2885  // First, look for the unresolved filename in our index.
2886  IconFilenames::iterator fi = _icon_filenames.find(filename);
2887  if (fi != _icon_filenames.end()) {
2888  return (HICON)((*fi).second);
2889  }
2890 
2891  // If it wasn't found, resolve the filename and search for that.
2892 
2893  // Since we have to use a Windows call to load the image from a
2894  // filename, we can't load a virtual file and we can't use the
2895  // virtual file system.
2896  Filename resolved = filename;
2897  if (!resolved.resolve_filename(get_model_path())) {
2898  // The filename doesn't exist along the search path.
2899  if (resolved.is_fully_qualified() && resolved.exists()) {
2900  // But it does exist locally, so accept it.
2901 
2902  } else {
2903  windisplay_cat.warning()
2904  << "Could not find icon filename " << filename << "\n";
2905  return 0;
2906  }
2907  }
2908  fi = _icon_filenames.find(resolved);
2909  if (fi != _icon_filenames.end()) {
2910  _icon_filenames[filename] = (*fi).second;
2911  return (HICON)((*fi).second);
2912  }
2913 
2914  Filename os = resolved.to_os_specific();
2915 
2916  HANDLE h = LoadImage(NULL, os.c_str(),
2917  IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
2918  if (h == 0) {
2919  windisplay_cat.warning()
2920  << "windows icon filename '" << os << "' could not be loaded!!\n";
2921  }
2922 
2923  _icon_filenames[filename] = h;
2924  _icon_filenames[resolved] = h;
2925  return (HICON)h;
2926 }
2927 
2928 ////////////////////////////////////////////////////////////////////
2929 // Function: WinGraphicsWindow::get_cursor
2930 // Access: Private, Static
2931 // Description: Loads and returns an HCURSOR corresponding to the
2932 // indicated filename. If the file cannot be loaded,
2933 // returns 0.
2934 ////////////////////////////////////////////////////////////////////
2935 HCURSOR WinGraphicsWindow::
2936 get_cursor(const Filename &filename) {
2937  // The empty filename means to disable a custom cursor.
2938  if (filename.empty()) {
2939  return 0;
2940  }
2941 
2942  // First, look for the unresolved filename in our index.
2943  IconFilenames::iterator fi = _cursor_filenames.find(filename);
2944  if (fi != _cursor_filenames.end()) {
2945  return (HCURSOR)((*fi).second);
2946  }
2947 
2948  // If it wasn't found, resolve the filename and search for that.
2949 
2950  // Since we have to use a Windows call to load the image from a
2951  // filename, we can't load a virtual file and we can't use the
2952  // virtual file system.
2953  Filename resolved = filename;
2954  if (!resolved.resolve_filename(get_model_path())) {
2955  // The filename doesn't exist.
2956  windisplay_cat.warning()
2957  << "Could not find cursor filename " << filename << "\n";
2958  return 0;
2959  }
2960  fi = _cursor_filenames.find(resolved);
2961  if (fi != _cursor_filenames.end()) {
2962  _cursor_filenames[filename] = (*fi).second;
2963  return (HCURSOR)((*fi).second);
2964  }
2965 
2966  Filename os = resolved.to_os_specific();
2967 
2968  HANDLE h = LoadImage(NULL, os.c_str(),
2969  IMAGE_CURSOR, 0, 0, LR_LOADFROMFILE);
2970  if (h == 0) {
2971  windisplay_cat.warning()
2972  << "windows cursor filename '" << os << "' could not be loaded!!\n";
2973  show_error_message();
2974  }
2975 
2976  _cursor_filenames[filename] = h;
2977  _cursor_filenames[resolved] = h;
2978  return (HCURSOR)h;
2979 }
2980 
2981 static HCURSOR get_cursor(const Filename &filename);
2982 
2983 ////////////////////////////////////////////////////////////////////
2984 // Function: WinGraphicsWindow::register_window_class
2985 // Access: Private, Static
2986 // Description: Registers a Window class appropriate for the
2987 // indicated properties. This class may be shared by
2988 // multiple windows.
2989 ////////////////////////////////////////////////////////////////////
2990 const WinGraphicsWindow::WindowClass &WinGraphicsWindow::
2991 register_window_class(const WindowProperties &props) {
2992  WindowClass wcreg(props);
2993  wostringstream wclass_name;
2994  wclass_name << L"WinGraphicsWindow" << _window_class_index;
2995  wcreg._name = wclass_name.str();
2996 
2997  pair<WindowClasses::iterator, bool> found = _window_classes.insert(wcreg);
2998  const WindowClass &wclass = (*found.first);
2999 
3000  if (!found.second) {
3001  // We have already created a window class.
3002  return wclass;
3003  }
3004 
3005  // We have not yet created this window class.
3006  _window_class_index++;
3007 
3008  WNDCLASSW wc;
3009 
3010  HINSTANCE instance = GetModuleHandle(NULL);
3011 
3012  // Clear before filling in window structure!
3013  ZeroMemory(&wc, sizeof(wc));
3014  wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
3015  wc.lpfnWndProc = (WNDPROC)static_window_proc;
3016  wc.hInstance = instance;
3017 
3018  wc.hIcon = wclass._icon;
3019 
3020  wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
3021  wc.lpszMenuName = NULL;
3022  wc.lpszClassName = wclass._name.c_str();
3023 
3024  if (!RegisterClassW(&wc)) {
3025  windisplay_cat.error()
3026  << "could not register window class " << wclass._name << "!" << endl;
3027  return wclass;
3028  }
3029 
3030  return wclass;
3031 }
3032 
3033 ////////////////////////////////////////////////////////////////////
3034 // Function: WinGraphicsWindow::WinWindowHandle::Constructor
3035 // Access: Public
3036 // Description:
3037 ////////////////////////////////////////////////////////////////////
3038 WinGraphicsWindow::WinWindowHandle::
3039 WinWindowHandle(WinGraphicsWindow *window, const WindowHandle &copy) :
3040  WindowHandle(copy),
3041  _window(window)
3042 {
3043 }
3044 
3045 ////////////////////////////////////////////////////////////////////
3046 // Function: WinGraphicsWindow::WinWindowHandle::clear_window
3047 // Access: Public
3048 // Description: Should be called by the WinGraphicsWindow's
3049 // destructor, so that we don't end up with a floating
3050 // pointer should this object persist beyond the
3051 // lifespan of its window.
3052 ////////////////////////////////////////////////////////////////////
3053 void WinGraphicsWindow::WinWindowHandle::
3054 clear_window() {
3055  _window = NULL;
3056 }
3057 
3058 ////////////////////////////////////////////////////////////////////
3059 // Function: WinGraphicsWindow::WinWindowHandle::receive_windows_message
3060 // Access: Public, Virtual
3061 // Description: Called on a child handle to deliver a keyboard button
3062 // event generated in the parent window.
3063 ////////////////////////////////////////////////////////////////////
3064 void WinGraphicsWindow::WinWindowHandle::
3065 receive_windows_message(unsigned int msg, int wparam, int lparam) {
3066  if (_window != NULL) {
3067  _window->receive_windows_message(msg, wparam, lparam);
3068  }
3069 }
3070 
3071 
3072 // pops up MsgBox w/system error msg
3073 void PrintErrorMessage(DWORD msgID) {
3074  LPTSTR pMessageBuffer;
3075 
3076  if (msgID==PRINT_LAST_ERROR)
3077  msgID=GetLastError();
3078 
3079  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
3080  NULL,msgID,
3081  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //The user default language
3082  (LPTSTR) &pMessageBuffer, // the weird ptrptr->ptr cast is intentional, see FORMAT_MESSAGE_ALLOCATE_BUFFER
3083  1024, NULL);
3084  MessageBox(GetDesktopWindow(),pMessageBuffer,_T(errorbox_title),MB_OK);
3085  windisplay_cat.fatal() << "System error msg: " << pMessageBuffer << endl;
3086  LocalFree( pMessageBuffer );
3087 }
3088 
3089 void
3090 ClearToBlack(HWND hWnd, const WindowProperties &props) {
3091  if (!props.has_origin()) {
3092  if (windisplay_cat.is_debug()) {
3093  windisplay_cat.debug()
3094  << "Skipping ClearToBlack, no origin specified yet.\n";
3095  }
3096  return;
3097  }
3098 
3099  if (windisplay_cat.is_debug()) {
3100  windisplay_cat.debug()
3101  << "ClearToBlack(" << hWnd << ", " << props << ")\n";
3102  }
3103  // clear to black
3104  HDC hDC=GetDC(hWnd); // GetDC is not particularly fast. if this needs to be super-quick, we should cache GetDC's hDC
3105  RECT clrRect = {
3106  props.get_x_origin(), props.get_y_origin(),
3107  props.get_x_origin() + props.get_x_size(),
3108  props.get_y_origin() + props.get_y_size()
3109  };
3110  FillRect(hDC,&clrRect,(HBRUSH)GetStockObject(BLACK_BRUSH));
3111  ReleaseDC(hWnd,hDC);
3112  GdiFlush();
3113 }
3114 
3115 ////////////////////////////////////////////////////////////////////
3116 // Function: get_client_rect_screen
3117 // Description: Fills view_rect with the coordinates of the client
3118 // area of the indicated window, converted to screen
3119 // coordinates.
3120 ////////////////////////////////////////////////////////////////////
3121 void get_client_rect_screen(HWND hwnd, RECT *view_rect) {
3122  GetClientRect(hwnd, view_rect);
3123 
3124  POINT ul, lr;
3125  ul.x = view_rect->left;
3126  ul.y = view_rect->top;
3127  lr.x = view_rect->right;
3128  lr.y = view_rect->bottom;
3129 
3130  ClientToScreen(hwnd, &ul);
3131  ClientToScreen(hwnd, &lr);
3132 
3133  view_rect->left = ul.x;
3134  view_rect->top = ul.y;
3135  view_rect->right = lr.x;
3136  view_rect->bottom = lr.y;
3137 }
3138 
3139 ////////////////////////////////////////////////////////////////////
3140 // Function: WinGraphicsWindow::add_window_proc
3141 // Access: Public, Virtual
3142 // Description: Adds the specified Windows proc event handler to be called
3143 // whenever a Windows event occurs.
3144 //
3145 ////////////////////////////////////////////////////////////////////
3147  nassertv(wnd_proc != NULL);
3148  _window_proc_classes.insert( (GraphicsWindowProc*)wnd_proc );
3149 }
3150 
3151 ////////////////////////////////////////////////////////////////////
3152 // Function: WinGraphicsWindow::remove_window_proc
3153 // Access: Public, Virtual
3154 // Description: Removes the specified Windows proc event handler.
3155 //
3156 ////////////////////////////////////////////////////////////////////
3158  nassertv(wnd_proc != NULL);
3159  _window_proc_classes.erase( (GraphicsWindowProc*)wnd_proc );
3160 }
3161 
3162 ////////////////////////////////////////////////////////////////////
3163 // Function: WinGraphicsWindow::clear_window_procs
3164 // Access: Public, Virtual
3165 // Description: Removes all Windows proc event handlers.
3166 //
3167 ////////////////////////////////////////////////////////////////////
3169  _window_proc_classes.clear();
3170 }
3171 
3172 ////////////////////////////////////////////////////////////////////
3173 // Function: WinGraphicsWindow::supports_window_procs
3174 // Access: Public, Virtual
3175 // Description: Returns whether this window supports adding of windows proc handlers.
3176 //
3177 ////////////////////////////////////////////////////////////////////
3179  return true;
3180 }
3181 
3182 ////////////////////////////////////////////////////////////////////
3183 // Function: WinGraphicsWindow::is_touch_event
3184 // Access: Public, Virtual
3185 // Description: Returns whether the specified event msg is a touch message.
3186 //
3187 ////////////////////////////////////////////////////////////////////
3190 #ifdef HAVE_WIN_TOUCHINPUT
3191  return callbackData->get_msg() == WM_TOUCH;
3192 #else
3193  return false;
3194 #endif
3195 }
3196 
3197 ////////////////////////////////////////////////////////////////////
3198 // Function: WinGraphicsWindow::get_num_touches
3199 // Access: Public, Virtual
3200 // Description: Returns the current number of touches on this window.
3201 //
3202 ////////////////////////////////////////////////////////////////////
3205 #ifdef HAVE_WIN_TOUCHINPUT
3206  return _numTouches;
3207 #else
3208  return 0;
3209 #endif
3210 }
3211 
3212 ////////////////////////////////////////////////////////////////////
3213 // Function: WinGraphicsWindow::get_touch_info
3214 // Access: Public, Virtual
3215 // Description: Returns the TouchInfo object describing the specified touch.
3216 //
3217 ////////////////////////////////////////////////////////////////////
3219 get_touch_info(int index){
3220 #ifdef HAVE_WIN_TOUCHINPUT
3221  TOUCHINPUT ti = _touches[index];
3222  POINT point;
3223  point.x = TOUCH_COORD_TO_PIXEL(ti.x);
3224  point.y = TOUCH_COORD_TO_PIXEL(ti.y);
3225  ScreenToClient(_hWnd, &point);
3226 
3227  TouchInfo ret = TouchInfo();
3228  ret.set_x(point.x);
3229  ret.set_y(point.y);
3230  ret.set_id(ti.dwID);
3231  ret.set_flags(ti.dwFlags);
3232  return ret;
3233 #else
3234  return TouchInfo();
3235 #endif
3236 }
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.
bool is_any_specified() const
Returns true if any properties have been specified, false otherwise.
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...
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
MouseMode get_mouse_mode() const
See set_mouse_mode().
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
This object represents a window on the desktop, not necessarily a Panda window.
Definition: windowHandle.h:40
bool get_foreground() const
Returns true if the window is in the foreground.
void clear_foreground()
Removes the foreground specification from the properties.
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...
ZOrder get_z_order() const
Returns the window&#39;s z_order.
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 has_icon_filename() const
Returns true if set_icon_filename() has been specified.
bool get_minimized() const
Returns true if the window is minimized.
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
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.
bool has_origin() const
Returns true if the window origin has been specified, false otherwise.
void clear_title()
Removes the title specification from the properties.
const string & get_title() const
Returns the window&#39;s title.
virtual int get_num_touches()
Returns the current number of touches on this window.
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...
int get_y_origin() const
Returns the y coordinate of the window&#39;s top-left corner, not including decorations.
virtual void remove_window_proc(const GraphicsWindowProc *wnd_proc_object)
Removes the specified Windows proc event handler.
void clear_minimized()
Removes the minimized specification from the properties.
bool is_fully_qualified() const
Returns true if the filename is fully qualified, e.g.
Definition: filename.I:682
string get_close_request_event() const
Returns the name of the event set via set_close_request_event().
const WindowProperties get_properties() const
Returns the current properties of the window.
bool get_unexposed_draw() const
See set_unexposed_draw().
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
bool has_fullscreen() const
Returns true if set_fullscreen() has been specified.
virtual LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
This is the nonstatic window_proc function.
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).
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
OSHandle * get_os_handle() const
Returns the OS-specific handle stored internally to the WindowHandle wrapper.
Definition: windowHandle.I:41
void clear_icon_filename()
Removes the icon_filename specification from the properties.
bool get_fullscreen() const
Returns true if the window is in fullscreen mode.
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.
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:58
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
const Filename & get_icon_filename() const
Returns the icon filename associated with the window.
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
const Filename & get_cursor_filename() const
Returns the icon filename associated with the mouse cursor.
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...
bool has_foreground() const
Returns true if set_foreground() has been specified.
This is a structure representing a single input device that may be associated with a window...
bool has_cursor_hidden() const
Returns true if set_cursor_hidden() has been specified.
This is a base class for the various different classes that represent the result of a frame of render...
bool has_z_order() const
Returns true if the window z_order 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...
void clear_z_order()
Removes the z_order specification from the properties.
bool has_cursor_filename() const
Returns true if set_cursor_filename() has been specified.
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).
bool has_minimized() const
Returns true if set_minimized() has been specified.
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.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
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.
int get_y_size() const
Returns size in pixels in the y dimension of the useful part of the window, not including decorations...
int get_x_size() const
Returns size in pixels in the x dimension of the useful part of the window, not including decorations...
bool has_title() const
Returns true if the window title has been specified, false otherwise.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
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...
void set_wtext(const wstring &wtext)
Changes the text that is stored in the encoder.
Definition: textEncoder.I:516
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...
bool is_fullscreen() const
Returns true if the window has been opened as a fullscreen window, false otherwise.
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
void set_open(bool open)
Specifies whether the window should be open.
int get_x_origin() const
Returns the x coordinate of the window&#39;s top-left corner, not including decorations.
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 exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1356
string get_text() const
Returns the current text, as encoded via the current encoding system.
Definition: textEncoder.I:167
virtual bool supports_window_procs() const
Returns whether this window supports adding of windows proc handlers.
static GraphicsWindowInputDevice pointer_only(GraphicsWindow *host, const string &name)
This named constructor returns an input device that only has a pointing device, no keyboard...