Panda3D
|
00001 // Filename: winGraphicsWindow.cxx 00002 // Created by: drose (20Dec02) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "winGraphicsWindow.h" 00016 #include "config_windisplay.h" 00017 #include "winGraphicsPipe.h" 00018 00019 #include "graphicsPipe.h" 00020 #include "keyboardButton.h" 00021 #include "mouseButton.h" 00022 #include "clockObject.h" 00023 #include "config_util.h" 00024 #include "throw_event.h" 00025 #include "nativeWindowHandle.h" 00026 00027 #include <tchar.h> 00028 00029 00030 00031 TypeHandle WinGraphicsWindow::_type_handle; 00032 TypeHandle WinGraphicsWindow::WinWindowHandle::_type_handle; 00033 00034 WinGraphicsWindow::WindowHandles WinGraphicsWindow::_window_handles; 00035 WinGraphicsWindow *WinGraphicsWindow::_creating_window = NULL; 00036 00037 WinGraphicsWindow *WinGraphicsWindow::_cursor_window = NULL; 00038 bool WinGraphicsWindow::_cursor_hidden = false; 00039 00040 // These are used to save the previous state of the fancy Win2000 00041 // effects that interfere with rendering when the mouse wanders into a 00042 // window's client area. 00043 bool WinGraphicsWindow::_got_saved_params = false; 00044 int WinGraphicsWindow::_saved_mouse_trails; 00045 BOOL WinGraphicsWindow::_saved_cursor_shadow; 00046 BOOL WinGraphicsWindow::_saved_mouse_vanish; 00047 00048 WinGraphicsWindow::IconFilenames WinGraphicsWindow::_icon_filenames; 00049 WinGraphicsWindow::IconFilenames WinGraphicsWindow::_cursor_filenames; 00050 00051 WinGraphicsWindow::WindowClasses WinGraphicsWindow::_window_classes; 00052 int WinGraphicsWindow::_window_class_index = 0; 00053 00054 static const char * const errorbox_title = "Panda3D Error"; 00055 00056 //////////////////////////////////////////////////////////////////// 00057 // 00058 // These static variables contain pointers to the Raw Input 00059 // functions, which are dynamically extracted from USER32.DLL 00060 // 00061 //////////////////////////////////////////////////////////////////// 00062 00063 typedef WINUSERAPI UINT (WINAPI *tGetRawInputDeviceList) 00064 (OUT PRAWINPUTDEVICELIST pRawInputDeviceList, IN OUT PUINT puiNumDevices, IN UINT cbSize); 00065 typedef WINUSERAPI UINT(WINAPI *tGetRawInputData) 00066 (IN HRAWINPUT hRawInput, IN UINT uiCommand, OUT LPVOID pData, IN OUT PUINT pcbSize, IN UINT cbSizeHeader); 00067 typedef WINUSERAPI UINT(WINAPI *tGetRawInputDeviceInfoA) 00068 (IN HANDLE hDevice, IN UINT uiCommand, OUT LPVOID pData, IN OUT PUINT pcbSize); 00069 typedef WINUSERAPI BOOL (WINAPI *tRegisterRawInputDevices) 00070 (IN PCRAWINPUTDEVICE pRawInputDevices, IN UINT uiNumDevices, IN UINT cbSize); 00071 00072 static tGetRawInputDeviceList pGetRawInputDeviceList; 00073 static tGetRawInputData pGetRawInputData; 00074 static tGetRawInputDeviceInfoA pGetRawInputDeviceInfoA; 00075 static tRegisterRawInputDevices pRegisterRawInputDevices; 00076 00077 //////////////////////////////////////////////////////////////////// 00078 // Function: WinGraphicsWindow::Constructor 00079 // Access: Public 00080 // Description: 00081 //////////////////////////////////////////////////////////////////// 00082 WinGraphicsWindow:: 00083 WinGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, 00084 const string &name, 00085 const FrameBufferProperties &fb_prop, 00086 const WindowProperties &win_prop, 00087 int flags, 00088 GraphicsStateGuardian *gsg, 00089 GraphicsOutput *host) : 00090 GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host) 00091 { 00092 initialize_input_devices(); 00093 _hWnd = (HWND)0; 00094 _ime_open = false; 00095 _ime_active = false; 00096 _tracking_mouse_leaving = false; 00097 _maximized = false; 00098 _cursor = 0; 00099 _lost_keypresses = false; 00100 _lshift_down = false; 00101 _rshift_down = false; 00102 _lcontrol_down = false; 00103 _rcontrol_down = false; 00104 _lalt_down = false; 00105 _ralt_down = false; 00106 _hparent = NULL; 00107 #ifdef PANDA_WIN7 00108 _numTouches = 0; 00109 #endif 00110 } 00111 00112 //////////////////////////////////////////////////////////////////// 00113 // Function: WinGraphicsWindow::Destructor 00114 // Access: Public, Virtual 00115 // Description: 00116 //////////////////////////////////////////////////////////////////// 00117 WinGraphicsWindow:: 00118 ~WinGraphicsWindow() { 00119 if (_window_handle != (WindowHandle *)NULL) { 00120 DCAST(WinWindowHandle, _window_handle)->clear_window(); 00121 } 00122 } 00123 00124 //////////////////////////////////////////////////////////////////// 00125 // Function: WinGraphicsWindow::move_pointer 00126 // Access: Published, Virtual 00127 // Description: Forces the pointer to the indicated position within 00128 // the window, if possible. 00129 // 00130 // Returns true if successful, false on failure. This 00131 // may fail if the mouse is not currently within the 00132 // window, or if the API doesn't support this operation. 00133 //////////////////////////////////////////////////////////////////// 00134 bool WinGraphicsWindow:: 00135 move_pointer(int device, int x, int y) { 00136 // First, indicate that the IME is no longer active, so that it won't 00137 // send the string through WM_IME_COMPOSITION. But we still leave 00138 // _ime_open true, so that it also won't send the string through WM_CHAR. 00139 _ime_active = false; 00140 00141 // Note: this is not thread-safe; it should be called only from App. 00142 // Probably not an issue. 00143 if (device == 0) { 00144 // Move the system mouse pointer. 00145 if (!_properties.get_foreground() ) 00146 // !_input_devices[0].get_pointer().get_in_window()) 00147 { 00148 // If the window doesn't have input focus, or the mouse isn't 00149 // currently within the window, forget it. 00150 return false; 00151 } 00152 00153 RECT view_rect; 00154 get_client_rect_screen(_hWnd, &view_rect); 00155 00156 SetCursorPos(view_rect.left + x, view_rect.top + y); 00157 _input_devices[0].set_pointer_in_window(x, y); 00158 return true; 00159 } else { 00160 // Move a raw mouse. 00161 if ((device < 1)||(device >= (int)_input_devices.size())) { 00162 return false; 00163 } 00164 _input_devices[device].set_pointer_in_window(x, y); 00165 return true; 00166 } 00167 } 00168 00169 //////////////////////////////////////////////////////////////////// 00170 // Function: WinGraphicsWindow::close_ime 00171 // Access: Published, Virtual 00172 // Description: Forces the ime window to close, if any 00173 // 00174 //////////////////////////////////////////////////////////////////// 00175 void WinGraphicsWindow:: 00176 close_ime() { 00177 // Check if the ime window is open 00178 if (!_ime_open) 00179 return; 00180 00181 HIMC hIMC = ImmGetContext(_hWnd); 00182 if (hIMC != 0) { 00183 if (!ImmSetOpenStatus(hIMC, false)) { 00184 windisplay_cat.debug() << "ImmSetOpenStatus failed\n"; 00185 } 00186 ImmReleaseContext(_hWnd, hIMC); 00187 } 00188 _ime_open = false; 00189 00190 windisplay_cat.debug() << "success: closed ime window\n"; 00191 return; 00192 } 00193 00194 //////////////////////////////////////////////////////////////////// 00195 // Function: WinGraphicsWindow::begin_flip 00196 // Access: Public, Virtual 00197 // Description: This function will be called within the draw thread 00198 // after end_frame() has been called on all windows, to 00199 // initiate the exchange of the front and back buffers. 00200 // 00201 // This should instruct the window to prepare for the 00202 // flip at the next video sync, but it should not wait. 00203 // 00204 // We have the two separate functions, begin_flip() and 00205 // end_flip(), to make it easier to flip all of the 00206 // windows at the same time. 00207 //////////////////////////////////////////////////////////////////// 00208 void WinGraphicsWindow:: 00209 begin_flip() { 00210 } 00211 00212 //////////////////////////////////////////////////////////////////// 00213 // Function: WinGraphicsWindow::process_events 00214 // Access: Public, Virtual 00215 // Description: Do whatever processing is necessary to ensure that 00216 // the window responds to user events. Also, honor any 00217 // requests recently made via request_properties() 00218 // 00219 // This function is called only within the window 00220 // thread. 00221 //////////////////////////////////////////////////////////////////// 00222 void WinGraphicsWindow:: 00223 process_events() { 00224 GraphicsWindow::process_events(); 00225 00226 // We can't treat the message loop specially just because the window 00227 // is minimized, because we might be reading messages queued up for 00228 // some other window, which is not minimized. 00229 /* 00230 if (!_window_active) { 00231 // Get 1 msg at a time until no more are left and we block and sleep, 00232 // or message changes _return_control_to_app or !_window_active status 00233 00234 while(!_window_active && (!_return_control_to_app)) { 00235 process_1_event(); 00236 } 00237 _return_control_to_app = false; 00238 00239 } else 00240 */ 00241 00242 MSG msg; 00243 00244 // Handle all the messages on the queue in a row. Some of these 00245 // might be for another window, but they will get dispatched 00246 // appropriately. 00247 while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { 00248 process_1_event(); 00249 } 00250 } 00251 00252 //////////////////////////////////////////////////////////////////// 00253 // Function: WinGraphicsWindow::set_properties_now 00254 // Access: Public, Virtual 00255 // Description: Applies the requested set of properties to the 00256 // window, if possible, for instance to request a change 00257 // in size or minimization status. 00258 // 00259 // The window properties are applied immediately, rather 00260 // than waiting until the next frame. This implies that 00261 // this method may *only* be called from within the 00262 // window thread. 00263 // 00264 // The properties that have been applied are cleared 00265 // from the structure by this function; so on return, 00266 // whatever remains in the properties structure are 00267 // those that were unchanged for some reason (probably 00268 // because the underlying interface does not support 00269 // changing that property on an open window). 00270 //////////////////////////////////////////////////////////////////// 00271 void WinGraphicsWindow:: 00272 set_properties_now(WindowProperties &properties) { 00273 GraphicsWindow::set_properties_now(properties); 00274 if (!properties.is_any_specified()) { 00275 // The base class has already handled this case. 00276 return; 00277 } 00278 00279 if (properties.has_title()) { 00280 string title = properties.get_title(); 00281 _properties.set_title(title); 00282 TextEncoder encoder; 00283 wstring title_w = encoder.decode_text(title); 00284 SetWindowTextW(_hWnd, title_w.c_str()); 00285 properties.clear_title(); 00286 } 00287 00288 if (properties.has_cursor_hidden()) { 00289 bool hide_cursor = properties.get_cursor_hidden(); 00290 _properties.set_cursor_hidden(hide_cursor); 00291 if (_cursor_window == this) { 00292 hide_or_show_cursor(hide_cursor); 00293 } 00294 00295 properties.clear_cursor_hidden(); 00296 } 00297 00298 if (properties.has_cursor_filename()) { 00299 Filename filename = properties.get_cursor_filename(); 00300 _properties.set_cursor_filename(filename); 00301 00302 _cursor = get_cursor(filename); 00303 if (_cursor == 0) { 00304 _cursor = LoadCursor(NULL, IDC_ARROW); 00305 } 00306 00307 if (_cursor_window == this) { 00308 SetCursor(_cursor); 00309 } 00310 00311 properties.clear_cursor_filename(); 00312 } 00313 00314 if (properties.has_z_order()) { 00315 WindowProperties::ZOrder last_z_order = _properties.get_z_order(); 00316 _properties.set_z_order(properties.get_z_order()); 00317 adjust_z_order(last_z_order, properties.get_z_order()); 00318 00319 properties.clear_z_order(); 00320 } 00321 00322 if (properties.has_foreground() && properties.get_foreground()) { 00323 if (!SetActiveWindow(_hWnd)) { 00324 windisplay_cat.warning() 00325 << "SetForegroundWindow() failed!\n"; 00326 } else { 00327 _properties.set_foreground(true); 00328 } 00329 00330 properties.clear_foreground(); 00331 } 00332 00333 if (properties.has_minimized()) { 00334 if (_properties.get_minimized() != properties.get_minimized()) { 00335 if (properties.get_minimized()) { 00336 ShowWindow(_hWnd, SW_MINIMIZE); 00337 } else { 00338 ShowWindow(_hWnd, SW_RESTORE); 00339 } 00340 _properties.set_minimized(properties.get_minimized()); 00341 _properties.set_foreground(!properties.get_minimized()); 00342 } 00343 properties.clear_minimized(); 00344 } 00345 00346 if (properties.has_fullscreen()) { 00347 if (properties.get_fullscreen() && !is_fullscreen()) { 00348 if (do_fullscreen_switch()){ 00349 _properties.set_fullscreen(true); 00350 properties.clear_fullscreen(); 00351 } else { 00352 windisplay_cat.warning() 00353 << "Switching to fullscreen mode failed!\n"; 00354 } 00355 } else if (!properties.get_fullscreen() && is_fullscreen()){ 00356 if (do_windowed_switch()){ 00357 _properties.set_fullscreen(false); 00358 properties.clear_fullscreen(); 00359 } else { 00360 windisplay_cat.warning() 00361 << "Switching to windowed mode failed!\n"; 00362 } 00363 } 00364 } 00365 } 00366 00367 //////////////////////////////////////////////////////////////////// 00368 // Function: WinGraphicsWindow::trigger_flip 00369 // Access: Protected 00370 // Description: To be called at the end of the frame, after the 00371 // window has successfully been drawn and is ready to be 00372 // flipped (if appropriate). 00373 //////////////////////////////////////////////////////////////////// 00374 void WinGraphicsWindow:: 00375 trigger_flip() { 00376 GraphicsWindow::trigger_flip(); 00377 00378 if (!get_unexposed_draw()) { 00379 // Now that we've drawn or whatever, invalidate the rectangle so 00380 // we won't redraw again until we get the WM_PAINT message. 00381 00382 InvalidateRect(_hWnd, NULL, FALSE); 00383 _got_expose_event = false; 00384 00385 if (windisplay_cat.is_spam()) { 00386 windisplay_cat.spam() 00387 << "InvalidateRect: " << this << "\n"; 00388 } 00389 } 00390 } 00391 00392 //////////////////////////////////////////////////////////////////// 00393 // Function: WinGraphicsWindow::close_window 00394 // Access: Protected, Virtual 00395 // Description: Closes the window right now. Called from the window 00396 // thread. 00397 //////////////////////////////////////////////////////////////////// 00398 void WinGraphicsWindow:: 00399 close_window() { 00400 set_cursor_out_of_window(); 00401 DestroyWindow(_hWnd); 00402 00403 if (is_fullscreen()) { 00404 // revert to default display mode. 00405 do_fullscreen_disable(); 00406 } 00407 00408 // Remove the window handle from our global map. 00409 _window_handles.erase(_hWnd); 00410 _hWnd = (HWND)0; 00411 00412 GraphicsWindow::close_window(); 00413 } 00414 00415 //////////////////////////////////////////////////////////////////// 00416 // Function: WinGraphicsWindow::open_window 00417 // Access: Protected, Virtual 00418 // Description: Opens the window right now. Called from the window 00419 // thread. Returns true if the window is successfully 00420 // opened, or false if there was a problem. 00421 //////////////////////////////////////////////////////////////////// 00422 bool WinGraphicsWindow:: 00423 open_window() { 00424 if (_properties.has_cursor_filename()) { 00425 _cursor = get_cursor(_properties.get_cursor_filename()); 00426 } 00427 if (_cursor == 0) { 00428 _cursor = LoadCursor(NULL, IDC_ARROW); 00429 } 00430 bool want_foreground = (!_properties.has_foreground() || _properties.get_foreground()); 00431 bool want_minimized = (_properties.has_minimized() && _properties.get_minimized()) && !want_foreground; 00432 00433 HWND old_foreground_window = GetForegroundWindow(); 00434 00435 // Store the current window pointer in _creating_window, so we can 00436 // call CreateWindow() and know which window it is sending events to 00437 // even before it gives us a handle. Warning: this is not thread 00438 // safe! 00439 _creating_window = this; 00440 bool opened = open_graphic_window(is_fullscreen()); 00441 _creating_window = (WinGraphicsWindow *)NULL; 00442 00443 if (!opened) { 00444 return false; 00445 } 00446 00447 // Now that we have a window handle, store it in our global map, so 00448 // future messages for this window can be routed properly. 00449 _window_handles.insert(WindowHandles::value_type(_hWnd, this)); 00450 00451 // move window to top of zorder. 00452 SetWindowPos(_hWnd, HWND_TOP, 0,0,0,0, 00453 SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE); 00454 00455 // need to do twice to override any minimized flags in StartProcessInfo 00456 if (want_minimized) { 00457 ShowWindow(_hWnd, SW_MINIMIZE); 00458 ShowWindow(_hWnd, SW_MINIMIZE); 00459 } else { 00460 ShowWindow(_hWnd, SW_SHOWNORMAL); 00461 ShowWindow(_hWnd, SW_SHOWNORMAL); 00462 } 00463 00464 HWND new_foreground_window = _hWnd; 00465 if (!want_foreground) { 00466 // If we specifically requested the window not to be on top, 00467 // restore the previous foreground window (if we can). 00468 new_foreground_window = old_foreground_window; 00469 } 00470 00471 if (!SetActiveWindow(new_foreground_window)) { 00472 windisplay_cat.warning() 00473 << "SetActiveWindow() failed!\n"; 00474 } 00475 00476 // Let's aggressively call SetForegroundWindow() in addition to 00477 // SetActiveWindow(). It seems to work in some cases to make the 00478 // window come to the top, where SetActiveWindow doesn't work. 00479 if (!SetForegroundWindow(new_foreground_window)) { 00480 windisplay_cat.warning() 00481 << "SetForegroundWindow() failed!\n"; 00482 } 00483 00484 // Determine the initial open status of the IME. 00485 _ime_open = false; 00486 _ime_active = false; 00487 HIMC hIMC = ImmGetContext(_hWnd); 00488 if (hIMC != 0) { 00489 _ime_open = (ImmGetOpenStatus(hIMC) != 0); 00490 ImmReleaseContext(_hWnd, hIMC); 00491 } 00492 00493 // Registers to receive the WM_INPUT messages 00494 if ((pRegisterRawInputDevices)&&(_input_devices.size() > 1)) { 00495 RAWINPUTDEVICE Rid; 00496 Rid.usUsagePage = 0x01; 00497 Rid.usUsage = 0x02; 00498 Rid.dwFlags = 0;// RIDEV_NOLEGACY; // adds HID mouse and also ignores legacy mouse messages 00499 Rid.hwndTarget = _hWnd; 00500 pRegisterRawInputDevices(&Rid, 1, sizeof (Rid)); 00501 } 00502 00503 // Create a WindowHandle for ourselves 00504 _window_handle = NativeWindowHandle::make_win(_hWnd); 00505 00506 // Actually, we want a WinWindowHandle. 00507 _window_handle = new WinWindowHandle(this, *_window_handle); 00508 00509 // And tell our parent window that we're now its child. 00510 if (_parent_window_handle != (WindowHandle *)NULL) { 00511 _parent_window_handle->attach_child(_window_handle); 00512 } 00513 00514 // set us as the focus window for keyboard input 00515 set_focus(); 00516 00517 // Register for Win7 touch events. 00518 #ifdef PANDA_WIN7 00519 RegisterTouchWindow(_hWnd, 0); 00520 #endif 00521 00522 return true; 00523 } 00524 00525 //////////////////////////////////////////////////////////////////// 00526 // Function: WinGraphicsWindow::initialize_input_devices 00527 // Access: Private 00528 // Description: Creates the array of input devices. The first 00529 // one is always the system mouse and keyboard. 00530 // Each subsequent one is a raw mouse device. Also 00531 // initializes a parallel array, _input_device_handle, 00532 // with the win32 handle of each raw input device. 00533 //////////////////////////////////////////////////////////////////// 00534 00535 void WinGraphicsWindow:: 00536 initialize_input_devices() { 00537 UINT nInputDevices; 00538 PRAWINPUTDEVICELIST pRawInputDeviceList; 00539 00540 nassertv(_input_devices.size() == 0); 00541 00542 // Clear the handle array, and set up the system keyboard/mouse 00543 memset(_input_device_handle, 0, sizeof(_input_device_handle)); 00544 GraphicsWindowInputDevice device = 00545 GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse"); 00546 add_input_device(device); 00547 00548 // Try initializing the Raw Input function pointers. 00549 if (pRegisterRawInputDevices==0) { 00550 HMODULE user32 = LoadLibrary("user32.dll"); 00551 if (user32) { 00552 pRegisterRawInputDevices = (tRegisterRawInputDevices)GetProcAddress(user32,"RegisterRawInputDevices"); 00553 pGetRawInputDeviceList = (tGetRawInputDeviceList) GetProcAddress(user32,"GetRawInputDeviceList"); 00554 pGetRawInputDeviceInfoA = (tGetRawInputDeviceInfoA) GetProcAddress(user32,"GetRawInputDeviceInfoA"); 00555 pGetRawInputData = (tGetRawInputData) GetProcAddress(user32,"GetRawInputData"); 00556 } 00557 } 00558 00559 if (pRegisterRawInputDevices==0) return; 00560 if (pGetRawInputDeviceList==0) return; 00561 if (pGetRawInputDeviceInfoA==0) return; 00562 if (pGetRawInputData==0) return; 00563 00564 // Get the number of devices. 00565 if (pGetRawInputDeviceList(NULL, &nInputDevices, sizeof(RAWINPUTDEVICELIST)) != 0) 00566 return; 00567 00568 // Allocate the array to hold the DeviceList 00569 pRawInputDeviceList = (PRAWINPUTDEVICELIST)alloca(sizeof(RAWINPUTDEVICELIST) * nInputDevices); 00570 if (pRawInputDeviceList==0) return; 00571 00572 // Fill the Array 00573 if (pGetRawInputDeviceList(pRawInputDeviceList, &nInputDevices, sizeof(RAWINPUTDEVICELIST)) == -1) 00574 return; 00575 00576 // Loop through all raw devices and find the raw mice 00577 for (int i = 0; i < (int)nInputDevices; i++) { 00578 if (pRawInputDeviceList[i].dwType == RIM_TYPEMOUSE) { 00579 // Fetch information about specified mouse device. 00580 UINT nSize; 00581 if (pGetRawInputDeviceInfoA(pRawInputDeviceList[i].hDevice, RIDI_DEVICENAME, (LPVOID)0, &nSize) != 0) 00582 return; 00583 char *psName = (char*)alloca(sizeof(TCHAR) * nSize); 00584 if (psName == 0) return; 00585 if (pGetRawInputDeviceInfoA(pRawInputDeviceList[i].hDevice, RIDI_DEVICENAME, (LPVOID)psName, &nSize) < 0) 00586 return; 00587 00588 // If it's not an RDP mouse, add it to the list of raw mice. 00589 if (strncmp(psName,"\\??\\Root#RDP_MOU#0000#",22)!=0) { 00590 if (_input_devices.size() < 32) { 00591 if (strncmp(psName,"\\??\\",4)==0) psName += 4; 00592 char *pound1 = strchr(psName,'#'); 00593 char *pound2 = pound1 ? strchr(pound1+1,'#') : 0; 00594 char *pound3 = pound2 ? strchr(pound2+1,'#') : 0; 00595 if (pound3) *pound3 = 0; 00596 for (char *p = psName; *p; p++) { 00597 if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) { 00598 *p = '_'; 00599 } 00600 } 00601 if (pound2) *pound2 = '.'; 00602 _input_device_handle[_input_devices.size()] = pRawInputDeviceList[i].hDevice; 00603 GraphicsWindowInputDevice device = GraphicsWindowInputDevice::pointer_only(this, psName); 00604 device.set_pointer_in_window(0,0); 00605 add_input_device(device); 00606 } 00607 } 00608 } 00609 } 00610 } 00611 00612 //////////////////////////////////////////////////////////////////// 00613 // Function: WinGraphicsWindow::fullscreen_minimized 00614 // Access: Protected, Virtual 00615 // Description: This is a hook for derived classes to do something 00616 // special, if necessary, when a fullscreen window has 00617 // been minimized. The given WindowProperties struct 00618 // will be applied to this window's properties after 00619 // this function returns. 00620 //////////////////////////////////////////////////////////////////// 00621 void WinGraphicsWindow:: 00622 fullscreen_minimized(WindowProperties &) { 00623 } 00624 00625 //////////////////////////////////////////////////////////////////// 00626 // Function: WinGraphicsWindow::fullscreen_restored 00627 // Access: Protected, Virtual 00628 // Description: This is a hook for derived classes to do something 00629 // special, if necessary, when a fullscreen window has 00630 // been restored after being minimized. The given 00631 // WindowProperties struct will be applied to this 00632 // window's properties after this function returns. 00633 //////////////////////////////////////////////////////////////////// 00634 void WinGraphicsWindow:: 00635 fullscreen_restored(WindowProperties &) { 00636 } 00637 00638 //////////////////////////////////////////////////////////////////// 00639 // Function: WinGraphicsWindow::do_reshape_request 00640 // Access: Protected, Virtual 00641 // Description: Called from the window thread in response to a request 00642 // from within the code (via request_properties()) to 00643 // change the size and/or position of the window. 00644 // Returns true if the window is successfully changed, 00645 // or false if there was a problem. 00646 //////////////////////////////////////////////////////////////////// 00647 bool WinGraphicsWindow:: 00648 do_reshape_request(int x_origin, int y_origin, bool has_origin, 00649 int x_size, int y_size) { 00650 if (windisplay_cat.is_debug()) { 00651 windisplay_cat.debug() 00652 << "Got reshape request (" << x_origin << ", " << y_origin 00653 << ", " << has_origin << ", " << x_size << ", " << y_size << ")\n"; 00654 } 00655 if (!is_fullscreen()) { 00656 if (has_origin) { 00657 // A coordinate of -2 means to center the window in its client area. 00658 if (x_origin == -2) { 00659 x_origin = 0.5 * (_pipe->get_display_width() - x_size); 00660 } 00661 if (y_origin == -2) { 00662 y_origin = 0.5 * (_pipe->get_display_height() - y_size); 00663 } 00664 _properties.set_origin(x_origin, y_origin); 00665 00666 if (x_origin == -1 && y_origin == -1) { 00667 x_origin = 0; 00668 y_origin = 0; 00669 has_origin = false; 00670 } 00671 } 00672 00673 // Compute the appropriate size and placement for the window, 00674 // including decorations. 00675 RECT view_rect; 00676 SetRect(&view_rect, x_origin, y_origin, 00677 x_origin + x_size, y_origin + y_size); 00678 WINDOWINFO wi; 00679 GetWindowInfo(_hWnd, &wi); 00680 AdjustWindowRectEx(&view_rect, wi.dwStyle, FALSE, wi.dwExStyle); 00681 00682 UINT flags = SWP_NOZORDER | SWP_NOSENDCHANGING; 00683 00684 if (has_origin) { 00685 x_origin = view_rect.left; 00686 y_origin = view_rect.top; 00687 } else { 00688 x_origin = CW_USEDEFAULT; 00689 y_origin = CW_USEDEFAULT; 00690 flags |= SWP_NOMOVE; 00691 } 00692 00693 SetWindowPos(_hWnd, NULL, x_origin, y_origin, 00694 view_rect.right - view_rect.left, 00695 view_rect.bottom - view_rect.top, 00696 flags); 00697 00698 handle_reshape(); 00699 return true; 00700 } 00701 00702 // Resizing a fullscreen window is a little trickier. 00703 return do_fullscreen_resize(x_size, y_size); 00704 } 00705 00706 //////////////////////////////////////////////////////////////////// 00707 // Function: WinGraphicsWindow::handle_reshape 00708 // Access: Protected, Virtual 00709 // Description: Called in the window thread when the window size or 00710 // location is changed, this updates the properties 00711 // structure accordingly. 00712 //////////////////////////////////////////////////////////////////// 00713 void WinGraphicsWindow:: 00714 handle_reshape() { 00715 RECT view_rect; 00716 GetClientRect(_hWnd, &view_rect); 00717 ClientToScreen(_hWnd, (POINT*)&view_rect.left); // translates top,left pnt 00718 ClientToScreen(_hWnd, (POINT*)&view_rect.right); // translates right,bottom pnt 00719 00720 WindowProperties properties; 00721 properties.set_size((view_rect.right - view_rect.left), 00722 (view_rect.bottom - view_rect.top)); 00723 00724 // _props origin should reflect upper left of view rectangle 00725 properties.set_origin(view_rect.left, view_rect.top); 00726 00727 if (windisplay_cat.is_spam()) { 00728 windisplay_cat.spam() 00729 << "reshape to origin: (" << properties.get_x_origin() << "," 00730 << properties.get_y_origin() << "), size: (" << properties.get_x_size() 00731 << "," << properties.get_y_size() << ")\n"; 00732 } 00733 00734 adjust_z_order(); 00735 system_changed_properties(properties); 00736 } 00737 00738 //////////////////////////////////////////////////////////////////// 00739 // Function: WinGraphicsWindow::do_fullscreen_resize 00740 // Access: Protected, Virtual 00741 // Description: Called in the window thread to resize a fullscreen 00742 // window. 00743 //////////////////////////////////////////////////////////////////// 00744 bool WinGraphicsWindow:: 00745 do_fullscreen_resize(int x_size, int y_size) { 00746 HWND hDesktopWindow = GetDesktopWindow(); 00747 HDC scrnDC = GetDC(hDesktopWindow); 00748 DWORD dwFullScreenBitDepth = GetDeviceCaps(scrnDC, BITSPIXEL); 00749 ReleaseDC(hDesktopWindow, scrnDC); 00750 00751 // resize will always leave screen bitdepth unchanged 00752 00753 // allowing resizing of lowvidmem cards to > 640x480. why? I'll 00754 // assume check was already done by caller, so he knows what he 00755 // wants 00756 00757 DEVMODE dm; 00758 if (!find_acceptable_display_mode(x_size, y_size, 00759 dwFullScreenBitDepth, dm)) { 00760 windisplay_cat.error() 00761 << "window resize(" << x_size << ", " << y_size 00762 << ") failed, no compatible fullscreen display mode found!\n"; 00763 return false; 00764 } 00765 00766 // this causes WM_SIZE msg to be produced 00767 SetWindowPos(_hWnd, NULL, 0,0, x_size, y_size, 00768 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSENDCHANGING); 00769 int chg_result = ChangeDisplaySettings(&dm, CDS_FULLSCREEN); 00770 00771 if (chg_result != DISP_CHANGE_SUCCESSFUL) { 00772 windisplay_cat.error() 00773 << "resize ChangeDisplaySettings failed (error code: " 00774 << chg_result << ") for specified res: " 00775 << dm.dmPelsWidth << " x " << dm.dmPelsHeight 00776 << " x " << dm.dmBitsPerPel << ", " 00777 << dm.dmDisplayFrequency << " Hz\n"; 00778 return false; 00779 } 00780 00781 _fullscreen_display_mode = dm; 00782 00783 windisplay_cat.info() 00784 << "Resized fullscreen window to " << x_size << ", " << y_size 00785 << " bitdepth " << dwFullScreenBitDepth << ", " 00786 << dm.dmDisplayFrequency << "Hz\n"; 00787 00788 _properties.set_size(x_size, y_size); 00789 set_size_and_recalc(x_size, y_size); 00790 00791 return true; 00792 } 00793 00794 //////////////////////////////////////////////////////////////////// 00795 // Function: WinGraphicsWindow::do_fullscreen_switch 00796 // Access: Protected, Virtual 00797 // Description: Called in the set_properties_now function 00798 // to switch to fullscreen. 00799 //////////////////////////////////////////////////////////////////// 00800 bool WinGraphicsWindow:: 00801 do_fullscreen_switch() { 00802 if (!do_fullscreen_enable()) { 00803 // Couldn't get fullscreen. 00804 return false; 00805 } 00806 00807 DWORD window_style = make_style(true); 00808 SetWindowLong(_hWnd, GWL_STYLE, window_style); 00809 00810 WINDOW_METRICS metrics; 00811 bool has_origin; 00812 if (!calculate_metrics(true, window_style, metrics, has_origin)){ 00813 return false; 00814 } 00815 00816 SetWindowPos(_hWnd, HWND_NOTOPMOST, 0, 0, metrics.width, metrics.height, 00817 SWP_FRAMECHANGED | SWP_SHOWWINDOW); 00818 return true; 00819 } 00820 00821 //////////////////////////////////////////////////////////////////// 00822 // Function: WinGraphicsWindow::do_windowed_switch 00823 // Access: Protected, Virtual 00824 // Description: Called in the set_properties_now function 00825 // to switch to windowed mode. 00826 //////////////////////////////////////////////////////////////////// 00827 bool WinGraphicsWindow:: 00828 do_windowed_switch() { 00829 do_fullscreen_disable(); 00830 DWORD window_style = make_style(false); 00831 SetWindowLong(_hWnd, GWL_STYLE, window_style); 00832 00833 WINDOW_METRICS metrics; 00834 bool has_origin; 00835 00836 if (!calculate_metrics(false, window_style, metrics, has_origin)){ 00837 return false; 00838 } 00839 00840 // We send SWP_FRAMECHANGED so that the new styles are taken into account. 00841 // Also, we place the Windows at 0,0 to play safe until we decide how to 00842 // get Panda to remember the windowed origin. 00843 00844 SetWindowPos(_hWnd, HWND_NOTOPMOST, 0, 0, 00845 metrics.width, metrics.height, 00846 SWP_FRAMECHANGED | SWP_SHOWWINDOW); 00847 00848 return true; 00849 } 00850 00851 //////////////////////////////////////////////////////////////////// 00852 // Function: WinGraphicsWindow::reconsider_fullscreen_size 00853 // Access: Protected, Virtual 00854 // Description: Called before creating a fullscreen window to give 00855 // the driver a chance to adjust the particular 00856 // resolution request, if necessary. 00857 //////////////////////////////////////////////////////////////////// 00858 void WinGraphicsWindow:: 00859 reconsider_fullscreen_size(DWORD &, DWORD &, DWORD &) { 00860 } 00861 00862 //////////////////////////////////////////////////////////////////// 00863 // Function: WinGraphicsWindow::support_overlay_window 00864 // Access: Protected, Virtual 00865 // Description: Some windows graphics contexts (e.g. DirectX) 00866 // require special support to enable the displaying of 00867 // an overlay window (particularly the IME window) over 00868 // the fullscreen graphics window. This is a hook for 00869 // the window to enable or disable that mode when 00870 // necessary. 00871 //////////////////////////////////////////////////////////////////// 00872 void WinGraphicsWindow:: 00873 support_overlay_window(bool) { 00874 } 00875 00876 //////////////////////////////////////////////////////////////////// 00877 // Function: WinGraphicsWindow::make_style 00878 // Access: Private 00879 // Description: Constructs a dwStyle for the specified mode, 00880 // be it windowed or fullscreen. 00881 //////////////////////////////////////////////////////////////////// 00882 DWORD WinGraphicsWindow:: 00883 make_style(bool fullscreen) { 00884 // from MSDN: 00885 // An OpenGL window has its own pixel format. Because of this, only 00886 // device contexts retrieved for the client area of an OpenGL 00887 // window are allowed to draw into the window. As a result, an 00888 // OpenGL window should be created with the WS_CLIPCHILDREN and 00889 // WS_CLIPSIBLINGS styles. Additionally, the window class attribute 00890 // should not include the CS_PARENTDC style. 00891 00892 DWORD window_style = WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; 00893 00894 if (fullscreen){ 00895 window_style |= WS_SYSMENU; 00896 } else if (!_properties.get_undecorated()) { 00897 window_style |= (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX); 00898 00899 if (!_properties.get_fixed_size()) { 00900 window_style |= (WS_SIZEBOX | WS_MAXIMIZEBOX); 00901 } else { 00902 window_style |= WS_BORDER; 00903 } 00904 } 00905 return window_style; 00906 } 00907 00908 //////////////////////////////////////////////////////////////////// 00909 // Function: WinGraphicsWindow::calculate_metrics 00910 // Access: Private 00911 // Description: Calculates the metrics for the specified mode, 00912 // be it windowed or fullscreen. 00913 //////////////////////////////////////////////////////////////////// 00914 bool WinGraphicsWindow:: 00915 calculate_metrics(bool fullscreen, DWORD window_style, WINDOW_METRICS &metrics, 00916 bool &has_origin) { 00917 metrics.x = 0; 00918 metrics.y = 0; 00919 has_origin = _properties.has_origin(); 00920 if (!fullscreen && has_origin) { 00921 metrics.x = _properties.get_x_origin(); 00922 metrics.y = _properties.get_y_origin(); 00923 00924 // A coordinate of -2 means to center the window in its client area. 00925 if (metrics.x == -2) { 00926 metrics.x = 0.5 * (_pipe->get_display_width() - _properties.get_x_origin()); 00927 } 00928 if (metrics.y == -2) { 00929 metrics.y = 0.5 * (_pipe->get_display_height() - _properties.get_y_origin()); 00930 } 00931 _properties.set_origin(metrics.x, metrics.y); 00932 00933 if (metrics.x == -1 && metrics.y == -1) { 00934 metrics.x = 0; 00935 metrics.y = 0; 00936 has_origin = false; 00937 } 00938 } 00939 00940 metrics.width = _properties.get_x_size(); 00941 metrics.height = _properties.get_y_size(); 00942 00943 if (!fullscreen){ 00944 RECT win_rect; 00945 SetRect(&win_rect, metrics.x, metrics.y, 00946 metrics.x + metrics.width, metrics.y + metrics.height); 00947 00948 // Compute window size based on desired client area size 00949 if (!AdjustWindowRect(&win_rect, window_style, FALSE)) { 00950 windisplay_cat.error() 00951 << "AdjustWindowRect failed!" << endl; 00952 return false; 00953 } 00954 00955 if (has_origin) { 00956 metrics.x = win_rect.left; 00957 metrics.y = win_rect.top; 00958 } else { 00959 metrics.x = CW_USEDEFAULT; 00960 metrics.y = CW_USEDEFAULT; 00961 } 00962 metrics.width = win_rect.right - win_rect.left; 00963 metrics.height = win_rect.bottom - win_rect.top; 00964 } 00965 00966 return true; 00967 } 00968 00969 //////////////////////////////////////////////////////////////////// 00970 // Function: WinGraphicsWindow::open_graphic_window 00971 // Access: Private 00972 // Description: Creates a regular or fullscreen window. 00973 //////////////////////////////////////////////////////////////////// 00974 bool WinGraphicsWindow:: 00975 open_graphic_window(bool fullscreen) { 00976 DWORD window_style = make_style(fullscreen); 00977 00978 wstring title; 00979 if (_properties.has_title()) { 00980 TextEncoder encoder; 00981 title = encoder.decode_text(_properties.get_title()); 00982 } 00983 00984 if (!_properties.has_size()) { 00985 //Just fill in a conservative default size if one isn't specified. 00986 _properties.set_size(640, 480); 00987 } 00988 00989 WINDOW_METRICS metrics; 00990 bool has_origin; 00991 if (!calculate_metrics(fullscreen, window_style, metrics, has_origin)){ 00992 return false; 00993 } 00994 00995 const WindowClass &wclass = register_window_class(_properties); 00996 HINSTANCE hinstance = GetModuleHandle(NULL); 00997 00998 _hparent = NULL; 00999 01000 if (!fullscreen){ 01001 WindowHandle *window_handle = _properties.get_parent_window(); 01002 if (window_handle != NULL) { 01003 windisplay_cat.info() 01004 << "Got parent_window " << *window_handle << "\n"; 01005 WindowHandle::OSHandle *os_handle = window_handle->get_os_handle(); 01006 if (os_handle != NULL) { 01007 windisplay_cat.info() 01008 << "os_handle type " << os_handle->get_type() << "\n"; 01009 01010 if (os_handle->is_of_type(NativeWindowHandle::WinHandle::get_class_type())) { 01011 NativeWindowHandle::WinHandle *win_handle = DCAST(NativeWindowHandle::WinHandle, os_handle); 01012 _hparent = win_handle->get_handle(); 01013 } else if (os_handle->is_of_type(NativeWindowHandle::IntHandle::get_class_type())) { 01014 NativeWindowHandle::IntHandle *int_handle = DCAST(NativeWindowHandle::IntHandle, os_handle); 01015 _hparent = (HWND)int_handle->get_handle(); 01016 } 01017 } 01018 } 01019 _parent_window_handle = window_handle; 01020 } else { 01021 _parent_window_handle = NULL; 01022 } 01023 01024 if (!_hparent) { // This can be a regular window or a fullscreen window 01025 _hWnd = CreateWindowW(wclass._name.c_str(), title.c_str(), window_style, 01026 metrics.x, metrics.y, 01027 metrics.width, 01028 metrics.height, 01029 NULL, NULL, hinstance, 0); 01030 } else { // This is a regular window with a parent 01031 int x_origin = 0; 01032 int y_origin = 0; 01033 01034 if (!fullscreen && has_origin) { 01035 x_origin = _properties.get_x_origin(); 01036 y_origin = _properties.get_y_origin(); 01037 } 01038 01039 _hWnd = CreateWindowW(wclass._name.c_str(), title.c_str(), 01040 WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS , 01041 x_origin, y_origin, 01042 _properties.get_x_size(), _properties.get_y_size(), 01043 _hparent, NULL, hinstance, 0); 01044 01045 if (_hWnd) { 01046 // join our keyboard state with the parents 01047 01048 // Actually, let's not. Is there really any reason to do this? 01049 // It causes problems with the browser plugin--it deadlocks when 01050 // the parent process is waiting on the child process. 01051 //AttachThreadInput(GetWindowThreadProcessId(_hparent,NULL), GetCurrentThreadId(),TRUE); 01052 01053 WindowProperties properties; 01054 properties.set_foreground(true); 01055 system_changed_properties(properties); 01056 } 01057 } 01058 01059 if (!_hWnd) { 01060 windisplay_cat.error() 01061 << "CreateWindow() failed!" << endl; 01062 show_error_message(); 01063 return false; 01064 } 01065 01066 // I'd prefer to CreateWindow after DisplayChange in case it messes 01067 // up GL somehow, but I need the window's black background to cover 01068 // up the desktop during the mode change. 01069 01070 if (fullscreen){ 01071 if (!do_fullscreen_enable()){ 01072 return false; 01073 } 01074 } 01075 01076 return true; 01077 } 01078 01079 //////////////////////////////////////////////////////////////////// 01080 // Function: WinGraphicsWindow::do_fullscreen_enable 01081 // Access: Private 01082 // Description: This is a low-level function that just puts Windows 01083 // in fullscreen mode. Not to confuse with 01084 // do_fullscreen_switch(). 01085 //////////////////////////////////////////////////////////////////// 01086 bool WinGraphicsWindow:: 01087 do_fullscreen_enable() { 01088 01089 HWND hDesktopWindow = GetDesktopWindow(); 01090 HDC scrnDC = GetDC(hDesktopWindow); 01091 DWORD cur_bitdepth = GetDeviceCaps(scrnDC, BITSPIXEL); 01092 // DWORD drvr_ver = GetDeviceCaps(scrnDC, DRIVERVERSION); 01093 // DWORD cur_scrnwidth = GetDeviceCaps(scrnDC, HORZRES); 01094 // DWORD cur_scrnheight = GetDeviceCaps(scrnDC, VERTRES); 01095 ReleaseDC(hDesktopWindow, scrnDC); 01096 01097 DWORD dwWidth = _properties.get_x_size(); 01098 DWORD dwHeight = _properties.get_y_size(); 01099 DWORD dwFullScreenBitDepth = cur_bitdepth; 01100 01101 DEVMODE dm; 01102 reconsider_fullscreen_size(dwWidth, dwHeight, dwFullScreenBitDepth); 01103 if (!find_acceptable_display_mode(dwWidth, dwHeight, dwFullScreenBitDepth, dm)) { 01104 windisplay_cat.error() 01105 << "Videocard has no supported display resolutions at specified res (" 01106 << dwWidth << " x " << dwHeight << " x " << dwFullScreenBitDepth <<")\n"; 01107 return false; 01108 } 01109 01110 dm.dmPelsWidth = dwWidth; 01111 dm.dmPelsHeight = dwHeight; 01112 dm.dmBitsPerPel = dwFullScreenBitDepth; 01113 int chg_result = ChangeDisplaySettings(&dm, CDS_FULLSCREEN); 01114 01115 if (chg_result != DISP_CHANGE_SUCCESSFUL) { 01116 windisplay_cat.error() 01117 << "ChangeDisplaySettings failed (error code: " 01118 << chg_result << ") for specified res: " 01119 << dm.dmPelsWidth << " x " << dm.dmPelsHeight 01120 << " x " << dm.dmBitsPerPel << ", " 01121 << dm.dmDisplayFrequency << " Hz\n"; 01122 return false; 01123 } 01124 01125 _fullscreen_display_mode = dm; 01126 01127 _properties.set_origin(0, 0); 01128 _properties.set_size(dwWidth, dwHeight); 01129 01130 return true; 01131 01132 } 01133 01134 //////////////////////////////////////////////////////////////////// 01135 // Function: WinGraphicsWindow::do_fullscreen_disable 01136 // Access: Private 01137 // Description: This is a low-level function that just gets Windows 01138 // out of fullscreen mode. Not to confuse with 01139 // do_windowed_switch(). 01140 //////////////////////////////////////////////////////////////////// 01141 bool WinGraphicsWindow:: 01142 do_fullscreen_disable() { 01143 int chg_result = ChangeDisplaySettings(NULL, 0x0); 01144 if (chg_result != DISP_CHANGE_SUCCESSFUL) { 01145 windisplay_cat.warning() 01146 << "ChangeDisplaySettings failed to restore Windowed mode\n"; 01147 return false; 01148 } 01149 return true; 01150 } 01151 01152 //////////////////////////////////////////////////////////////////// 01153 // Function: WinGraphicsWindow::adjust_z_order 01154 // Access: Private 01155 // Description: Adjusts the Z-order of a window after it has been 01156 // moved. 01157 //////////////////////////////////////////////////////////////////// 01158 void WinGraphicsWindow:: 01159 adjust_z_order() { 01160 WindowProperties::ZOrder z_order = _properties.get_z_order(); 01161 adjust_z_order(z_order, z_order); 01162 } 01163 01164 //////////////////////////////////////////////////////////////////// 01165 // Function: WinGraphicsWindow::adjust_z_order 01166 // Access: Private 01167 // Description: Adjusts the Z-order of a window after it has been 01168 // moved. 01169 //////////////////////////////////////////////////////////////////// 01170 void WinGraphicsWindow:: 01171 adjust_z_order(WindowProperties::ZOrder last_z_order, 01172 WindowProperties::ZOrder this_z_order) { 01173 HWND order; 01174 bool do_change = false; 01175 01176 switch (this_z_order) { 01177 case WindowProperties::Z_bottom: 01178 order = HWND_BOTTOM; 01179 do_change = true; 01180 break; 01181 01182 case WindowProperties::Z_normal: 01183 if ((last_z_order != WindowProperties::Z_normal) && 01184 // If we aren't changing the window order, don't move it to 01185 // the top. 01186 (last_z_order != WindowProperties::Z_bottom || 01187 _properties.get_foreground()) 01188 // If the window was previously on the bottom, but it doesn't 01189 // have focus now, don't move it to the top; it will get moved 01190 // the next time we get focus. 01191 ) { 01192 order = HWND_TOP; 01193 do_change = true; 01194 } 01195 break; 01196 01197 case WindowProperties::Z_top: 01198 order = HWND_TOPMOST; 01199 do_change = true; 01200 break; 01201 } 01202 if (do_change) { 01203 BOOL result = SetWindowPos(_hWnd, order, 0,0,0,0, 01204 SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE); 01205 if (!result) { 01206 windisplay_cat.warning() 01207 << "SetWindowPos failed.\n"; 01208 } 01209 } 01210 } 01211 01212 //////////////////////////////////////////////////////////////////// 01213 // Function: WinGraphicsWindow::track_mouse_leaving 01214 // Access: Private 01215 // Description: Intended to be called whenever mouse motion is 01216 // detected within the window, this indicates that the 01217 // mouse is within the window and tells Windows that we 01218 // want to be told when the mouse leaves the window. 01219 //////////////////////////////////////////////////////////////////// 01220 void WinGraphicsWindow:: 01221 track_mouse_leaving(HWND hwnd) { 01222 // Note: could use _TrackMouseEvent in comctrl32.dll (part of IE 01223 // 3.0+) which emulates TrackMouseEvent on w95, but that requires 01224 // another 500K of memory to hold that DLL, which is lame just to 01225 // support w95, which probably has other issues anyway 01226 WinGraphicsPipe *winpipe; 01227 DCAST_INTO_V(winpipe, _pipe); 01228 01229 if (winpipe->_pfnTrackMouseEvent != NULL) { 01230 TRACKMOUSEEVENT tme = { 01231 sizeof(TRACKMOUSEEVENT), 01232 TME_LEAVE, 01233 hwnd, 01234 0 01235 }; 01236 01237 // tell win32 to post WM_MOUSELEAVE msgs 01238 BOOL bSucceeded = winpipe->_pfnTrackMouseEvent(&tme); 01239 01240 if ((!bSucceeded) && windisplay_cat.is_debug()) { 01241 windisplay_cat.debug() 01242 << "TrackMouseEvent failed!, LastError=" << GetLastError() << endl; 01243 } 01244 01245 _tracking_mouse_leaving = true; 01246 } 01247 } 01248 01249 //////////////////////////////////////////////////////////////////// 01250 // Function: WinGraphicsWindow::set_focus 01251 // Access: Private 01252 // Description: Attempts to set this window as the "focus" window, so 01253 // that keyboard events come here. 01254 //////////////////////////////////////////////////////////////////// 01255 void WinGraphicsWindow:: 01256 set_focus() { 01257 if (SetFocus(_hWnd) == NULL && GetLastError() != 0) { 01258 // If the SetFocus() request failed, maybe we're running in the 01259 // plugin environment on Vista, with UAC enabled. In this case, 01260 // we're not allowed to assign focus to the Panda window for some 01261 // stupid reason. So instead, we have to ask the parent window 01262 // (in the browser process) to proxy our keyboard events for us. 01263 if (_parent_window_handle != NULL && _window_handle != NULL) { 01264 _parent_window_handle->request_keyboard_focus(_window_handle); 01265 } else { 01266 // Otherwise, something is wrong. 01267 windisplay_cat.error() 01268 << "SetFocus failed: " << GetLastError() << "\n"; 01269 } 01270 } 01271 } 01272 01273 //////////////////////////////////////////////////////////////////// 01274 // Function: WinGraphicsWindow::receive_windows_message 01275 // Access: Public 01276 // Description: This is called to receive a keyboard event generated 01277 // by proxy by another window in a parent process. This 01278 // hacky system is used in the web plugin system to 01279 // allow the Panda window to receive keyboard events on 01280 // Vista, which doesn't allow the Panda window to set 01281 // keyboard focus to itself. 01282 //////////////////////////////////////////////////////////////////// 01283 void WinGraphicsWindow:: 01284 receive_windows_message(unsigned int msg, int wparam, int lparam) { 01285 // Well, we'll just deliver this directly to window_proc(), 01286 // supplying our own window handle. For the most part, we don't 01287 // care about the window handle anyway, but this might become an 01288 // issue for the IME. TODO: investigate IME issues. 01289 01290 window_proc(_hWnd, msg, wparam, lparam); 01291 } 01292 01293 //////////////////////////////////////////////////////////////////// 01294 // Function: WinGraphicsWindow::window_proc 01295 // Access: Public, Virtual 01296 // Description: This is the nonstatic window_proc function. It is 01297 // called to handle window events for this particular 01298 // window. 01299 //////////////////////////////////////////////////////////////////// 01300 LONG WinGraphicsWindow:: 01301 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { 01302 if (windisplay_cat.is_spam()) { 01303 windisplay_cat.spam() 01304 << ClockObject::get_global_clock()->get_real_time() 01305 << " window_proc(" << (void *)this << ", " << hwnd << ", " 01306 << msg << ", " << wparam << ", " << lparam << ")\n"; 01307 } 01308 WindowProperties properties; 01309 int button = -1; 01310 01311 switch (msg) { 01312 case WM_MOUSEMOVE: 01313 if (!_tracking_mouse_leaving) { 01314 // need to re-call TrackMouseEvent every time mouse re-enters window 01315 track_mouse_leaving(hwnd); 01316 } 01317 set_cursor_in_window(); 01318 if(handle_mouse_motion(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam)))) 01319 return 0; 01320 break; 01321 01322 case WM_INPUT: 01323 handle_raw_input((HRAWINPUT)lparam); 01324 break; 01325 01326 case WM_MOUSELEAVE: 01327 _tracking_mouse_leaving = false; 01328 handle_mouse_exit(); 01329 set_cursor_out_of_window(); 01330 break; 01331 01332 case WM_CREATE: 01333 { 01334 track_mouse_leaving(hwnd); 01335 ClearToBlack(hwnd, _properties); 01336 01337 POINT cpos; 01338 GetCursorPos(&cpos); 01339 ScreenToClient(hwnd, &cpos); 01340 RECT clientRect; 01341 GetClientRect(hwnd, &clientRect); 01342 if (PtInRect(&clientRect,cpos)) { 01343 set_cursor_in_window(); // should window focus be true as well? 01344 } else { 01345 set_cursor_out_of_window(); 01346 } 01347 } 01348 break; 01349 01350 /* 01351 case WM_SHOWWINDOW: 01352 // You'd think WM_SHOWWINDOW would be just the thing for embedded 01353 // windows, but it turns out it's not sent to the child windows 01354 // when the parent is minimized. I guess it's only sent for an 01355 // explicit call to ShowWindow, phooey. 01356 { 01357 if (windisplay_cat.is_debug()) { 01358 windisplay_cat.debug() 01359 << "WM_SHOWWINDOW: " << hwnd << ", " << wparam << "\n"; 01360 } 01361 if (wparam) { 01362 // Window is being shown. 01363 properties.set_minimized(false); 01364 } else { 01365 // Window is being hidden. 01366 properties.set_minimized(true); 01367 } 01368 system_changed_properties(properties); 01369 } 01370 break; 01371 */ 01372 01373 case WM_CLOSE: 01374 // This is a message from the system indicating that the user 01375 // has requested to close the window (e.g. alt-f4). 01376 { 01377 string close_request_event = get_close_request_event(); 01378 if (!close_request_event.empty()) { 01379 // In this case, the app has indicated a desire to intercept 01380 // the request and process it directly. 01381 throw_event(close_request_event); 01382 return 0; 01383 01384 } else { 01385 // In this case, the default case, the app does not intend 01386 // to service the request, so we do by closing the window. 01387 close_window(); 01388 properties.set_open(false); 01389 system_changed_properties(properties); 01390 01391 // TODO: make sure we release the GSG properly. 01392 } 01393 } 01394 break; 01395 01396 case WM_CHILDACTIVATE: 01397 if (windisplay_cat.is_debug()) { 01398 windisplay_cat.debug() 01399 << "WM_CHILDACTIVATE: " << hwnd << "\n"; 01400 } 01401 break; 01402 01403 case WM_ACTIVATE: 01404 if (windisplay_cat.is_debug()) { 01405 windisplay_cat.debug() 01406 << "WM_ACTIVATE: " << hwnd << ", " << wparam << ", " << lparam << "\n"; 01407 } 01408 properties.set_minimized((wparam & 0xffff0000) != 0); 01409 if ((wparam & 0xffff) != WA_INACTIVE) 01410 { 01411 properties.set_foreground(true); 01412 if (is_fullscreen()) 01413 { 01414 // When a fullscreen window goes active, it automatically gets 01415 // un-minimized. 01416 int chg_result = 01417 ChangeDisplaySettings(&_fullscreen_display_mode, CDS_FULLSCREEN); 01418 if (chg_result != DISP_CHANGE_SUCCESSFUL) { 01419 const DEVMODE &dm = _fullscreen_display_mode; 01420 windisplay_cat.error() 01421 << "restore ChangeDisplaySettings failed (error code: " 01422 << chg_result << ") for specified res: " 01423 << dm.dmPelsWidth << " x " << dm.dmPelsHeight 01424 << " x " << dm.dmBitsPerPel << ", " 01425 << dm.dmDisplayFrequency << " Hz\n"; 01426 } 01427 01428 GdiFlush(); 01429 SetWindowPos(_hWnd, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOOWNERZORDER); 01430 fullscreen_restored(properties); 01431 } 01432 } 01433 else 01434 { 01435 properties.set_foreground(false); 01436 if (is_fullscreen()) 01437 { 01438 // When a fullscreen window goes inactive, it automatically 01439 // gets minimized. 01440 properties.set_minimized(true); 01441 01442 // It seems order is important here. We must minimize the 01443 // window before restoring the display settings, or risk 01444 // losing the graphics context. 01445 ShowWindow(_hWnd, SW_MINIMIZE); 01446 GdiFlush(); 01447 do_fullscreen_disable(); 01448 fullscreen_minimized(properties); 01449 } 01450 } 01451 01452 adjust_z_order(); 01453 system_changed_properties(properties); 01454 break; 01455 01456 case WM_SIZE: 01457 if (windisplay_cat.is_debug()) { 01458 windisplay_cat.debug() 01459 << "WM_SIZE: " << hwnd << ", " << wparam << "\n"; 01460 } 01461 // for maximized, unmaximize, need to call resize code 01462 // artificially since no WM_EXITSIZEMOVE is generated. 01463 if (wparam == SIZE_MAXIMIZED) { 01464 _maximized = true; 01465 handle_reshape(); 01466 01467 } else if (wparam == SIZE_RESTORED && _maximized) { 01468 // SIZE_RESTORED might mean we restored to its original size 01469 // before the maximize, but it might also be called while the 01470 // user is resizing the window by hand. Checking the _maximized 01471 // flag that we set above allows us to differentiate the two 01472 // cases. 01473 _maximized = false; 01474 handle_reshape(); 01475 } 01476 break; 01477 01478 case WM_EXITSIZEMOVE: 01479 handle_reshape(); 01480 break; 01481 01482 case WM_WINDOWPOSCHANGED: 01483 adjust_z_order(); 01484 break; 01485 01486 case WM_PAINT: 01487 // In response to WM_PAINT, we check to see if there are any 01488 // update regions at all; if there are, we declare the window 01489 // exposed. This is used to implement !_unexposed_draw. 01490 if (GetUpdateRect(_hWnd, NULL, false)) { 01491 if (windisplay_cat.is_spam()) { 01492 windisplay_cat.spam() 01493 << "Got update regions: " << this << "\n"; 01494 } 01495 _got_expose_event = true; 01496 } 01497 break; 01498 01499 case WM_LBUTTONDOWN: 01500 if (_lost_keypresses) { 01501 resend_lost_keypresses(); 01502 } 01503 SetCapture(hwnd); 01504 _input_devices[0].set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam))); 01505 _input_devices[0].button_down(MouseButton::button(0), get_message_time()); 01506 01507 // A button-click in the window means to grab the keyboard focus. 01508 set_focus(); 01509 return 0; 01510 01511 case WM_MBUTTONDOWN: 01512 if (_lost_keypresses) { 01513 resend_lost_keypresses(); 01514 } 01515 SetCapture(hwnd); 01516 _input_devices[0].set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam))); 01517 _input_devices[0].button_down(MouseButton::button(1), get_message_time()); 01518 // A button-click in the window means to grab the keyboard focus. 01519 set_focus(); 01520 return 0; 01521 01522 case WM_RBUTTONDOWN: 01523 if (_lost_keypresses) { 01524 resend_lost_keypresses(); 01525 } 01526 SetCapture(hwnd); 01527 _input_devices[0].set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam))); 01528 _input_devices[0].button_down(MouseButton::button(2), get_message_time()); 01529 // A button-click in the window means to grab the keyboard focus. 01530 set_focus(); 01531 return 0; 01532 01533 case WM_XBUTTONDOWN: 01534 { 01535 if (_lost_keypresses) { 01536 resend_lost_keypresses(); 01537 } 01538 SetCapture(hwnd); 01539 int whichButton = GET_XBUTTON_WPARAM(wparam); 01540 _input_devices[0].set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam))); 01541 if (whichButton == XBUTTON1) { 01542 _input_devices[0].button_down(MouseButton::button(3), get_message_time()); 01543 } else if (whichButton == XBUTTON2) { 01544 _input_devices[0].button_down(MouseButton::button(4), get_message_time()); 01545 } 01546 } 01547 return 0; 01548 01549 case WM_LBUTTONUP: 01550 if (_lost_keypresses) { 01551 resend_lost_keypresses(); 01552 } 01553 ReleaseCapture(); 01554 _input_devices[0].button_up(MouseButton::button(0), get_message_time()); 01555 return 0; 01556 01557 case WM_MBUTTONUP: 01558 if (_lost_keypresses) { 01559 resend_lost_keypresses(); 01560 } 01561 ReleaseCapture(); 01562 _input_devices[0].button_up(MouseButton::button(1), get_message_time()); 01563 return 0; 01564 01565 case WM_RBUTTONUP: 01566 if (_lost_keypresses) { 01567 resend_lost_keypresses(); 01568 } 01569 ReleaseCapture(); 01570 _input_devices[0].button_up(MouseButton::button(2), get_message_time()); 01571 return 0; 01572 01573 case WM_XBUTTONUP: 01574 { 01575 if (_lost_keypresses) { 01576 resend_lost_keypresses(); 01577 } 01578 ReleaseCapture(); 01579 int whichButton = GET_XBUTTON_WPARAM(wparam); 01580 if (whichButton == XBUTTON1) { 01581 _input_devices[0].button_up(MouseButton::button(3), get_message_time()); 01582 } else if (whichButton == XBUTTON2) { 01583 _input_devices[0].button_up(MouseButton::button(4), get_message_time()); 01584 } 01585 } 01586 return 0; 01587 01588 case WM_MOUSEWHEEL: 01589 { 01590 int delta = GET_WHEEL_DELTA_WPARAM(wparam); 01591 01592 POINT point; 01593 GetCursorPos(&point); 01594 ScreenToClient(hwnd, &point); 01595 double time = get_message_time(); 01596 01597 if (delta >= 0) { 01598 while (delta > 0) { 01599 handle_keypress(MouseButton::wheel_up(), point.x, point.y, time); 01600 handle_keyrelease(MouseButton::wheel_up(), time); 01601 delta -= WHEEL_DELTA; 01602 } 01603 } else { 01604 while (delta < 0) { 01605 handle_keypress(MouseButton::wheel_down(), point.x, point.y, time); 01606 handle_keyrelease(MouseButton::wheel_down(), time); 01607 delta += WHEEL_DELTA; 01608 } 01609 } 01610 return 0; 01611 } 01612 break; 01613 01614 01615 case WM_IME_SETCONTEXT: 01616 if (!ime_hide) 01617 break; 01618 01619 windisplay_cat.debug() << "hwnd = " << hwnd << " and GetFocus = " << GetFocus() << endl; 01620 _ime_hWnd = ImmGetDefaultIMEWnd(hwnd); 01621 if (::SendMessage(_ime_hWnd, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0)) 01622 //if (::SendMessage(hwnd, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0)) 01623 windisplay_cat.debug() << "SendMessage failed for " << _ime_hWnd << endl; 01624 else 01625 windisplay_cat.debug() << "SendMessage Succeeded for " << _ime_hWnd << endl; 01626 01627 windisplay_cat.debug() << "wparam is " << wparam << ", lparam is " << lparam << endl; 01628 lparam &= ~ISC_SHOWUIALL; 01629 if (ImmIsUIMessage(_ime_hWnd, msg, wparam, lparam)) 01630 windisplay_cat.debug() << "wparam is " << wparam << ", lparam is " << lparam << endl; 01631 01632 break; 01633 01634 01635 case WM_IME_NOTIFY: 01636 if (wparam == IMN_SETOPENSTATUS) { 01637 HIMC hIMC = ImmGetContext(hwnd); 01638 nassertr(hIMC != 0, 0); 01639 _ime_open = (ImmGetOpenStatus(hIMC) != 0); 01640 if (!_ime_open) { 01641 _ime_active = false; // Sanity enforcement. 01642 } 01643 if (ime_hide) { 01644 //if (0) { 01645 COMPOSITIONFORM comf; 01646 CANDIDATEFORM canf; 01647 ImmGetCompositionWindow(hIMC, &comf); 01648 ImmGetCandidateWindow(hIMC, 0, &canf); 01649 windisplay_cat.debug() << 01650 "comf style " << comf.dwStyle << 01651 " comf point: x" << comf.ptCurrentPos.x << ",y " << comf.ptCurrentPos.y << 01652 " comf rect: l " << comf.rcArea.left << ",t " << comf.rcArea.top << ",r " << 01653 comf.rcArea.right << ",b " << comf.rcArea.bottom << endl; 01654 windisplay_cat.debug() << 01655 "canf style " << canf.dwStyle << 01656 " canf point: x" << canf.ptCurrentPos.x << ",y " << canf.ptCurrentPos.y << 01657 " canf rect: l " << canf.rcArea.left << ",t " << canf.rcArea.top << ",r " << 01658 canf.rcArea.right << ",b " << canf.rcArea.bottom << endl; 01659 comf.dwStyle = CFS_POINT; 01660 comf.ptCurrentPos.x = 2000; 01661 comf.ptCurrentPos.y = 2000; 01662 01663 canf.dwStyle = CFS_EXCLUDE; 01664 canf.dwIndex = 0; 01665 canf.ptCurrentPos.x = 0; 01666 canf.ptCurrentPos.y = 0; 01667 canf.rcArea.left = 0; 01668 canf.rcArea.top = 0; 01669 canf.rcArea.right = 640; 01670 canf.rcArea.bottom = 480; 01671 01672 #if 0 01673 comf.rcArea.left = 200; 01674 comf.rcArea.top = 200; 01675 comf.rcArea.right = 0; 01676 comf.rcArea.bottom = 0; 01677 #endif 01678 01679 if (ImmSetCompositionWindow(hIMC, &comf)) 01680 windisplay_cat.debug() << "set composition form: success\n"; 01681 for (int i=0; i<3; ++i) { 01682 if (ImmSetCandidateWindow(hIMC, &canf)) 01683 windisplay_cat.debug() << "set candidate form: success\n"; 01684 canf.dwIndex++; 01685 } 01686 } 01687 01688 ImmReleaseContext(hwnd, hIMC); 01689 } 01690 break; 01691 01692 case WM_IME_STARTCOMPOSITION: 01693 support_overlay_window(true); 01694 _ime_active = true; 01695 break; 01696 01697 case WM_IME_ENDCOMPOSITION: 01698 support_overlay_window(false); 01699 _ime_active = false; 01700 01701 if (ime_aware) { 01702 wstring ws; 01703 _input_devices[0].candidate(ws, 0, 0, 0); 01704 } 01705 01706 break; 01707 01708 case WM_IME_COMPOSITION: 01709 if (ime_aware) { 01710 01711 // If the ime window is not marked as active at this point, we 01712 // must be in the process of closing it down (in close_ime), and 01713 // we don't want to send the current composition string in that 01714 // case. But we do need to return 0 to tell windows not to try 01715 // to send the composition string through WM_CHAR messages. 01716 if (!_ime_active) { 01717 return 0; 01718 } 01719 01720 HIMC hIMC = ImmGetContext(hwnd); 01721 nassertr(hIMC != 0, 0); 01722 01723 DWORD result_size = 0; 01724 static const int ime_buffer_size = 256; 01725 static const int ime_buffer_size_bytes = ime_buffer_size / sizeof(wchar_t); 01726 wchar_t ime_buffer[ime_buffer_size]; 01727 size_t cursor_pos, delta_start; 01728 01729 if (lparam & GCS_RESULTSTR) { 01730 result_size = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, 01731 ime_buffer, ime_buffer_size_bytes); 01732 size_t num_chars = result_size / sizeof(wchar_t); 01733 for (size_t i = 0; i < num_chars; ++i) { 01734 _input_devices[0].keystroke(ime_buffer[i]); 01735 } 01736 } 01737 01738 if (lparam & GCS_COMPSTR) { 01739 result_size = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, NULL, 0); 01740 cursor_pos = result_size & 0xffff; 01741 01742 result_size = ImmGetCompositionStringW(hIMC, GCS_DELTASTART, NULL, 0); 01743 delta_start = result_size & 0xffff; 01744 result_size = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, ime_buffer, ime_buffer_size); 01745 size_t num_chars = result_size / sizeof(wchar_t); 01746 01747 _input_devices[0].candidate(wstring(ime_buffer, num_chars), 01748 min(cursor_pos, delta_start), 01749 max(cursor_pos, delta_start), 01750 cursor_pos); 01751 } 01752 ImmReleaseContext(hwnd, hIMC); 01753 } 01754 break; 01755 01756 case WM_CHAR: 01757 // Ignore WM_CHAR messages if we have the IME open, since 01758 // everything will come in through WM_IME_COMPOSITION. (It's 01759 // supposed to come in through WM_CHAR, too, but there seems to 01760 // be a bug in Win2000 in that it only sends question mark 01761 // characters through here.) 01762 01763 // Actually, probably that "bug" was due to the fact that we were 01764 // previously using the ANSI versions of RegisterClass etc., in 01765 // which case the actual value passed to WM_CHAR seems to be 01766 // poorly defined. Now we are using RegisterClassW etc., which 01767 // means WM_CHAR is absolutely supposed to be utf-16. 01768 if (!_ime_open) { 01769 _input_devices[0].keystroke(wparam); 01770 } 01771 break; 01772 01773 case WM_SYSKEYDOWN: 01774 if (_lost_keypresses) { 01775 resend_lost_keypresses(); 01776 } 01777 if (windisplay_cat.is_debug()) { 01778 windisplay_cat.debug() 01779 << "syskeydown: " << wparam << " (" << lookup_key(wparam) << ")\n"; 01780 } 01781 { 01782 // Alt and F10 are sent as WM_SYSKEYDOWN instead of WM_KEYDOWN 01783 // want to use defwindproc on Alt syskey so std windows cmd 01784 // Alt-F4 works, etc 01785 POINT point; 01786 GetCursorPos(&point); 01787 ScreenToClient(hwnd, &point); 01788 handle_keypress(lookup_key(wparam), point.x, point.y, 01789 get_message_time()); 01790 01791 // wparam does not contain left/right information for SHIFT, 01792 // CONTROL, or ALT, so we have to track their status and do 01793 // the right thing. We'll send the left/right specific key 01794 // event along with the general key event. 01795 // 01796 // Key repeating is not being handled consistently for LALT 01797 // and RALT, but from comments below, it's only being handled 01798 // the way it is for backspace, so we'll leave it as is. 01799 if (wparam == VK_MENU) { 01800 if ((GetKeyState(VK_LMENU) & 0x8000) != 0 && ! _lalt_down) { 01801 handle_keypress(KeyboardButton::lalt(), point.x, point.y, 01802 get_message_time()); 01803 _lalt_down = true; 01804 } 01805 if ((GetKeyState(VK_RMENU) & 0x8000) != 0 && ! _ralt_down) { 01806 handle_keypress(KeyboardButton::ralt(), point.x, point.y, 01807 get_message_time()); 01808 _ralt_down = true; 01809 } 01810 } 01811 if (wparam == VK_F10) { 01812 // bypass default windproc F10 behavior (it activates the main 01813 // menu, but we have none) 01814 return 0; 01815 } 01816 } 01817 break; 01818 01819 case WM_SYSCOMMAND: 01820 if (wparam == SC_KEYMENU) { 01821 // if Alt is released (alone w/o other keys), defwindproc will 01822 // send this command, which will 'activate' the title bar menu 01823 // (we have none) and give focus to it. we don't want this to 01824 // happen, so kill this msg. 01825 01826 // Note that the WM_SYSKEYUP message for Alt has already 01827 // been sent (if it is going to be), so ignoring this 01828 // special message does no harm. 01829 return 0; 01830 } 01831 break; 01832 01833 case WM_KEYDOWN: 01834 if (_lost_keypresses) { 01835 resend_lost_keypresses(); 01836 } 01837 if (windisplay_cat.is_debug()) { 01838 windisplay_cat.debug() 01839 << "keydown: " << wparam << " (" << lookup_key(wparam) << ")\n"; 01840 } 01841 01842 // If this bit is not zero, this is just a keyrepeat echo; we 01843 // ignore these for handle_keypress (we respect keyrepeat only 01844 // for handle_keystroke). 01845 if ((lparam & 0x40000000) == 0) { 01846 POINT point; 01847 GetCursorPos(&point); 01848 ScreenToClient(hwnd, &point); 01849 handle_keypress(lookup_key(wparam), point.x, point.y, 01850 get_message_time()); 01851 01852 // wparam does not contain left/right information for SHIFT, 01853 // CONTROL, or ALT, so we have to track their status and do 01854 // the right thing. We'll send the left/right specific key 01855 // event along with the general key event. 01856 if (wparam == VK_SHIFT) { 01857 if ((GetKeyState(VK_LSHIFT) & 0x8000) != 0 && ! _lshift_down) { 01858 handle_keypress(KeyboardButton::lshift(), point.x, point.y, 01859 get_message_time()); 01860 _lshift_down = true; 01861 } 01862 if ((GetKeyState(VK_RSHIFT) & 0x8000) != 0 && ! _rshift_down) { 01863 handle_keypress(KeyboardButton::rshift(), point.x, point.y, 01864 get_message_time()); 01865 _rshift_down = true; 01866 } 01867 } else if(wparam == VK_CONTROL) { 01868 if ((GetKeyState(VK_LCONTROL) & 0x8000) != 0 && ! _lcontrol_down) { 01869 handle_keypress(KeyboardButton::lcontrol(), point.x, point.y, 01870 get_message_time()); 01871 _lcontrol_down = true; 01872 } 01873 if ((GetKeyState(VK_RCONTROL) & 0x8000) != 0 && ! _rcontrol_down) { 01874 handle_keypress(KeyboardButton::rcontrol(), point.x, point.y, 01875 get_message_time()); 01876 _rcontrol_down = true; 01877 } 01878 } 01879 01880 // Handle Cntrl-V paste from clipboard. Is there a better way 01881 // to detect this hotkey? 01882 if ((wparam=='V') && (GetKeyState(VK_CONTROL) < 0) && 01883 !_input_devices.empty()) { 01884 HGLOBAL hglb; 01885 char *lptstr; 01886 01887 if (IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL)) { 01888 // Maybe we should support CF_UNICODETEXT if it is available 01889 // too? 01890 hglb = GetClipboardData(CF_TEXT); 01891 if (hglb!=NULL) { 01892 lptstr = (char *) GlobalLock(hglb); 01893 if (lptstr != NULL) { 01894 char *pChar; 01895 for (pChar=lptstr; *pChar!=NULL; pChar++) { 01896 _input_devices[0].keystroke((uchar)*pChar); 01897 } 01898 GlobalUnlock(hglb); 01899 } 01900 } 01901 CloseClipboard(); 01902 } 01903 } 01904 } else { 01905 // Actually, for now we'll respect the repeat anyway, just 01906 // so we support backspace properly. Rethink later. 01907 POINT point; 01908 GetCursorPos(&point); 01909 ScreenToClient(hwnd, &point); 01910 handle_keypress(lookup_key(wparam), point.x, point.y, 01911 get_message_time()); 01912 01913 // wparam does not contain left/right information for SHIFT, 01914 // CONTROL, or ALT, so we have to track their status and do 01915 // the right thing. We'll send the left/right specific key 01916 // event along with the general key event. 01917 // 01918 // If the user presses LSHIFT and then RSHIFT, the RSHIFT event 01919 // will come in with the keyrepeat flag on (i.e. it will end up 01920 // in this block). The logic below should detect this correctly 01921 // and only send the RSHIFT event. Note that the CONTROL event 01922 // will be sent twice, once for each keypress. Since keyrepeats 01923 // are currently being sent simply as additional keypress events, 01924 // that should be okay for now. 01925 if (wparam == VK_SHIFT) { 01926 if (((GetKeyState(VK_LSHIFT) & 0x8000) != 0) && ! _lshift_down ) { 01927 handle_keypress(KeyboardButton::lshift(), point.x, point.y, 01928 get_message_time()); 01929 _lshift_down = true; 01930 } else if (((GetKeyState(VK_RSHIFT) & 0x8000) != 0) && ! _rshift_down ) { 01931 handle_keypress(KeyboardButton::rshift(), point.x, point.y, 01932 get_message_time()); 01933 _rshift_down = true; 01934 } else { 01935 if ((GetKeyState(VK_LSHIFT) & 0x8000) != 0) { 01936 handle_keypress(KeyboardButton::lshift(), point.x, point.y, 01937 get_message_time()); 01938 } 01939 if ((GetKeyState(VK_RSHIFT) & 0x8000) != 0) { 01940 handle_keypress(KeyboardButton::rshift(), point.x, point.y, 01941 get_message_time()); 01942 } 01943 } 01944 } else if(wparam == VK_CONTROL) { 01945 if (((GetKeyState(VK_LCONTROL) & 0x8000) != 0) && ! _lcontrol_down ) { 01946 handle_keypress(KeyboardButton::lcontrol(), point.x, point.y, 01947 get_message_time()); 01948 _lcontrol_down = true; 01949 } else if (((GetKeyState(VK_RCONTROL) & 0x8000) != 0) && ! _rcontrol_down ) { 01950 handle_keypress(KeyboardButton::rcontrol(), point.x, point.y, 01951 get_message_time()); 01952 _rcontrol_down = true; 01953 } else { 01954 if ((GetKeyState(VK_LCONTROL) & 0x8000) != 0) { 01955 handle_keypress(KeyboardButton::lcontrol(), point.x, point.y, 01956 get_message_time()); 01957 } 01958 if ((GetKeyState(VK_RCONTROL) & 0x8000) != 0) { 01959 handle_keypress(KeyboardButton::rcontrol(), point.x, point.y, 01960 get_message_time()); 01961 } 01962 } 01963 } 01964 } 01965 break; 01966 01967 case WM_SYSKEYUP: 01968 case WM_KEYUP: 01969 if (_lost_keypresses) { 01970 resend_lost_keypresses(); 01971 } 01972 if (windisplay_cat.is_debug()) { 01973 windisplay_cat.debug() 01974 << "keyup: " << wparam << " (" << lookup_key(wparam) << ")\n"; 01975 } 01976 handle_keyrelease(lookup_key(wparam), get_message_time()); 01977 01978 // wparam does not contain left/right information for SHIFT, 01979 // CONTROL, or ALT, so we have to track their status and do 01980 // the right thing. We'll send the left/right specific key 01981 // event along with the general key event. 01982 if (wparam == VK_SHIFT) { 01983 if ((GetKeyState(VK_LSHIFT) & 0x8000) == 0 && _lshift_down) { 01984 handle_keyrelease(KeyboardButton::lshift(), get_message_time()); 01985 _lshift_down = false; 01986 } 01987 if ((GetKeyState(VK_RSHIFT) & 0x8000) == 0 && _rshift_down) { 01988 handle_keyrelease(KeyboardButton::rshift(), get_message_time()); 01989 _rshift_down = false; 01990 } 01991 } else if(wparam == VK_CONTROL) { 01992 if ((GetKeyState(VK_LCONTROL) & 0x8000) == 0 && _lcontrol_down) { 01993 handle_keyrelease(KeyboardButton::lcontrol(), get_message_time()); 01994 _lcontrol_down = false; 01995 } 01996 if ((GetKeyState(VK_RCONTROL) & 0x8000) == 0 && _rcontrol_down) { 01997 handle_keyrelease(KeyboardButton::rcontrol(), get_message_time()); 01998 _rcontrol_down = false; 01999 } 02000 } else if(wparam == VK_MENU) { 02001 if ((GetKeyState(VK_LMENU) & 0x8000) == 0 && _lalt_down) { 02002 handle_keyrelease(KeyboardButton::lalt(), get_message_time()); 02003 _lalt_down = false; 02004 } 02005 if ((GetKeyState(VK_RMENU) & 0x8000) == 0 && _ralt_down) { 02006 handle_keyrelease(KeyboardButton::ralt(), get_message_time()); 02007 _ralt_down = false; 02008 } 02009 } 02010 break; 02011 02012 case WM_KILLFOCUS: 02013 if (windisplay_cat.is_debug()) { 02014 windisplay_cat.debug() 02015 << "killfocus\n"; 02016 } 02017 02018 _input_devices[0].focus_lost(get_message_time()); 02019 properties.set_foreground(false); 02020 system_changed_properties(properties); 02021 break; 02022 02023 case WM_SETFOCUS: 02024 // You would think that this would be a good time to call 02025 // resend_lost_keypresses(), but it turns out that we get 02026 // WM_SETFOCUS slightly before Windows starts resending key 02027 // up/down events to us. 02028 02029 // In particular, if the user restored focus using alt-tab, 02030 // then at this point the keyboard state will indicate that 02031 // both the alt and tab keys are held down. However, there is 02032 // a small window of opportunity for the user to release these 02033 // keys before Windows starts telling us about keyup events. 02034 // Thus, if we record the fact that alt and tab are being held 02035 // down now, we may miss the keyup events for them, and they 02036 // can get "stuck" down. 02037 02038 // So we have to defer calling resend_lost_keypresses() until 02039 // we know Windows is ready to send us key up/down events. I 02040 // don't know when we can guarantee that, except when we 02041 // actually do start to receive key up/down events, so that 02042 // call is made there. 02043 02044 if (windisplay_cat.is_debug()) { 02045 windisplay_cat.debug() 02046 << "setfocus\n"; 02047 } 02048 02049 if (_lost_keypresses) { 02050 resend_lost_keypresses(); 02051 } 02052 02053 properties.set_foreground(true); 02054 system_changed_properties(properties); 02055 break; 02056 02057 case PM_ACTIVE: 02058 if (windisplay_cat.is_debug()) { 02059 windisplay_cat.debug() 02060 << "PM_ACTIVE\n"; 02061 } 02062 properties.set_foreground(true); 02063 system_changed_properties(properties); 02064 break; 02065 02066 case PM_INACTIVE: 02067 if (windisplay_cat.is_debug()) { 02068 windisplay_cat.debug() 02069 << "PM_INACTIVE\n"; 02070 } 02071 properties.set_foreground(false); 02072 system_changed_properties(properties); 02073 break; 02074 02075 #ifdef PANDA_WIN7 02076 case WM_TOUCH: 02077 _numTouches = LOWORD(wparam); 02078 if(_numTouches > MAX_TOUCHES) 02079 _numTouches = MAX_TOUCHES; 02080 GetTouchInputInfo((HTOUCHINPUT)lparam, _numTouches, _touches, sizeof(TOUCHINPUT)); 02081 CloseTouchInputHandle((HTOUCHINPUT)lparam); 02082 break; 02083 #endif 02084 } 02085 02086 //do custom messages processing if any has been set 02087 for ( WinProcClasses::iterator it=_window_proc_classes.begin() ; it != _window_proc_classes.end(); it++ ){ 02088 (*it)->wnd_proc(this, hwnd, msg, wparam, lparam); 02089 } 02090 02091 return DefWindowProcW(hwnd, msg, wparam, lparam); 02092 } 02093 02094 02095 //////////////////////////////////////////////////////////////////// 02096 // Function: WinGraphicsWindow::static_window_proc 02097 // Access: Private, Static 02098 // Description: This is attached to the window class for all 02099 // WinGraphicsWindow windows; it is called to handle 02100 // window events. 02101 //////////////////////////////////////////////////////////////////// 02102 LONG WINAPI WinGraphicsWindow:: 02103 static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { 02104 // Look up the window in our global map. 02105 WindowHandles::const_iterator wi; 02106 wi = _window_handles.find(hwnd); 02107 if (wi != _window_handles.end()) { 02108 // We found the window. 02109 return (*wi).second->window_proc(hwnd, msg, wparam, lparam); 02110 } 02111 02112 // The window wasn't in the map; we must be creating it right now. 02113 if (_creating_window != (WinGraphicsWindow *)NULL) { 02114 return _creating_window->window_proc(hwnd, msg, wparam, lparam); 02115 } 02116 02117 // Oops, we weren't creating a window! Don't know how to handle the 02118 // message, so just pass it on to Windows to deal with it. 02119 return DefWindowProcW(hwnd, msg, wparam, lparam); 02120 } 02121 02122 //////////////////////////////////////////////////////////////////// 02123 // Function: WinGraphicsWindow::process_1_event 02124 // Access: Private, Static 02125 // Description: Handles one event from the message queue. 02126 //////////////////////////////////////////////////////////////////// 02127 void WinGraphicsWindow:: 02128 process_1_event() { 02129 MSG msg; 02130 02131 if (!GetMessage(&msg, NULL, 0, 0)) { 02132 // WM_QUIT received. We need a cleaner way to deal with this. 02133 // DestroyAllWindows(false); 02134 exit(msg.wParam); // this will invoke AtExitFn 02135 } 02136 02137 // Translate virtual key messages 02138 TranslateMessage(&msg); 02139 // Call window_proc 02140 DispatchMessage(&msg); 02141 } 02142 02143 //////////////////////////////////////////////////////////////////// 02144 // Function: WinGraphicsWindow::resend_lost_keypresses 02145 // Access: Private, Static 02146 // Description: Called when the keyboard focus has been restored to 02147 // the window after it has been lost for a time, this 02148 // rechecks the keyboard state and generates key up/down 02149 // messages for keys that have changed state in the 02150 // meantime. 02151 //////////////////////////////////////////////////////////////////// 02152 void WinGraphicsWindow:: 02153 resend_lost_keypresses() { 02154 nassertv(_lost_keypresses); 02155 // This is now a no-op. Not sure we really want to generate new 02156 // "down" or "resume" events for keys that were held while the 02157 // window focus is restored. 02158 02159 _lost_keypresses = false; 02160 } 02161 02162 //////////////////////////////////////////////////////////////////// 02163 // Function: WinGraphicsWindow::update_cursor_window 02164 // Access: Private, Static 02165 // Description: Changes _cursor_window from its current value to the 02166 // indicated value. This also changes the cursor 02167 // properties appropriately. 02168 //////////////////////////////////////////////////////////////////// 02169 void WinGraphicsWindow:: 02170 update_cursor_window(WinGraphicsWindow *to_window) { 02171 bool hide_cursor = false; 02172 if (to_window == (WinGraphicsWindow *)NULL) { 02173 // We are leaving a graphics window; we should restore the Win2000 02174 // effects. 02175 if (_got_saved_params) { 02176 SystemParametersInfo(SPI_SETMOUSETRAILS, NULL, 02177 (PVOID)_saved_mouse_trails, NULL); 02178 SystemParametersInfo(SPI_SETCURSORSHADOW, NULL, 02179 (PVOID)_saved_cursor_shadow, NULL); 02180 SystemParametersInfo(SPI_SETMOUSEVANISH, NULL, 02181 (PVOID)_saved_mouse_vanish, NULL); 02182 _got_saved_params = false; 02183 } 02184 02185 } else { 02186 const WindowProperties &to_props = to_window->get_properties(); 02187 hide_cursor = to_props.get_cursor_hidden(); 02188 02189 // We are entering a graphics window; we should save and disable 02190 // the Win2000 effects. These don't work at all well over a 3-D 02191 // window. 02192 02193 // These parameters are only defined for Win2000/XP, but they 02194 // should just cause a silent error on earlier OS's, which is OK. 02195 if (!_got_saved_params) { 02196 SystemParametersInfo(SPI_GETMOUSETRAILS, NULL, 02197 &_saved_mouse_trails, NULL); 02198 SystemParametersInfo(SPI_GETCURSORSHADOW, NULL, 02199 &_saved_cursor_shadow, NULL); 02200 SystemParametersInfo(SPI_GETMOUSEVANISH, NULL, 02201 &_saved_mouse_vanish, NULL); 02202 _got_saved_params = true; 02203 02204 SystemParametersInfo(SPI_SETMOUSETRAILS, NULL, (PVOID)0, NULL); 02205 SystemParametersInfo(SPI_SETCURSORSHADOW, NULL, (PVOID)false, NULL); 02206 SystemParametersInfo(SPI_SETMOUSEVANISH, NULL, (PVOID)false, NULL); 02207 } 02208 02209 SetCursor(to_window->_cursor); 02210 } 02211 02212 hide_or_show_cursor(hide_cursor); 02213 02214 _cursor_window = to_window; 02215 } 02216 02217 //////////////////////////////////////////////////////////////////// 02218 // Function: WinGraphicsWindow::hide_or_show_cursor 02219 // Access: Private, Static 02220 // Description: Hides or shows the mouse cursor according to the 02221 // indicated parameter. This is normally called when 02222 // the mouse wanders into or out of a window with the 02223 // cursor_hidden properties. 02224 //////////////////////////////////////////////////////////////////// 02225 void WinGraphicsWindow:: 02226 hide_or_show_cursor(bool hide_cursor) { 02227 if (hide_cursor) { 02228 if (!_cursor_hidden) { 02229 ShowCursor(false); 02230 _cursor_hidden = true; 02231 } 02232 } else { 02233 if (_cursor_hidden) { 02234 ShowCursor(true); 02235 _cursor_hidden = false; 02236 } 02237 } 02238 } 02239 02240 // don't pick any video modes < MIN_REFRESH_RATE Hz 02241 #define MIN_REFRESH_RATE 60 02242 // EnumDisplaySettings may indicate 0 or 1 for refresh rate, which means use driver default rate (assume its >min_refresh_rate) 02243 #define ACCEPTABLE_REFRESH_RATE(RATE) ((RATE >= MIN_REFRESH_RATE) || (RATE==0) || (RATE==1)) 02244 02245 //////////////////////////////////////////////////////////////////// 02246 // Function: WinGraphicsWindow::find_acceptable_display_mode 02247 // Access: Private, Static 02248 // Description: Looks for a fullscreen mode that meets the specified 02249 // size and bitdepth requirements. Returns true if a 02250 // suitable mode is found, false otherwise. 02251 //////////////////////////////////////////////////////////////////// 02252 bool WinGraphicsWindow:: 02253 find_acceptable_display_mode(DWORD dwWidth, DWORD dwHeight, DWORD bpp, 02254 DEVMODE &dm) { 02255 int modenum = 0; 02256 02257 while (1) { 02258 ZeroMemory(&dm, sizeof(dm)); 02259 dm.dmSize = sizeof(dm); 02260 02261 if (!EnumDisplaySettings(NULL, modenum, &dm)) { 02262 break; 02263 } 02264 02265 if ((dm.dmPelsWidth == dwWidth) && (dm.dmPelsHeight == dwHeight) && 02266 (dm.dmBitsPerPel == bpp)) { 02267 return true; 02268 } 02269 modenum++; 02270 } 02271 02272 return false; 02273 } 02274 02275 //////////////////////////////////////////////////////////////////// 02276 // Function: WinGraphicsWindow::show_error_message 02277 // Access: Private, Static 02278 // Description: Pops up a dialog box with the indicated Windows error 02279 // message ID (or the last error message generated) for 02280 // meaningful display to the user. 02281 //////////////////////////////////////////////////////////////////// 02282 void WinGraphicsWindow:: 02283 show_error_message(DWORD message_id) { 02284 LPTSTR message_buffer; 02285 02286 if (message_id == 0) { 02287 message_id = GetLastError(); 02288 } 02289 02290 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 02291 NULL, message_id, 02292 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //The user default language 02293 (LPTSTR)&message_buffer, // the weird ptrptr->ptr cast is intentional, see FORMAT_MESSAGE_ALLOCATE_BUFFER 02294 1024, NULL); 02295 MessageBox(GetDesktopWindow(), message_buffer, _T(errorbox_title), MB_OK); 02296 windisplay_cat.fatal() << "System error msg: " << message_buffer << endl; 02297 LocalFree(message_buffer); 02298 } 02299 02300 //////////////////////////////////////////////////////////////////// 02301 // Function: WinGraphicsWindow::handle_keypress 02302 // Access: Private 02303 // Description: 02304 //////////////////////////////////////////////////////////////////// 02305 void WinGraphicsWindow:: 02306 handle_keypress(ButtonHandle key, int x, int y, double time) { 02307 _input_devices[0].set_pointer_in_window(x, y); 02308 if (key != ButtonHandle::none()) { 02309 _input_devices[0].button_down(key, time); 02310 } 02311 } 02312 02313 //////////////////////////////////////////////////////////////////// 02314 // Function: WinGraphicsWindow::handle_keyresume 02315 // Access: Private 02316 // Description: Indicates we detected a key was already down when the 02317 // focus is restored to the window. Mainly useful for 02318 // tracking the state of modifier keys. 02319 //////////////////////////////////////////////////////////////////// 02320 void WinGraphicsWindow:: 02321 handle_keyresume(ButtonHandle key, double time) { 02322 if (key != ButtonHandle::none()) { 02323 _input_devices[0].button_resume_down(key, time); 02324 } 02325 } 02326 02327 //////////////////////////////////////////////////////////////////// 02328 // Function: WinGraphicsWindow::handle_keyrelease 02329 // Access: Private 02330 // Description: 02331 //////////////////////////////////////////////////////////////////// 02332 void WinGraphicsWindow:: 02333 handle_keyrelease(ButtonHandle key, double time) { 02334 if (key != ButtonHandle::none()) { 02335 _input_devices[0].button_up(key, time); 02336 } 02337 } 02338 02339 //////////////////////////////////////////////////////////////////// 02340 // Function: WinGraphicsWindow::lookup_key 02341 // Access: Private 02342 // Description: Translates the keycode reported by Windows to an 02343 // appropriate Panda ButtonHandle. 02344 //////////////////////////////////////////////////////////////////// 02345 ButtonHandle WinGraphicsWindow:: 02346 lookup_key(WPARAM wparam) const { 02347 // First, check for a few buttons that we filter out when the IME 02348 // window is open. 02349 if (!_ime_active) { 02350 switch(wparam) { 02351 case VK_BACK: return KeyboardButton::backspace(); 02352 case VK_DELETE: return KeyboardButton::del(); 02353 case VK_ESCAPE: return KeyboardButton::escape(); 02354 case VK_SPACE: return KeyboardButton::space(); 02355 case VK_UP: return KeyboardButton::up(); 02356 case VK_DOWN: return KeyboardButton::down(); 02357 case VK_LEFT: return KeyboardButton::left(); 02358 case VK_RIGHT: return KeyboardButton::right(); 02359 } 02360 } 02361 02362 // Now check for the rest of the buttons, including the ones that 02363 // we allow through even when the IME window is open. 02364 switch(wparam) { 02365 case VK_TAB: return KeyboardButton::tab(); 02366 case VK_PRIOR: return KeyboardButton::page_up(); 02367 case VK_NEXT: return KeyboardButton::page_down(); 02368 case VK_HOME: return KeyboardButton::home(); 02369 case VK_END: return KeyboardButton::end(); 02370 case VK_F1: return KeyboardButton::f1(); 02371 case VK_F2: return KeyboardButton::f2(); 02372 case VK_F3: return KeyboardButton::f3(); 02373 case VK_F4: return KeyboardButton::f4(); 02374 case VK_F5: return KeyboardButton::f5(); 02375 case VK_F6: return KeyboardButton::f6(); 02376 case VK_F7: return KeyboardButton::f7(); 02377 case VK_F8: return KeyboardButton::f8(); 02378 case VK_F9: return KeyboardButton::f9(); 02379 case VK_F10: return KeyboardButton::f10(); 02380 case VK_F11: return KeyboardButton::f11(); 02381 case VK_F12: return KeyboardButton::f12(); 02382 case VK_INSERT: return KeyboardButton::insert(); 02383 case VK_CAPITAL: return KeyboardButton::caps_lock(); 02384 case VK_NUMLOCK: return KeyboardButton::num_lock(); 02385 case VK_SCROLL: return KeyboardButton::scroll_lock(); 02386 case VK_PAUSE: return KeyboardButton::pause(); 02387 case VK_SNAPSHOT: return KeyboardButton::print_screen(); 02388 02389 case VK_SHIFT: return KeyboardButton::shift(); 02390 case VK_LSHIFT: return KeyboardButton::lshift(); 02391 case VK_RSHIFT: return KeyboardButton::rshift(); 02392 02393 case VK_CONTROL: return KeyboardButton::control(); 02394 case VK_LCONTROL: return KeyboardButton::lcontrol(); 02395 case VK_RCONTROL: return KeyboardButton::rcontrol(); 02396 02397 case VK_MENU: return KeyboardButton::alt(); 02398 case VK_LMENU: return KeyboardButton::lalt(); 02399 case VK_RMENU: return KeyboardButton::ralt(); 02400 02401 default: 02402 int key = MapVirtualKey(wparam, 2); 02403 if (isascii(key) && key != 0) { 02404 // We used to try to remap lowercase to uppercase keys 02405 // here based on the state of the shift and/or caps lock 02406 // keys. But that's a mistake, and doesn't allow for 02407 // international or user-defined keyboards; let Windows 02408 // do that mapping. 02409 02410 // Nowadays, we make a distinction between a "button" 02411 // and a "keystroke". A button corresponds to a 02412 // physical button on the keyboard and has a down and up 02413 // event associated. A keystroke may or may not 02414 // correspond to a physical button, but will be some 02415 // Unicode character and will not have a corresponding 02416 // up event. 02417 return KeyboardButton::ascii_key(tolower(key)); 02418 } 02419 break; 02420 } 02421 return ButtonHandle::none(); 02422 } 02423 02424 //////////////////////////////////////////////////////////////////// 02425 // Function: WinGraphicsWindow::handle_raw_input 02426 // Access: Private 02427 // Description: 02428 //////////////////////////////////////////////////////////////////// 02429 void WinGraphicsWindow:: 02430 handle_raw_input(HRAWINPUT hraw) { 02431 LPBYTE lpb; 02432 UINT dwSize; 02433 02434 if (hraw == 0) { 02435 return; 02436 } 02437 if (pGetRawInputData(hraw, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)) == -1) { 02438 return; 02439 } 02440 02441 lpb = (LPBYTE)alloca(sizeof(LPBYTE) * dwSize); 02442 if (lpb == NULL) { 02443 return; 02444 } 02445 02446 if (pGetRawInputData(hraw, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) { 02447 return; 02448 } 02449 02450 RAWINPUT *raw = (RAWINPUT *)lpb; 02451 if (raw->header.hDevice == 0) { 02452 return; 02453 } 02454 02455 for (int i = 1; i < (int)(_input_devices.size()); ++i) { 02456 if (_input_device_handle[i] == raw->header.hDevice) { 02457 int adjx = raw->data.mouse.lLastX; 02458 int adjy = raw->data.mouse.lLastY; 02459 02460 if (raw->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { 02461 _input_devices[i].set_pointer_in_window(adjx, adjy); 02462 } else { 02463 int oldx = _input_devices[i].get_raw_pointer().get_x(); 02464 int oldy = _input_devices[i].get_raw_pointer().get_y(); 02465 _input_devices[i].set_pointer_in_window(oldx + adjx, oldy + adjy); 02466 } 02467 02468 if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) { 02469 _input_devices[i].button_down(MouseButton::button(0), get_message_time()); 02470 } 02471 if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP) { 02472 _input_devices[i].button_up(MouseButton::button(0), get_message_time()); 02473 } 02474 if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) { 02475 _input_devices[i].button_down(MouseButton::button(2), get_message_time()); 02476 } 02477 if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP) { 02478 _input_devices[i].button_up(MouseButton::button(2), get_message_time()); 02479 } 02480 if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) { 02481 _input_devices[i].button_down(MouseButton::button(1), get_message_time()); 02482 } 02483 if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP) { 02484 _input_devices[i].button_up(MouseButton::button(1), get_message_time()); 02485 } 02486 if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) { 02487 _input_devices[i].button_down(MouseButton::button(3), get_message_time()); 02488 } 02489 if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP) { 02490 _input_devices[i].button_up(MouseButton::button(3), get_message_time()); 02491 } 02492 if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) { 02493 _input_devices[i].button_down(MouseButton::button(4), get_message_time()); 02494 } 02495 if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP) { 02496 _input_devices[i].button_up(MouseButton::button(4), get_message_time()); 02497 } 02498 } 02499 } 02500 } 02501 02502 //////////////////////////////////////////////////////////////////// 02503 // Function: WinGraphicsWindow::handle_mouse_motion 02504 // Access: Private 02505 // Description: 02506 //////////////////////////////////////////////////////////////////// 02507 bool WinGraphicsWindow:: 02508 handle_mouse_motion(int x, int y) { 02509 _input_devices[0].set_pointer_in_window(x, y); 02510 return false; 02511 } 02512 02513 //////////////////////////////////////////////////////////////////// 02514 // Function: WinGraphicsWindow::handle_mouse_exit 02515 // Access: Private 02516 // Description: 02517 //////////////////////////////////////////////////////////////////// 02518 void WinGraphicsWindow:: 02519 handle_mouse_exit() { 02520 // note: 'mouse_motion' is considered the 'entry' event 02521 _input_devices[0].set_pointer_out_of_window(); 02522 } 02523 02524 //////////////////////////////////////////////////////////////////// 02525 // Function: WinGraphicsWindow::get_icon 02526 // Access: Private, Static 02527 // Description: Loads and returns an HICON corresponding to the 02528 // indicated filename. If the file cannot be loaded, 02529 // returns 0. 02530 //////////////////////////////////////////////////////////////////// 02531 HICON WinGraphicsWindow:: 02532 get_icon(const Filename &filename) { 02533 // First, look for the unresolved filename in our index. 02534 IconFilenames::iterator fi = _icon_filenames.find(filename); 02535 if (fi != _icon_filenames.end()) { 02536 return (HICON)((*fi).second); 02537 } 02538 02539 // If it wasn't found, resolve the filename and search for that. 02540 02541 // Since we have to use a Windows call to load the image from a 02542 // filename, we can't load a virtual file and we can't use the 02543 // virtual file system. 02544 Filename resolved = filename; 02545 if (!resolved.resolve_filename(get_model_path())) { 02546 // The filename doesn't exist along the search path. 02547 if (resolved.is_fully_qualified() && resolved.exists()) { 02548 // But it does exist locally, so accept it. 02549 02550 } else { 02551 windisplay_cat.warning() 02552 << "Could not find icon filename " << filename << "\n"; 02553 return 0; 02554 } 02555 } 02556 fi = _icon_filenames.find(resolved); 02557 if (fi != _icon_filenames.end()) { 02558 _icon_filenames[filename] = (*fi).second; 02559 return (HICON)((*fi).second); 02560 } 02561 02562 Filename os = resolved.to_os_specific(); 02563 02564 HANDLE h = LoadImage(NULL, os.c_str(), 02565 IMAGE_ICON, 0, 0, LR_LOADFROMFILE); 02566 if (h == 0) { 02567 windisplay_cat.warning() 02568 << "windows icon filename '" << os << "' could not be loaded!!\n"; 02569 } 02570 02571 _icon_filenames[filename] = h; 02572 _icon_filenames[resolved] = h; 02573 return (HICON)h; 02574 } 02575 02576 //////////////////////////////////////////////////////////////////// 02577 // Function: WinGraphicsWindow::get_cursor 02578 // Access: Private, Static 02579 // Description: Loads and returns an HCURSOR corresponding to the 02580 // indicated filename. If the file cannot be loaded, 02581 // returns 0. 02582 //////////////////////////////////////////////////////////////////// 02583 HCURSOR WinGraphicsWindow:: 02584 get_cursor(const Filename &filename) { 02585 // First, look for the unresolved filename in our index. 02586 IconFilenames::iterator fi = _cursor_filenames.find(filename); 02587 if (fi != _cursor_filenames.end()) { 02588 return (HCURSOR)((*fi).second); 02589 } 02590 02591 // If it wasn't found, resolve the filename and search for that. 02592 02593 // Since we have to use a Windows call to load the image from a 02594 // filename, we can't load a virtual file and we can't use the 02595 // virtual file system. 02596 Filename resolved = filename; 02597 if (!resolved.resolve_filename(get_model_path())) { 02598 // The filename doesn't exist. 02599 windisplay_cat.warning() 02600 << "Could not find cursor filename " << filename << "\n"; 02601 return 0; 02602 } 02603 fi = _cursor_filenames.find(resolved); 02604 if (fi != _cursor_filenames.end()) { 02605 _cursor_filenames[filename] = (*fi).second; 02606 return (HCURSOR)((*fi).second); 02607 } 02608 02609 Filename os = resolved.to_os_specific(); 02610 02611 HANDLE h = LoadImage(NULL, os.c_str(), 02612 IMAGE_CURSOR, 0, 0, LR_LOADFROMFILE); 02613 if (h == 0) { 02614 windisplay_cat.warning() 02615 << "windows cursor filename '" << os << "' could not be loaded!!\n"; 02616 show_error_message(); 02617 } 02618 02619 _cursor_filenames[filename] = h; 02620 _cursor_filenames[resolved] = h; 02621 return (HCURSOR)h; 02622 } 02623 02624 static HCURSOR get_cursor(const Filename &filename); 02625 02626 //////////////////////////////////////////////////////////////////// 02627 // Function: WinGraphicsWindow::register_window_class 02628 // Access: Private, Static 02629 // Description: Registers a Window class appropriate for the 02630 // indicated properties. This class may be shared by 02631 // multiple windows. 02632 //////////////////////////////////////////////////////////////////// 02633 const WinGraphicsWindow::WindowClass &WinGraphicsWindow:: 02634 register_window_class(const WindowProperties &props) { 02635 WindowClass wcreg(props); 02636 wostringstream wclass_name; 02637 wclass_name << L"WinGraphicsWindow" << _window_class_index; 02638 wcreg._name = wclass_name.str(); 02639 02640 pair<WindowClasses::iterator, bool> found = _window_classes.insert(wcreg); 02641 const WindowClass &wclass = (*found.first); 02642 02643 if (!found.second) { 02644 // We have already created a window class. 02645 return wclass; 02646 } 02647 02648 // We have not yet created this window class. 02649 _window_class_index++; 02650 02651 WNDCLASSW wc; 02652 02653 HINSTANCE instance = GetModuleHandle(NULL); 02654 02655 // Clear before filling in window structure! 02656 ZeroMemory(&wc, sizeof(wc)); 02657 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 02658 wc.lpfnWndProc = (WNDPROC)static_window_proc; 02659 wc.hInstance = instance; 02660 02661 wc.hIcon = wclass._icon; 02662 02663 wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); 02664 wc.lpszMenuName = NULL; 02665 wc.lpszClassName = wclass._name.c_str(); 02666 02667 if (!RegisterClassW(&wc)) { 02668 windisplay_cat.error() 02669 << "could not register window class " << wclass._name << "!" << endl; 02670 return wclass; 02671 } 02672 02673 return wclass; 02674 } 02675 02676 //////////////////////////////////////////////////////////////////// 02677 // Function: WinGraphicsWindow::WinWindowHandle::Constructor 02678 // Access: Public 02679 // Description: 02680 //////////////////////////////////////////////////////////////////// 02681 WinGraphicsWindow::WinWindowHandle:: 02682 WinWindowHandle(WinGraphicsWindow *window, const WindowHandle ©) : 02683 WindowHandle(copy), 02684 _window(window) 02685 { 02686 } 02687 02688 //////////////////////////////////////////////////////////////////// 02689 // Function: WinGraphicsWindow::WinWindowHandle::clear_window 02690 // Access: Public 02691 // Description: Should be called by the WinGraphicsWindow's 02692 // destructor, so that we don't end up with a floating 02693 // pointer should this object persist beyond the 02694 // lifespan of its window. 02695 //////////////////////////////////////////////////////////////////// 02696 void WinGraphicsWindow::WinWindowHandle:: 02697 clear_window() { 02698 _window = NULL; 02699 } 02700 02701 //////////////////////////////////////////////////////////////////// 02702 // Function: WinGraphicsWindow::WinWindowHandle::receive_windows_message 02703 // Access: Public, Virtual 02704 // Description: Called on a child handle to deliver a keyboard button 02705 // event generated in the parent window. 02706 //////////////////////////////////////////////////////////////////// 02707 void WinGraphicsWindow::WinWindowHandle:: 02708 receive_windows_message(unsigned int msg, int wparam, int lparam) { 02709 if (_window != NULL) { 02710 _window->receive_windows_message(msg, wparam, lparam); 02711 } 02712 } 02713 02714 02715 // pops up MsgBox w/system error msg 02716 void PrintErrorMessage(DWORD msgID) { 02717 LPTSTR pMessageBuffer; 02718 02719 if (msgID==PRINT_LAST_ERROR) 02720 msgID=GetLastError(); 02721 02722 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 02723 NULL,msgID, 02724 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //The user default language 02725 (LPTSTR) &pMessageBuffer, // the weird ptrptr->ptr cast is intentional, see FORMAT_MESSAGE_ALLOCATE_BUFFER 02726 1024, NULL); 02727 MessageBox(GetDesktopWindow(),pMessageBuffer,_T(errorbox_title),MB_OK); 02728 windisplay_cat.fatal() << "System error msg: " << pMessageBuffer << endl; 02729 LocalFree( pMessageBuffer ); 02730 } 02731 02732 void 02733 ClearToBlack(HWND hWnd, const WindowProperties &props) { 02734 if (!props.has_origin()) { 02735 if (windisplay_cat.is_debug()) { 02736 windisplay_cat.debug() 02737 << "Skipping ClearToBlack, no origin specified yet.\n"; 02738 } 02739 return; 02740 } 02741 02742 if (windisplay_cat.is_debug()) { 02743 windisplay_cat.debug() 02744 << "ClearToBlack(" << hWnd << ", " << props << ")\n"; 02745 } 02746 // clear to black 02747 HDC hDC=GetDC(hWnd); // GetDC is not particularly fast. if this needs to be super-quick, we should cache GetDC's hDC 02748 RECT clrRect = { 02749 props.get_x_origin(), props.get_y_origin(), 02750 props.get_x_origin() + props.get_x_size(), 02751 props.get_y_origin() + props.get_y_size() 02752 }; 02753 FillRect(hDC,&clrRect,(HBRUSH)GetStockObject(BLACK_BRUSH)); 02754 ReleaseDC(hWnd,hDC); 02755 GdiFlush(); 02756 } 02757 02758 //////////////////////////////////////////////////////////////////// 02759 // Function: get_client_rect_screen 02760 // Description: Fills view_rect with the coordinates of the client 02761 // area of the indicated window, converted to screen 02762 // coordinates. 02763 //////////////////////////////////////////////////////////////////// 02764 void get_client_rect_screen(HWND hwnd, RECT *view_rect) { 02765 GetClientRect(hwnd, view_rect); 02766 02767 POINT ul, lr; 02768 ul.x = view_rect->left; 02769 ul.y = view_rect->top; 02770 lr.x = view_rect->right; 02771 lr.y = view_rect->bottom; 02772 02773 ClientToScreen(hwnd, &ul); 02774 ClientToScreen(hwnd, &lr); 02775 02776 view_rect->left = ul.x; 02777 view_rect->top = ul.y; 02778 view_rect->right = lr.x; 02779 view_rect->bottom = lr.y; 02780 } 02781 02782 //////////////////////////////////////////////////////////////////// 02783 // Function: WinGraphicsWindow::add_window_proc 02784 // Access: Public, Virtual 02785 // Description: Adds the specified Windows proc event handler to be called 02786 // whenever a Windows event occurs. 02787 // 02788 //////////////////////////////////////////////////////////////////// 02789 void WinGraphicsWindow::add_window_proc( const GraphicsWindowProc* wnd_proc ){ 02790 nassertv(wnd_proc != NULL); 02791 _window_proc_classes.insert( (GraphicsWindowProc*)wnd_proc ); 02792 } 02793 02794 //////////////////////////////////////////////////////////////////// 02795 // Function: WinGraphicsWindow::remove_window_proc 02796 // Access: Public, Virtual 02797 // Description: Removes the specified Windows proc event handler. 02798 // 02799 //////////////////////////////////////////////////////////////////// 02800 void WinGraphicsWindow::remove_window_proc( const GraphicsWindowProc* wnd_proc ){ 02801 nassertv(wnd_proc != NULL); 02802 _window_proc_classes.erase( (GraphicsWindowProc*)wnd_proc ); 02803 } 02804 02805 //////////////////////////////////////////////////////////////////// 02806 // Function: WinGraphicsWindow::clear_window_procs 02807 // Access: Public, Virtual 02808 // Description: Removes all Windows proc event handlers. 02809 // 02810 //////////////////////////////////////////////////////////////////// 02811 void WinGraphicsWindow::clear_window_procs(){ 02812 _window_proc_classes.clear(); 02813 } 02814 02815 //////////////////////////////////////////////////////////////////// 02816 // Function: WinGraphicsWindow::supports_window_procs 02817 // Access: Public, Virtual 02818 // Description: Returns whether this window supports adding of windows proc handlers. 02819 // 02820 //////////////////////////////////////////////////////////////////// 02821 bool WinGraphicsWindow::supports_window_procs() const{ 02822 return true; 02823 } 02824 02825 //////////////////////////////////////////////////////////////////// 02826 // Function: WinGraphicsWindow::is_touch_event 02827 // Access: Public, Virtual 02828 // Description: Returns whether the specified event msg is a touch message. 02829 // 02830 //////////////////////////////////////////////////////////////////// 02831 bool WinGraphicsWindow:: 02832 is_touch_event(GraphicsWindowProcCallbackData* callbackData){ 02833 #ifdef PANDA_WIN7 02834 return callbackData->get_msg() == WM_TOUCH; 02835 #else 02836 return false; 02837 #endif 02838 } 02839 02840 //////////////////////////////////////////////////////////////////// 02841 // Function: WinGraphicsWindow::get_num_touches 02842 // Access: Public, Virtual 02843 // Description: Returns the current number of touches on this window. 02844 // 02845 //////////////////////////////////////////////////////////////////// 02846 int WinGraphicsWindow:: 02847 get_num_touches(){ 02848 #ifdef PANDA_WIN7 02849 return _numTouches; 02850 #else 02851 return 0; 02852 #endif 02853 } 02854 02855 //////////////////////////////////////////////////////////////////// 02856 // Function: WinGraphicsWindow::get_touch_info 02857 // Access: Public, Virtual 02858 // Description: Returns the TouchInfo object describing the specified touch. 02859 // 02860 //////////////////////////////////////////////////////////////////// 02861 TouchInfo WinGraphicsWindow:: 02862 get_touch_info(int index){ 02863 #ifdef PANDA_WIN7 02864 TOUCHINPUT ti = _touches[index]; 02865 POINT point; 02866 point.x = TOUCH_COORD_TO_PIXEL(ti.x); 02867 point.y = TOUCH_COORD_TO_PIXEL(ti.y); 02868 ScreenToClient(_hWnd, &point); 02869 02870 TouchInfo ret = TouchInfo(); 02871 ret.set_x(point.x); 02872 ret.set_y(point.y); 02873 ret.set_id(ti.dwID); 02874 ret.set_flags(ti.dwFlags); 02875 return ret; 02876 #else 02877 return TouchInfo(); 02878 #endif 02879 }