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