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