Panda3D

winGraphicsWindow.cxx

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 &copy) :
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 }
 All Classes Functions Variables Enumerations