Panda3D
 All Classes Functions Variables Enumerations
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 HAVE_WIN_TOUCHINPUT
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 HAVE_WIN_TOUCHINPUT
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   if (!GetClientRect(_hWnd, &view_rect)) {
00717     // Sometimes we get a "reshape" before the window is fully
00718     // created, in which case GetClientRect() ought to fail.  Ignore
00719     // this.
00720     if (windisplay_cat.is_debug()) {
00721       windisplay_cat.debug()
00722         << "GetClientRect() failed in handle_reshape.  Ignoring.\n";
00723     }
00724     return;
00725   }
00726 
00727   // But in practice, GetClientRect() doesn't really fail, but just
00728   // returns all zeroes.  Ignore this too.
00729   if (view_rect.left == 0 && view_rect.right == 0 && 
00730       view_rect.bottom == 0 && view_rect.top == 0) {
00731     if (windisplay_cat.is_debug()) {
00732       windisplay_cat.debug()
00733         << "GetClientRect() returned all zeroes in handle_reshape.  Ignoring.\n";
00734     }
00735     return;
00736   }
00737 
00738   bool result = ClientToScreen(_hWnd, (POINT*)&view_rect.left);   // translates top,left pnt
00739   if (result) {
00740     result = ClientToScreen(_hWnd, (POINT*)&view_rect.right);  // translates right,bottom pnt
00741   }
00742 
00743   if (!result) {
00744     if (windisplay_cat.is_debug()) {
00745       windisplay_cat.debug()
00746         << "ClientToScreen() failed in handle_reshape.  Ignoring.\n";
00747     }
00748     return;
00749   }
00750   
00751   WindowProperties properties;
00752   properties.set_size((view_rect.right - view_rect.left), 
00753                       (view_rect.bottom - view_rect.top));
00754 
00755   // _props origin should reflect upper left of view rectangle
00756   properties.set_origin(view_rect.left, view_rect.top);
00757   
00758   if (windisplay_cat.is_spam()) {
00759     windisplay_cat.spam()
00760       << "reshape to origin: (" << properties.get_x_origin() << "," 
00761       << properties.get_y_origin() << "), size: (" << properties.get_x_size()
00762       << "," << properties.get_y_size() << ")\n";
00763   }
00764 
00765   adjust_z_order();
00766   system_changed_properties(properties);
00767 }
00768 
00769 ////////////////////////////////////////////////////////////////////
00770 //     Function: WinGraphicsWindow::do_fullscreen_resize
00771 //       Access: Protected, Virtual
00772 //  Description: Called in the window thread to resize a fullscreen
00773 //               window.
00774 ////////////////////////////////////////////////////////////////////
00775 bool WinGraphicsWindow::
00776 do_fullscreen_resize(int x_size, int y_size) {
00777   HWND hDesktopWindow = GetDesktopWindow();
00778   HDC scrnDC = GetDC(hDesktopWindow);
00779   DWORD dwFullScreenBitDepth = GetDeviceCaps(scrnDC, BITSPIXEL);
00780   ReleaseDC(hDesktopWindow, scrnDC);
00781 
00782   // resize will always leave screen bitdepth unchanged
00783 
00784   // allowing resizing of lowvidmem cards to > 640x480.  why?  I'll
00785   // assume check was already done by caller, so he knows what he
00786   // wants
00787 
00788   DEVMODE dm;
00789   if (!find_acceptable_display_mode(x_size, y_size,
00790                                     dwFullScreenBitDepth, dm)) {
00791     windisplay_cat.error()
00792       << "window resize(" << x_size << ", " << y_size 
00793       << ") failed, no compatible fullscreen display mode found!\n";
00794     return false;
00795   }
00796 
00797   // this causes WM_SIZE msg to be produced
00798   SetWindowPos(_hWnd, NULL, 0,0, x_size, y_size, 
00799                SWP_NOZORDER | SWP_NOMOVE | SWP_NOSENDCHANGING);
00800   int chg_result = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
00801 
00802   if (chg_result != DISP_CHANGE_SUCCESSFUL) {
00803     windisplay_cat.error()
00804       << "resize ChangeDisplaySettings failed (error code: " 
00805       << chg_result << ") for specified res: "
00806       << dm.dmPelsWidth << " x " << dm.dmPelsHeight
00807       << " x " << dm.dmBitsPerPel << ", " 
00808       << dm.dmDisplayFrequency << " Hz\n";
00809     return false;
00810   }
00811 
00812   _fullscreen_display_mode = dm;
00813 
00814   windisplay_cat.info()
00815     << "Resized fullscreen window to " << x_size << ", " << y_size 
00816     << " bitdepth " << dwFullScreenBitDepth << ", "
00817     << dm.dmDisplayFrequency << "Hz\n";
00818 
00819   _properties.set_size(x_size, y_size);
00820   set_size_and_recalc(x_size, y_size);
00821 
00822   return true;
00823 }
00824 
00825 ////////////////////////////////////////////////////////////////////
00826 //     Function: WinGraphicsWindow::do_fullscreen_switch
00827 //       Access: Protected, Virtual
00828 //  Description: Called in the set_properties_now function
00829 //               to switch to fullscreen.
00830 ////////////////////////////////////////////////////////////////////
00831 bool WinGraphicsWindow::
00832 do_fullscreen_switch() {
00833   if (!do_fullscreen_enable()) {
00834     // Couldn't get fullscreen.
00835     return false;
00836   }
00837 
00838   DWORD window_style = make_style(true);
00839   SetWindowLong(_hWnd, GWL_STYLE, window_style);
00840 
00841   WINDOW_METRICS metrics;
00842   bool has_origin;
00843   if (!calculate_metrics(true, window_style, metrics, has_origin)){
00844     return false;
00845   }
00846 
00847   SetWindowPos(_hWnd, HWND_NOTOPMOST, 0, 0, metrics.width, metrics.height,
00848     SWP_FRAMECHANGED | SWP_SHOWWINDOW);
00849   return true;
00850 }
00851 
00852 ////////////////////////////////////////////////////////////////////
00853 //     Function: WinGraphicsWindow::do_windowed_switch
00854 //       Access: Protected, Virtual
00855 //  Description: Called in the set_properties_now function
00856 //               to switch to windowed mode.
00857 ////////////////////////////////////////////////////////////////////
00858 bool WinGraphicsWindow::
00859 do_windowed_switch() {
00860   do_fullscreen_disable();
00861   DWORD window_style = make_style(false);
00862   SetWindowLong(_hWnd, GWL_STYLE, window_style);
00863 
00864   WINDOW_METRICS metrics;
00865   bool has_origin;
00866 
00867   if (!calculate_metrics(false, window_style, metrics, has_origin)){
00868     return false;
00869   }
00870   
00871   // We send SWP_FRAMECHANGED so that the new styles are taken into account.
00872   // Also, we place the Windows at 0,0 to play safe until we decide how to
00873   // get Panda to remember the windowed origin.
00874 
00875   SetWindowPos(_hWnd, HWND_NOTOPMOST, 0, 0,
00876                metrics.width, metrics.height,
00877                SWP_FRAMECHANGED | SWP_SHOWWINDOW);
00878 
00879   return true;
00880 }
00881 
00882 ////////////////////////////////////////////////////////////////////
00883 //     Function: WinGraphicsWindow::reconsider_fullscreen_size
00884 //       Access: Protected, Virtual
00885 //  Description: Called before creating a fullscreen window to give
00886 //               the driver a chance to adjust the particular
00887 //               resolution request, if necessary.
00888 ////////////////////////////////////////////////////////////////////
00889 void WinGraphicsWindow::
00890 reconsider_fullscreen_size(DWORD &, DWORD &, DWORD &) {
00891 }
00892 
00893 ////////////////////////////////////////////////////////////////////
00894 //     Function: WinGraphicsWindow::support_overlay_window
00895 //       Access: Protected, Virtual
00896 //  Description: Some windows graphics contexts (e.g. DirectX)
00897 //               require special support to enable the displaying of
00898 //               an overlay window (particularly the IME window) over
00899 //               the fullscreen graphics window.  This is a hook for
00900 //               the window to enable or disable that mode when
00901 //               necessary.
00902 ////////////////////////////////////////////////////////////////////
00903 void WinGraphicsWindow::
00904 support_overlay_window(bool) {
00905 }
00906 
00907 ////////////////////////////////////////////////////////////////////
00908 //     Function: WinGraphicsWindow::make_style
00909 //       Access: Private
00910 //  Description: Constructs a dwStyle for the specified mode,
00911 //               be it windowed or fullscreen.
00912 ////////////////////////////////////////////////////////////////////
00913 DWORD WinGraphicsWindow::
00914 make_style(bool fullscreen) {
00915   //  from MSDN:
00916   //  An OpenGL window has its own pixel format. Because of this, only
00917   //  device contexts retrieved for the client area of an OpenGL
00918   //  window are allowed to draw into the window. As a result, an
00919   //  OpenGL window should be created with the WS_CLIPCHILDREN and
00920   //  WS_CLIPSIBLINGS styles. Additionally, the window class attribute
00921   //  should not include the CS_PARENTDC style.
00922 
00923   DWORD window_style = WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
00924 
00925   if (fullscreen){
00926     window_style |= WS_SYSMENU;
00927   } else if (!_properties.get_undecorated()) {
00928     window_style |= (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX);
00929 
00930     if (!_properties.get_fixed_size()) {
00931       window_style |= (WS_SIZEBOX | WS_MAXIMIZEBOX);
00932     } else {
00933       window_style |= WS_BORDER;
00934     }
00935   }
00936   return window_style;
00937 }
00938 
00939 ////////////////////////////////////////////////////////////////////
00940 //     Function: WinGraphicsWindow::calculate_metrics
00941 //       Access: Private
00942 //  Description: Calculates the metrics for the specified mode,
00943 //               be it windowed or fullscreen.
00944 ////////////////////////////////////////////////////////////////////
00945 bool WinGraphicsWindow::
00946 calculate_metrics(bool fullscreen, DWORD window_style, WINDOW_METRICS &metrics,
00947                   bool &has_origin) {
00948   metrics.x = 0;
00949   metrics.y = 0;
00950   has_origin = _properties.has_origin();
00951   if (!fullscreen && has_origin) {
00952     metrics.x = _properties.get_x_origin();
00953     metrics.y = _properties.get_y_origin();
00954     
00955     // A coordinate of -2 means to center the window in its client area.
00956     if (metrics.x == -2) {
00957       metrics.x = 0.5 * (_pipe->get_display_width() - _properties.get_x_size());
00958     }
00959     if (metrics.y == -2) {
00960       metrics.y = 0.5 * (_pipe->get_display_height() - _properties.get_y_size());
00961     }
00962     _properties.set_origin(metrics.x, metrics.y);
00963 
00964     if (metrics.x == -1 && metrics.y == -1) {
00965       metrics.x = 0;
00966       metrics.y = 0;
00967       has_origin = false;
00968     }
00969   }
00970 
00971   metrics.width = _properties.get_x_size();
00972   metrics.height = _properties.get_y_size();
00973 
00974   if (!fullscreen){
00975     RECT win_rect;
00976     SetRect(&win_rect, metrics.x, metrics.y,
00977             metrics.x + metrics.width, metrics.y + metrics.height);
00978     
00979     // Compute window size based on desired client area size
00980     if (!AdjustWindowRect(&win_rect, window_style, FALSE)) {
00981       windisplay_cat.error()
00982         << "AdjustWindowRect failed!" << endl;
00983       return false;
00984     }
00985 
00986     if (has_origin) {
00987       metrics.x = win_rect.left;
00988       metrics.y = win_rect.top;
00989     } else {
00990       metrics.x = CW_USEDEFAULT;
00991       metrics.y = CW_USEDEFAULT;
00992     }
00993     metrics.width = win_rect.right - win_rect.left;
00994     metrics.height = win_rect.bottom - win_rect.top;
00995   }
00996 
00997   return true;
00998 }
00999 
01000 ////////////////////////////////////////////////////////////////////
01001 //     Function: WinGraphicsWindow::open_graphic_window
01002 //       Access: Private
01003 //  Description: Creates a regular or fullscreen window.
01004 ////////////////////////////////////////////////////////////////////
01005 bool WinGraphicsWindow::
01006 open_graphic_window(bool fullscreen) {
01007   DWORD window_style = make_style(fullscreen);
01008   
01009   wstring title;
01010   if (_properties.has_title()) {
01011     TextEncoder encoder;
01012     title = encoder.decode_text(_properties.get_title());
01013   }
01014 
01015   if (!_properties.has_size()) {
01016     //Just fill in a conservative default size if one isn't specified.
01017     _properties.set_size(640, 480);
01018   }
01019 
01020   WINDOW_METRICS metrics;
01021   bool has_origin;
01022   if (!calculate_metrics(fullscreen, window_style, metrics, has_origin)){
01023     return false;
01024   }
01025 
01026   const WindowClass &wclass = register_window_class(_properties);
01027   HINSTANCE hinstance = GetModuleHandle(NULL);
01028 
01029   _hparent = NULL;
01030   
01031   if (!fullscreen){
01032     WindowHandle *window_handle = _properties.get_parent_window();
01033     if (window_handle != NULL) {
01034       windisplay_cat.info()
01035         << "Got parent_window " << *window_handle << "\n";
01036       WindowHandle::OSHandle *os_handle = window_handle->get_os_handle();
01037       if (os_handle != NULL) {
01038         windisplay_cat.info()
01039           << "os_handle type " << os_handle->get_type() << "\n";
01040         
01041         if (os_handle->is_of_type(NativeWindowHandle::WinHandle::get_class_type())) {
01042           NativeWindowHandle::WinHandle *win_handle = DCAST(NativeWindowHandle::WinHandle, os_handle);
01043           _hparent = win_handle->get_handle();
01044           } else if (os_handle->is_of_type(NativeWindowHandle::IntHandle::get_class_type())) {
01045           NativeWindowHandle::IntHandle *int_handle = DCAST(NativeWindowHandle::IntHandle, os_handle);
01046           _hparent = (HWND)int_handle->get_handle();
01047         }
01048       }
01049     }
01050     _parent_window_handle = window_handle;
01051   } else {
01052     _parent_window_handle = NULL;
01053   }
01054 
01055   if (!_hparent) { // This can be a regular window or a fullscreen window
01056     _hWnd = CreateWindowW(wclass._name.c_str(), title.c_str(), window_style, 
01057                           metrics.x, metrics.y,
01058                           metrics.width,
01059                           metrics.height,
01060                           NULL, NULL, hinstance, 0);
01061   } else { // This is a regular window with a parent
01062     int x_origin = 0;
01063     int y_origin = 0;
01064     
01065     if (!fullscreen && has_origin) {
01066       x_origin = _properties.get_x_origin();
01067       y_origin = _properties.get_y_origin();
01068     }
01069 
01070     _hWnd = CreateWindowW(wclass._name.c_str(), title.c_str(), 
01071                           WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS ,
01072                           x_origin, y_origin,
01073                           _properties.get_x_size(), _properties.get_y_size(),
01074                           _hparent, NULL, hinstance, 0);
01075     
01076     if (_hWnd) {
01077       // join our keyboard state with the parents
01078 
01079       // Actually, let's not.  Is there really any reason to do this?
01080       // It causes problems with the browser plugin--it deadlocks when
01081       // the parent process is waiting on the child process.
01082       //AttachThreadInput(GetWindowThreadProcessId(_hparent,NULL), GetCurrentThreadId(),TRUE);
01083       
01084       WindowProperties properties;
01085       properties.set_foreground(true);
01086       system_changed_properties(properties);
01087     }
01088   }
01089   
01090   if (!_hWnd) {
01091     windisplay_cat.error()
01092       << "CreateWindow() failed!" << endl;
01093     show_error_message();
01094     return false;
01095   }
01096 
01097   // I'd prefer to CreateWindow after DisplayChange in case it messes
01098   // up GL somehow, but I need the window's black background to cover
01099   // up the desktop during the mode change.
01100 
01101   if (fullscreen){
01102     if (!do_fullscreen_enable()){
01103       return false;
01104     }
01105   }
01106 
01107   return true;
01108 }
01109 
01110 ////////////////////////////////////////////////////////////////////
01111 //     Function: WinGraphicsWindow::do_fullscreen_enable
01112 //       Access: Private
01113 //  Description: This is a low-level function that just puts Windows
01114 //               in fullscreen mode. Not to confuse with
01115 //               do_fullscreen_switch().
01116 ////////////////////////////////////////////////////////////////////
01117 bool WinGraphicsWindow::
01118 do_fullscreen_enable() {
01119 
01120   HWND hDesktopWindow = GetDesktopWindow();
01121   HDC scrnDC = GetDC(hDesktopWindow);
01122   DWORD cur_bitdepth = GetDeviceCaps(scrnDC, BITSPIXEL);
01123   //  DWORD drvr_ver = GetDeviceCaps(scrnDC, DRIVERVERSION);
01124   //  DWORD cur_scrnwidth = GetDeviceCaps(scrnDC, HORZRES);
01125   //  DWORD cur_scrnheight = GetDeviceCaps(scrnDC, VERTRES);
01126   ReleaseDC(hDesktopWindow, scrnDC);
01127   
01128   DWORD dwWidth = _properties.get_x_size();
01129   DWORD dwHeight = _properties.get_y_size();
01130   DWORD dwFullScreenBitDepth = cur_bitdepth;
01131 
01132   DEVMODE dm;
01133   reconsider_fullscreen_size(dwWidth, dwHeight, dwFullScreenBitDepth);
01134   if (!find_acceptable_display_mode(dwWidth, dwHeight, dwFullScreenBitDepth, dm)) {
01135     windisplay_cat.error() 
01136       << "Videocard has no supported display resolutions at specified res ("
01137       << dwWidth << " x " << dwHeight << " x " << dwFullScreenBitDepth <<")\n";
01138     return false;
01139   }
01140 
01141   dm.dmPelsWidth = dwWidth;
01142   dm.dmPelsHeight = dwHeight;
01143   dm.dmBitsPerPel = dwFullScreenBitDepth;
01144   int chg_result = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
01145 
01146   if (chg_result != DISP_CHANGE_SUCCESSFUL) {
01147     windisplay_cat.error()
01148       << "ChangeDisplaySettings failed (error code: "
01149       << chg_result << ") for specified res: "
01150       << dm.dmPelsWidth << " x " << dm.dmPelsHeight
01151       << " x " << dm.dmBitsPerPel << ", " 
01152       << dm.dmDisplayFrequency << " Hz\n";
01153     return false;
01154   }
01155 
01156   _fullscreen_display_mode = dm;
01157 
01158   _properties.set_origin(0, 0);
01159   _properties.set_size(dwWidth, dwHeight);
01160 
01161   return true;
01162 
01163 }
01164 
01165 ////////////////////////////////////////////////////////////////////
01166 //     Function: WinGraphicsWindow::do_fullscreen_disable
01167 //       Access: Private
01168 //  Description: This is a low-level function that just gets Windows
01169 //               out of fullscreen mode. Not to confuse with
01170 //               do_windowed_switch().
01171 ////////////////////////////////////////////////////////////////////
01172 bool WinGraphicsWindow::
01173 do_fullscreen_disable() {
01174   int chg_result = ChangeDisplaySettings(NULL, 0x0);
01175   if (chg_result != DISP_CHANGE_SUCCESSFUL) {
01176     windisplay_cat.warning()
01177       << "ChangeDisplaySettings failed to restore Windowed mode\n";
01178     return false;
01179   }
01180   return true;
01181 }
01182 
01183 ////////////////////////////////////////////////////////////////////
01184 //     Function: WinGraphicsWindow::adjust_z_order
01185 //       Access: Private
01186 //  Description: Adjusts the Z-order of a window after it has been
01187 //               moved.
01188 ////////////////////////////////////////////////////////////////////
01189 void WinGraphicsWindow::
01190 adjust_z_order() {
01191   WindowProperties::ZOrder z_order = _properties.get_z_order();
01192   adjust_z_order(z_order, z_order);
01193 }
01194 
01195 ////////////////////////////////////////////////////////////////////
01196 //     Function: WinGraphicsWindow::adjust_z_order
01197 //       Access: Private
01198 //  Description: Adjusts the Z-order of a window after it has been
01199 //               moved.
01200 ////////////////////////////////////////////////////////////////////
01201 void WinGraphicsWindow::
01202 adjust_z_order(WindowProperties::ZOrder last_z_order,
01203                WindowProperties::ZOrder this_z_order) {
01204   HWND order;
01205   bool do_change = false;
01206   
01207   switch (this_z_order) {
01208   case WindowProperties::Z_bottom:
01209     order = HWND_BOTTOM;
01210     do_change = true;
01211     break;
01212     
01213   case WindowProperties::Z_normal:
01214     if ((last_z_order != WindowProperties::Z_normal) &&
01215         // If we aren't changing the window order, don't move it to
01216         // the top.
01217         (last_z_order != WindowProperties::Z_bottom ||
01218          _properties.get_foreground())
01219         // If the window was previously on the bottom, but it doesn't
01220         // have focus now, don't move it to the top; it will get moved
01221         // the next time we get focus.
01222         ) {
01223       order = HWND_TOP;
01224       do_change = true;
01225     }
01226     break;
01227     
01228   case WindowProperties::Z_top:
01229     order = HWND_TOPMOST;
01230     do_change = true;
01231     break;
01232   }
01233   if (do_change) {
01234     BOOL result = SetWindowPos(_hWnd, order, 0,0,0,0, 
01235                                SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE);
01236     if (!result) {
01237       windisplay_cat.warning()
01238         << "SetWindowPos failed.\n";
01239     }
01240   }
01241 }
01242 
01243 ////////////////////////////////////////////////////////////////////
01244 //     Function: WinGraphicsWindow::track_mouse_leaving
01245 //       Access: Private
01246 //  Description: Intended to be called whenever mouse motion is
01247 //               detected within the window, this indicates that the
01248 //               mouse is within the window and tells Windows that we
01249 //               want to be told when the mouse leaves the window.
01250 ////////////////////////////////////////////////////////////////////
01251 void WinGraphicsWindow::
01252 track_mouse_leaving(HWND hwnd) {
01253   // Note: could use _TrackMouseEvent in comctrl32.dll (part of IE
01254   // 3.0+) which emulates TrackMouseEvent on w95, but that requires
01255   // another 500K of memory to hold that DLL, which is lame just to
01256   // support w95, which probably has other issues anyway
01257   WinGraphicsPipe *winpipe;
01258   DCAST_INTO_V(winpipe, _pipe);
01259 
01260   if (winpipe->_pfnTrackMouseEvent != NULL) {
01261     TRACKMOUSEEVENT tme = {
01262       sizeof(TRACKMOUSEEVENT),
01263       TME_LEAVE,
01264       hwnd,
01265       0
01266     };
01267 
01268     // tell win32 to post WM_MOUSELEAVE msgs
01269     BOOL bSucceeded = winpipe->_pfnTrackMouseEvent(&tme);  
01270     
01271     if ((!bSucceeded) && windisplay_cat.is_debug()) {
01272       windisplay_cat.debug()
01273         << "TrackMouseEvent failed!, LastError=" << GetLastError() << endl;
01274     }
01275     
01276     _tracking_mouse_leaving = true;
01277   }
01278 }
01279 
01280 ////////////////////////////////////////////////////////////////////
01281 //     Function: WinGraphicsWindow::set_focus
01282 //       Access: Private
01283 //  Description: Attempts to set this window as the "focus" window, so
01284 //               that keyboard events come here.
01285 ////////////////////////////////////////////////////////////////////
01286 void WinGraphicsWindow::
01287 set_focus() {
01288   if (SetFocus(_hWnd) == NULL && GetLastError() != 0) {
01289     // If the SetFocus() request failed, maybe we're running in the
01290     // plugin environment on Vista, with UAC enabled.  In this case,
01291     // we're not allowed to assign focus to the Panda window for some
01292     // stupid reason.  So instead, we have to ask the parent window
01293     // (in the browser process) to proxy our keyboard events for us.
01294     if (_parent_window_handle != NULL && _window_handle != NULL) {
01295       _parent_window_handle->request_keyboard_focus(_window_handle);
01296     } else {
01297       // Otherwise, something is wrong.
01298       windisplay_cat.error()
01299         << "SetFocus failed: " << GetLastError() << "\n";
01300     }
01301   }
01302 }
01303 
01304 ////////////////////////////////////////////////////////////////////
01305 //     Function: WinGraphicsWindow::receive_windows_message
01306 //       Access: Public
01307 //  Description: This is called to receive a keyboard event generated
01308 //               by proxy by another window in a parent process.  This
01309 //               hacky system is used in the web plugin system to
01310 //               allow the Panda window to receive keyboard events on
01311 //               Vista, which doesn't allow the Panda window to set
01312 //               keyboard focus to itself.
01313 ////////////////////////////////////////////////////////////////////
01314 void WinGraphicsWindow::
01315 receive_windows_message(unsigned int msg, int wparam, int lparam) {
01316   // Well, we'll just deliver this directly to window_proc(),
01317   // supplying our own window handle.  For the most part, we don't
01318   // care about the window handle anyway, but this might become an
01319   // issue for the IME.  TODO: investigate IME issues.
01320 
01321   window_proc(_hWnd, msg, wparam, lparam);
01322 }
01323 
01324 ////////////////////////////////////////////////////////////////////
01325 //     Function: WinGraphicsWindow::window_proc
01326 //       Access: Public, Virtual
01327 //  Description: This is the nonstatic window_proc function.  It is
01328 //               called to handle window events for this particular
01329 //               window.
01330 ////////////////////////////////////////////////////////////////////
01331 LONG WinGraphicsWindow::
01332 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
01333   if (windisplay_cat.is_spam()) {
01334     windisplay_cat.spam()
01335       << ClockObject::get_global_clock()->get_real_time() 
01336       << " window_proc(" << (void *)this << ", " << hwnd << ", "
01337       << msg << ", " << wparam << ", " << lparam << ")\n";
01338   }
01339   WindowProperties properties;
01340   int button = -1;
01341 
01342   switch (msg) {
01343   case WM_MOUSEMOVE: 
01344     if (!_tracking_mouse_leaving) {
01345       // need to re-call TrackMouseEvent every time mouse re-enters window
01346       track_mouse_leaving(hwnd);
01347     }
01348     set_cursor_in_window();
01349     if(handle_mouse_motion(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam))))
01350       return 0;
01351     break;
01352     
01353   case WM_INPUT:
01354     handle_raw_input((HRAWINPUT)lparam);
01355     break;
01356       
01357   case WM_MOUSELEAVE:
01358     _tracking_mouse_leaving = false;
01359     handle_mouse_exit();
01360     set_cursor_out_of_window();
01361     break;
01362     
01363   case WM_CREATE: 
01364     {
01365       track_mouse_leaving(hwnd);
01366       ClearToBlack(hwnd, _properties);
01367           
01368       POINT cpos;
01369       GetCursorPos(&cpos);
01370       ScreenToClient(hwnd, &cpos);
01371       RECT clientRect;
01372       GetClientRect(hwnd, &clientRect);
01373       if (PtInRect(&clientRect,cpos)) {
01374         set_cursor_in_window();  // should window focus be true as well?
01375       } else {
01376         set_cursor_out_of_window();
01377       }
01378     }
01379     break;
01380 
01381     /*
01382   case WM_SHOWWINDOW:
01383     // You'd think WM_SHOWWINDOW would be just the thing for embedded
01384     // windows, but it turns out it's not sent to the child windows
01385     // when the parent is minimized.  I guess it's only sent for an
01386     // explicit call to ShowWindow, phooey.
01387     {
01388       if (windisplay_cat.is_debug()) {
01389         windisplay_cat.debug()
01390           << "WM_SHOWWINDOW: " << hwnd << ", " << wparam << "\n";
01391       }
01392       if (wparam) {
01393         // Window is being shown.
01394         properties.set_minimized(false);
01395       } else {
01396         // Window is being hidden.
01397         properties.set_minimized(true);
01398       }
01399       system_changed_properties(properties);
01400     }
01401     break;
01402     */
01403         
01404   case WM_CLOSE:
01405     // This is a message from the system indicating that the user
01406     // has requested to close the window (e.g. alt-f4).
01407     {
01408       string close_request_event = get_close_request_event();
01409       if (!close_request_event.empty()) {
01410         // In this case, the app has indicated a desire to intercept
01411         // the request and process it directly.
01412         throw_event(close_request_event);
01413         return 0;
01414             
01415       } else {
01416         // In this case, the default case, the app does not intend
01417         // to service the request, so we do by closing the window.
01418         close_window();
01419         properties.set_open(false);
01420         system_changed_properties(properties);
01421             
01422         // TODO: make sure we release the GSG properly.
01423       }
01424     }
01425     break;
01426 
01427   case WM_CHILDACTIVATE:
01428     if (windisplay_cat.is_debug()) {
01429       windisplay_cat.debug()
01430         << "WM_CHILDACTIVATE: " << hwnd << "\n";
01431     }
01432     break;
01433     
01434   case WM_ACTIVATE:
01435     if (windisplay_cat.is_debug()) {
01436       windisplay_cat.debug()
01437         << "WM_ACTIVATE: " << hwnd << ", " << wparam << ", " << lparam << "\n";
01438     }
01439     properties.set_minimized((wparam & 0xffff0000) != 0);
01440     if ((wparam & 0xffff) != WA_INACTIVE) 
01441       {
01442         properties.set_foreground(true);
01443         if (is_fullscreen()) 
01444           {
01445             // When a fullscreen window goes active, it automatically gets
01446             // un-minimized.
01447             int chg_result =
01448               ChangeDisplaySettings(&_fullscreen_display_mode, CDS_FULLSCREEN);
01449             if (chg_result != DISP_CHANGE_SUCCESSFUL) {
01450               const DEVMODE &dm = _fullscreen_display_mode;
01451               windisplay_cat.error()
01452                 << "restore ChangeDisplaySettings failed (error code: " 
01453                 << chg_result << ") for specified res: "
01454                 << dm.dmPelsWidth << " x " << dm.dmPelsHeight
01455                 << " x " << dm.dmBitsPerPel << ", " 
01456                 << dm.dmDisplayFrequency << " Hz\n";
01457             }
01458 
01459             GdiFlush();
01460             SetWindowPos(_hWnd, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOOWNERZORDER);
01461             fullscreen_restored(properties);
01462           }
01463       }
01464     else 
01465       {
01466         properties.set_foreground(false);
01467         if (is_fullscreen()) 
01468           {
01469             // When a fullscreen window goes inactive, it automatically
01470             // gets minimized.
01471             properties.set_minimized(true);
01472     
01473             // It seems order is important here.  We must minimize the
01474             // window before restoring the display settings, or risk
01475             // losing the graphics context.
01476             ShowWindow(_hWnd, SW_MINIMIZE);
01477             GdiFlush();
01478             do_fullscreen_disable();
01479             fullscreen_minimized(properties);
01480           }
01481       }
01482 
01483     adjust_z_order();
01484     system_changed_properties(properties);
01485     break;
01486     
01487   case WM_SIZE:
01488     if (windisplay_cat.is_debug()) {
01489       windisplay_cat.debug()
01490         << "WM_SIZE: " << hwnd << ", " << wparam << "\n";
01491     }
01492     // for maximized, unmaximize, need to call resize code
01493     // artificially since no WM_EXITSIZEMOVE is generated.
01494     if (wparam == SIZE_MAXIMIZED) {
01495       _maximized = true;
01496       handle_reshape();
01497     
01498     } else if (wparam == SIZE_RESTORED && _maximized) {
01499       // SIZE_RESTORED might mean we restored to its original size
01500       // before the maximize, but it might also be called while the
01501       // user is resizing the window by hand.  Checking the _maximized
01502       // flag that we set above allows us to differentiate the two
01503       // cases.
01504       _maximized = false;
01505       handle_reshape();
01506     }
01507     break;
01508 
01509   case WM_EXITSIZEMOVE:
01510     handle_reshape();
01511     break;
01512 
01513   case WM_WINDOWPOSCHANGED:
01514     adjust_z_order();
01515     break;
01516 
01517   case WM_PAINT:
01518     // In response to WM_PAINT, we check to see if there are any
01519     // update regions at all; if there are, we declare the window
01520     // exposed.  This is used to implement !_unexposed_draw.
01521     if (GetUpdateRect(_hWnd, NULL, false)) {
01522       if (windisplay_cat.is_spam()) {
01523         windisplay_cat.spam()
01524           << "Got update regions: " << this << "\n";
01525       }
01526       _got_expose_event = true;
01527     }
01528     break;
01529     
01530   case WM_LBUTTONDOWN:
01531     if (_lost_keypresses) {
01532       resend_lost_keypresses();
01533     }
01534     SetCapture(hwnd);
01535     _input_devices[0].set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam)));
01536     _input_devices[0].button_down(MouseButton::button(0), get_message_time());
01537 
01538     // A button-click in the window means to grab the keyboard focus.
01539     set_focus();
01540     return 0;
01541         
01542   case WM_MBUTTONDOWN:
01543     if (_lost_keypresses) {
01544       resend_lost_keypresses();
01545     }
01546     SetCapture(hwnd);
01547     _input_devices[0].set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam)));
01548     _input_devices[0].button_down(MouseButton::button(1), get_message_time());
01549     // A button-click in the window means to grab the keyboard focus.
01550     set_focus();
01551     return 0;
01552 
01553   case WM_RBUTTONDOWN:
01554     if (_lost_keypresses) {
01555       resend_lost_keypresses();
01556     }
01557     SetCapture(hwnd);
01558     _input_devices[0].set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam)));
01559     _input_devices[0].button_down(MouseButton::button(2), get_message_time());
01560     // A button-click in the window means to grab the keyboard focus.
01561     set_focus();
01562     return 0;
01563 
01564   case WM_XBUTTONDOWN:
01565     {
01566       if (_lost_keypresses) {
01567         resend_lost_keypresses();
01568       }
01569       SetCapture(hwnd);
01570       int whichButton = GET_XBUTTON_WPARAM(wparam);
01571       _input_devices[0].set_pointer_in_window(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam)));
01572       if (whichButton == XBUTTON1) {
01573         _input_devices[0].button_down(MouseButton::button(3), get_message_time());
01574       } else if (whichButton == XBUTTON2) {
01575         _input_devices[0].button_down(MouseButton::button(4), get_message_time());
01576       }
01577     }
01578     return 0;
01579     
01580   case WM_LBUTTONUP:
01581     if (_lost_keypresses) {
01582       resend_lost_keypresses();
01583     }
01584     ReleaseCapture();
01585     _input_devices[0].button_up(MouseButton::button(0), get_message_time());
01586     return 0;
01587 
01588   case WM_MBUTTONUP:
01589     if (_lost_keypresses) {
01590       resend_lost_keypresses();
01591     }
01592     ReleaseCapture();
01593     _input_devices[0].button_up(MouseButton::button(1), get_message_time());
01594     return 0;
01595 
01596   case WM_RBUTTONUP:
01597     if (_lost_keypresses) {
01598       resend_lost_keypresses();
01599     }
01600     ReleaseCapture();
01601     _input_devices[0].button_up(MouseButton::button(2), get_message_time());
01602     return 0;
01603 
01604   case WM_XBUTTONUP:
01605     {
01606       if (_lost_keypresses) {
01607         resend_lost_keypresses();
01608       }
01609       ReleaseCapture();
01610       int whichButton = GET_XBUTTON_WPARAM(wparam);
01611       if (whichButton == XBUTTON1) {
01612         _input_devices[0].button_up(MouseButton::button(3), get_message_time());
01613       } else if (whichButton == XBUTTON2) {
01614         _input_devices[0].button_up(MouseButton::button(4), get_message_time());
01615       }
01616     }
01617     return 0;
01618 
01619   case WM_MOUSEWHEEL:
01620     {
01621       int delta = GET_WHEEL_DELTA_WPARAM(wparam);
01622 
01623       POINT point;
01624       GetCursorPos(&point);
01625       ScreenToClient(hwnd, &point);
01626       double time = get_message_time();
01627 
01628       if (delta >= 0) {
01629         while (delta > 0) {
01630           handle_keypress(MouseButton::wheel_up(), point.x, point.y, time);
01631           handle_keyrelease(MouseButton::wheel_up(), time);
01632           delta -= WHEEL_DELTA;
01633         }
01634       } else {
01635         while (delta < 0) {
01636           handle_keypress(MouseButton::wheel_down(), point.x, point.y, time);
01637           handle_keyrelease(MouseButton::wheel_down(), time);
01638           delta += WHEEL_DELTA;
01639         }
01640       }
01641       return 0;
01642     }
01643     break;
01644 
01645         
01646   case WM_IME_SETCONTEXT:
01647     if (!ime_hide)
01648       break;
01649 
01650     windisplay_cat.debug() << "hwnd = " << hwnd << " and GetFocus = " << GetFocus() << endl;
01651     _ime_hWnd = ImmGetDefaultIMEWnd(hwnd);
01652     if (::SendMessage(_ime_hWnd, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0))
01653       //if (::SendMessage(hwnd, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0))
01654       windisplay_cat.debug() << "SendMessage failed for " << _ime_hWnd << endl;
01655     else
01656       windisplay_cat.debug() << "SendMessage Succeeded for " << _ime_hWnd << endl;
01657         
01658     windisplay_cat.debug() << "wparam is " << wparam << ", lparam is " << lparam << endl;
01659     lparam &= ~ISC_SHOWUIALL;
01660     if (ImmIsUIMessage(_ime_hWnd, msg, wparam, lparam))
01661       windisplay_cat.debug() << "wparam is " << wparam << ", lparam is " << lparam << endl;
01662         
01663     break;
01664 
01665         
01666   case WM_IME_NOTIFY:
01667     if (wparam == IMN_SETOPENSTATUS) {
01668       HIMC hIMC = ImmGetContext(hwnd);
01669       nassertr(hIMC != 0, 0);
01670       _ime_open = (ImmGetOpenStatus(hIMC) != 0);
01671       if (!_ime_open) {
01672         _ime_active = false;  // Sanity enforcement.
01673       }
01674       if (ime_hide) {
01675         //if (0) {
01676         COMPOSITIONFORM comf;
01677         CANDIDATEFORM canf;
01678         ImmGetCompositionWindow(hIMC, &comf);
01679         ImmGetCandidateWindow(hIMC, 0, &canf);
01680         windisplay_cat.debug() << 
01681           "comf style " << comf.dwStyle << 
01682           " comf point: x" << comf.ptCurrentPos.x << ",y " << comf.ptCurrentPos.y <<
01683           " comf rect: l " << comf.rcArea.left << ",t " << comf.rcArea.top << ",r " <<
01684           comf.rcArea.right << ",b " << comf.rcArea.bottom << endl;
01685         windisplay_cat.debug() << 
01686           "canf style " << canf.dwStyle << 
01687           " canf point: x" << canf.ptCurrentPos.x << ",y " << canf.ptCurrentPos.y <<
01688           " canf rect: l " << canf.rcArea.left << ",t " << canf.rcArea.top << ",r " <<
01689           canf.rcArea.right << ",b " << canf.rcArea.bottom << endl;
01690         comf.dwStyle = CFS_POINT;
01691         comf.ptCurrentPos.x = 2000;
01692         comf.ptCurrentPos.y = 2000;
01693             
01694         canf.dwStyle = CFS_EXCLUDE;
01695         canf.dwIndex = 0;
01696         canf.ptCurrentPos.x = 0;
01697         canf.ptCurrentPos.y = 0;
01698         canf.rcArea.left = 0;
01699         canf.rcArea.top = 0;
01700         canf.rcArea.right = 640;
01701         canf.rcArea.bottom = 480;
01702             
01703 #if 0
01704         comf.rcArea.left = 200;
01705         comf.rcArea.top = 200;
01706         comf.rcArea.right = 0;
01707         comf.rcArea.bottom = 0;
01708 #endif
01709             
01710         if (ImmSetCompositionWindow(hIMC, &comf))
01711           windisplay_cat.debug() << "set composition form: success\n";
01712         for (int i=0; i<3; ++i) {
01713           if (ImmSetCandidateWindow(hIMC, &canf))
01714             windisplay_cat.debug() << "set candidate form: success\n";
01715           canf.dwIndex++;
01716         }
01717       }
01718 
01719       ImmReleaseContext(hwnd, hIMC);
01720     }
01721     break;
01722         
01723   case WM_IME_STARTCOMPOSITION:
01724     support_overlay_window(true);
01725     _ime_active = true;
01726     break;
01727         
01728   case WM_IME_ENDCOMPOSITION:
01729     support_overlay_window(false);
01730     _ime_active = false;
01731 
01732     if (ime_aware) {
01733       wstring ws;
01734       _input_devices[0].candidate(ws, 0, 0, 0);
01735     }
01736           
01737     break;
01738         
01739   case WM_IME_COMPOSITION:
01740     if (ime_aware) {
01741 
01742       // If the ime window is not marked as active at this point, we
01743       // must be in the process of closing it down (in close_ime), and
01744       // we don't want to send the current composition string in that
01745       // case.  But we do need to return 0 to tell windows not to try
01746       // to send the composition string through WM_CHAR messages.
01747       if (!_ime_active) {
01748         return 0;
01749       }
01750 
01751       HIMC hIMC = ImmGetContext(hwnd);
01752       nassertr(hIMC != 0, 0);
01753           
01754       DWORD result_size = 0;
01755       static const int ime_buffer_size = 256;
01756       static const int ime_buffer_size_bytes = ime_buffer_size / sizeof(wchar_t);
01757       wchar_t ime_buffer[ime_buffer_size];
01758       size_t cursor_pos, delta_start;
01759           
01760       if (lparam & GCS_RESULTSTR) {
01761         result_size = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR,
01762                                                ime_buffer, ime_buffer_size_bytes);
01763         size_t num_chars = result_size / sizeof(wchar_t);
01764         for (size_t i = 0; i < num_chars; ++i) {
01765           _input_devices[0].keystroke(ime_buffer[i]);
01766         }
01767       }
01768 
01769       if (lparam & GCS_COMPSTR) {
01770         result_size = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, NULL, 0);
01771         cursor_pos = result_size & 0xffff;
01772               
01773         result_size = ImmGetCompositionStringW(hIMC, GCS_DELTASTART, NULL, 0);
01774         delta_start = result_size & 0xffff;
01775         result_size = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, ime_buffer, ime_buffer_size);
01776         size_t num_chars = result_size / sizeof(wchar_t);
01777               
01778         _input_devices[0].candidate(wstring(ime_buffer, num_chars), 
01779                                     min(cursor_pos, delta_start), 
01780                                     max(cursor_pos, delta_start), 
01781                                     cursor_pos);
01782       }
01783       ImmReleaseContext(hwnd, hIMC);
01784     }
01785     break;
01786         
01787   case WM_CHAR:
01788     // Ignore WM_CHAR messages if we have the IME open, since
01789     // everything will come in through WM_IME_COMPOSITION.  (It's
01790     // supposed to come in through WM_CHAR, too, but there seems to
01791     // be a bug in Win2000 in that it only sends question mark
01792     // characters through here.)
01793 
01794     // Actually, probably that "bug" was due to the fact that we were
01795     // previously using the ANSI versions of RegisterClass etc., in
01796     // which case the actual value passed to WM_CHAR seems to be
01797     // poorly defined.  Now we are using RegisterClassW etc., which
01798     // means WM_CHAR is absolutely supposed to be utf-16.
01799     if (!_ime_open) {
01800       _input_devices[0].keystroke(wparam);
01801     }
01802     break;
01803     
01804   case WM_SYSKEYDOWN: 
01805     if (_lost_keypresses) {
01806       resend_lost_keypresses();
01807     }
01808     if (windisplay_cat.is_debug()) {
01809       windisplay_cat.debug()
01810         << "syskeydown: " << wparam << " (" << lookup_key(wparam) << ")\n";
01811     }
01812     {
01813       // Alt and F10 are sent as WM_SYSKEYDOWN instead of WM_KEYDOWN
01814       // want to use defwindproc on Alt syskey so std windows cmd
01815       // Alt-F4 works, etc
01816       POINT point;
01817       GetCursorPos(&point);
01818       ScreenToClient(hwnd, &point);
01819       handle_keypress(lookup_key(wparam), point.x, point.y, 
01820                       get_message_time());
01821 
01822       // wparam does not contain left/right information for SHIFT,
01823       // CONTROL, or ALT, so we have to track their status and do
01824       // the right thing.  We'll send the left/right specific key
01825       // event along with the general key event.
01826       //
01827       // Key repeating is not being handled consistently for LALT
01828       // and RALT, but from comments below, it's only being handled
01829       // the way it is for backspace, so we'll leave it as is.
01830       if (wparam == VK_MENU) {
01831         if ((GetKeyState(VK_LMENU) & 0x8000) != 0 && ! _lalt_down) {
01832           handle_keypress(KeyboardButton::lalt(), point.x, point.y,
01833                           get_message_time());
01834           _lalt_down = true;
01835         }
01836         if ((GetKeyState(VK_RMENU) & 0x8000) != 0 && ! _ralt_down) {
01837           handle_keypress(KeyboardButton::ralt(), point.x, point.y,
01838                           get_message_time());
01839           _ralt_down = true;
01840         }
01841       }
01842       if (wparam == VK_F10) {
01843         // bypass default windproc F10 behavior (it activates the main
01844         // menu, but we have none)
01845         return 0;
01846       }
01847     }
01848     break;
01849     
01850   case WM_SYSCOMMAND:
01851     if (wparam == SC_KEYMENU) {
01852       // if Alt is released (alone w/o other keys), defwindproc will
01853       // send this command, which will 'activate' the title bar menu
01854       // (we have none) and give focus to it.  we don't want this to
01855       // happen, so kill this msg.
01856 
01857       // Note that the WM_SYSKEYUP message for Alt has already
01858       // been sent (if it is going to be), so ignoring this
01859       // special message does no harm.
01860       return 0;
01861     }
01862     break;
01863         
01864   case WM_KEYDOWN: 
01865     if (_lost_keypresses) {
01866       resend_lost_keypresses();
01867     }
01868     if (windisplay_cat.is_debug()) {
01869       windisplay_cat.debug()
01870         << "keydown: " << wparam << " (" << lookup_key(wparam) << ")\n";
01871     }
01872 
01873     // If this bit is not zero, this is just a keyrepeat echo; we
01874     // ignore these for handle_keypress (we respect keyrepeat only
01875     // for handle_keystroke).
01876     if ((lparam & 0x40000000) == 0) {
01877       POINT point;
01878       GetCursorPos(&point);
01879       ScreenToClient(hwnd, &point);
01880       handle_keypress(lookup_key(wparam), point.x, point.y,
01881                       get_message_time());
01882 
01883       // wparam does not contain left/right information for SHIFT,
01884       // CONTROL, or ALT, so we have to track their status and do
01885       // the right thing.  We'll send the left/right specific key
01886       // event along with the general key event.
01887       if (wparam == VK_SHIFT) {
01888         if ((GetKeyState(VK_LSHIFT) & 0x8000) != 0 && ! _lshift_down) {
01889           handle_keypress(KeyboardButton::lshift(), point.x, point.y,
01890                           get_message_time());
01891           _lshift_down = true;
01892         }
01893         if ((GetKeyState(VK_RSHIFT) & 0x8000) != 0 && ! _rshift_down) {
01894           handle_keypress(KeyboardButton::rshift(), point.x, point.y,
01895                           get_message_time());
01896           _rshift_down = true;
01897         }
01898       } else if(wparam == VK_CONTROL) {
01899         if ((GetKeyState(VK_LCONTROL) & 0x8000) != 0 && ! _lcontrol_down) {
01900           handle_keypress(KeyboardButton::lcontrol(), point.x, point.y,
01901                           get_message_time());
01902           _lcontrol_down = true;
01903         }
01904         if ((GetKeyState(VK_RCONTROL) & 0x8000) != 0 && ! _rcontrol_down) {
01905           handle_keypress(KeyboardButton::rcontrol(), point.x, point.y,
01906                           get_message_time());
01907           _rcontrol_down = true;
01908         }
01909       }
01910 
01911       // Handle Cntrl-V paste from clipboard.  Is there a better way
01912       // to detect this hotkey?
01913       if ((wparam=='V') && (GetKeyState(VK_CONTROL) < 0) &&
01914           !_input_devices.empty()) {
01915         HGLOBAL hglb;
01916         char *lptstr;
01917     
01918         if (IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL)) {
01919           // Maybe we should support CF_UNICODETEXT if it is available
01920           // too?
01921           hglb = GetClipboardData(CF_TEXT);
01922           if (hglb!=NULL) {
01923             lptstr = (char *) GlobalLock(hglb);
01924             if (lptstr != NULL)  {
01925               char *pChar;
01926               for (pChar=lptstr; *pChar!=NULL; pChar++) {
01927                 _input_devices[0].keystroke((uchar)*pChar);
01928               }
01929               GlobalUnlock(hglb);
01930             }
01931           }
01932           CloseClipboard();
01933         }
01934       }
01935     } else {
01936       // Actually, for now we'll respect the repeat anyway, just
01937       // so we support backspace properly.  Rethink later.
01938       POINT point;
01939       GetCursorPos(&point);
01940       ScreenToClient(hwnd, &point);
01941       handle_keypress(lookup_key(wparam), point.x, point.y,
01942                       get_message_time());
01943 
01944       // wparam does not contain left/right information for SHIFT,
01945       // CONTROL, or ALT, so we have to track their status and do
01946       // the right thing.  We'll send the left/right specific key
01947       // event along with the general key event.
01948       //
01949       // If the user presses LSHIFT and then RSHIFT, the RSHIFT event
01950       // will come in with the keyrepeat flag on (i.e. it will end up
01951       // in this block).  The logic below should detect this correctly
01952       // and only send the RSHIFT event.  Note that the CONTROL event
01953       // will be sent twice, once for each keypress.  Since keyrepeats
01954       // are currently being sent simply as additional keypress events,
01955       // that should be okay for now.
01956       if (wparam == VK_SHIFT) {
01957         if (((GetKeyState(VK_LSHIFT) & 0x8000) != 0) && ! _lshift_down ) {
01958           handle_keypress(KeyboardButton::lshift(), point.x, point.y,
01959                           get_message_time());
01960           _lshift_down = true;
01961         } else if (((GetKeyState(VK_RSHIFT) & 0x8000) != 0) && ! _rshift_down ) {
01962           handle_keypress(KeyboardButton::rshift(), point.x, point.y,
01963                           get_message_time());
01964           _rshift_down = true;
01965         } else {
01966           if ((GetKeyState(VK_LSHIFT) & 0x8000) != 0) {
01967             handle_keypress(KeyboardButton::lshift(), point.x, point.y,
01968                             get_message_time());
01969           }
01970           if ((GetKeyState(VK_RSHIFT) & 0x8000) != 0) {
01971             handle_keypress(KeyboardButton::rshift(), point.x, point.y,
01972                             get_message_time());
01973           }
01974         }
01975       } else if(wparam == VK_CONTROL) {
01976         if (((GetKeyState(VK_LCONTROL) & 0x8000) != 0) && ! _lcontrol_down ) {
01977           handle_keypress(KeyboardButton::lcontrol(), point.x, point.y,
01978                           get_message_time());
01979           _lcontrol_down = true;
01980         } else if (((GetKeyState(VK_RCONTROL) & 0x8000) != 0) && ! _rcontrol_down ) {
01981           handle_keypress(KeyboardButton::rcontrol(), point.x, point.y,
01982                           get_message_time());
01983           _rcontrol_down = true;
01984         } else {
01985           if ((GetKeyState(VK_LCONTROL) & 0x8000) != 0) {
01986             handle_keypress(KeyboardButton::lcontrol(), point.x, point.y,
01987                             get_message_time());
01988           }
01989           if ((GetKeyState(VK_RCONTROL) & 0x8000) != 0) {
01990             handle_keypress(KeyboardButton::rcontrol(), point.x, point.y,
01991                             get_message_time());
01992           }
01993         }
01994       }
01995     }
01996     break;
01997     
01998   case WM_SYSKEYUP:
01999   case WM_KEYUP:
02000     if (_lost_keypresses) {
02001       resend_lost_keypresses();
02002     }
02003     if (windisplay_cat.is_debug()) {
02004       windisplay_cat.debug()
02005         << "keyup: " << wparam << " (" << lookup_key(wparam) << ")\n";
02006     }
02007     handle_keyrelease(lookup_key(wparam), get_message_time());
02008 
02009     // wparam does not contain left/right information for SHIFT,
02010     // CONTROL, or ALT, so we have to track their status and do
02011     // the right thing.  We'll send the left/right specific key
02012     // event along with the general key event.
02013     if (wparam == VK_SHIFT) {
02014       if ((GetKeyState(VK_LSHIFT) & 0x8000) == 0 && _lshift_down) {
02015         handle_keyrelease(KeyboardButton::lshift(), get_message_time());
02016         _lshift_down = false;
02017       }
02018       if ((GetKeyState(VK_RSHIFT) & 0x8000) == 0 && _rshift_down) {
02019         handle_keyrelease(KeyboardButton::rshift(), get_message_time());
02020         _rshift_down = false;
02021       }
02022     } else if(wparam == VK_CONTROL) {
02023       if ((GetKeyState(VK_LCONTROL) & 0x8000) == 0 && _lcontrol_down) {
02024         handle_keyrelease(KeyboardButton::lcontrol(), get_message_time());
02025         _lcontrol_down = false;
02026       }
02027       if ((GetKeyState(VK_RCONTROL) & 0x8000) == 0 && _rcontrol_down) {
02028         handle_keyrelease(KeyboardButton::rcontrol(), get_message_time());
02029         _rcontrol_down = false;
02030       }
02031     } else if(wparam == VK_MENU) {
02032       if ((GetKeyState(VK_LMENU) & 0x8000) == 0 && _lalt_down) {
02033         handle_keyrelease(KeyboardButton::lalt(), get_message_time());
02034         _lalt_down = false;
02035       }
02036       if ((GetKeyState(VK_RMENU) & 0x8000) == 0 && _ralt_down) {
02037         handle_keyrelease(KeyboardButton::ralt(), get_message_time());
02038         _ralt_down = false;
02039       }
02040     }
02041     break;
02042     
02043   case WM_KILLFOCUS: 
02044     if (windisplay_cat.is_debug()) {
02045       windisplay_cat.debug()
02046         << "killfocus\n";
02047     }
02048 
02049     _input_devices[0].focus_lost(get_message_time());
02050     properties.set_foreground(false);
02051     system_changed_properties(properties);
02052     break;
02053     
02054   case WM_SETFOCUS: 
02055     // You would think that this would be a good time to call
02056     // resend_lost_keypresses(), but it turns out that we get
02057     // WM_SETFOCUS slightly before Windows starts resending key
02058     // up/down events to us.
02059 
02060     // In particular, if the user restored focus using alt-tab,
02061     // then at this point the keyboard state will indicate that
02062     // both the alt and tab keys are held down.  However, there is
02063     // a small window of opportunity for the user to release these
02064     // keys before Windows starts telling us about keyup events.
02065     // Thus, if we record the fact that alt and tab are being held
02066     // down now, we may miss the keyup events for them, and they
02067     // can get "stuck" down.
02068 
02069     // So we have to defer calling resend_lost_keypresses() until
02070     // we know Windows is ready to send us key up/down events.  I
02071     // don't know when we can guarantee that, except when we
02072     // actually do start to receive key up/down events, so that
02073     // call is made there.
02074 
02075     if (windisplay_cat.is_debug()) {
02076       windisplay_cat.debug()
02077         << "setfocus\n";
02078     }
02079 
02080     if (_lost_keypresses) {
02081       resend_lost_keypresses();
02082     }
02083 
02084     properties.set_foreground(true);
02085     system_changed_properties(properties);
02086     break;
02087 
02088   case PM_ACTIVE:
02089     if (windisplay_cat.is_debug()) {
02090       windisplay_cat.debug()
02091         << "PM_ACTIVE\n";
02092     }
02093     properties.set_foreground(true);
02094     system_changed_properties(properties);
02095     break;
02096 
02097   case PM_INACTIVE:
02098     if (windisplay_cat.is_debug()) {
02099       windisplay_cat.debug()
02100         << "PM_INACTIVE\n";
02101     }
02102     properties.set_foreground(false);
02103     system_changed_properties(properties);
02104     break;
02105 
02106 #ifdef HAVE_WIN_TOUCHINPUT
02107   case WM_TOUCH:
02108         _numTouches = LOWORD(wparam);
02109         if(_numTouches > MAX_TOUCHES)
02110             _numTouches = MAX_TOUCHES;
02111         GetTouchInputInfo((HTOUCHINPUT)lparam, _numTouches, _touches, sizeof(TOUCHINPUT));
02112         CloseTouchInputHandle((HTOUCHINPUT)lparam);
02113     break;
02114 #endif
02115   }
02116 
02117   //do custom messages processing if any has been set
02118   for ( WinProcClasses::iterator it=_window_proc_classes.begin() ; it != _window_proc_classes.end(); it++ ){
02119       (*it)->wnd_proc(this, hwnd, msg, wparam, lparam);
02120   }
02121 
02122   return DefWindowProcW(hwnd, msg, wparam, lparam);
02123 }
02124 
02125 
02126 ////////////////////////////////////////////////////////////////////
02127 //     Function: WinGraphicsWindow::static_window_proc
02128 //       Access: Private, Static
02129 //  Description: This is attached to the window class for all
02130 //               WinGraphicsWindow windows; it is called to handle
02131 //               window events.
02132 ////////////////////////////////////////////////////////////////////
02133 LONG WINAPI WinGraphicsWindow::
02134 static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
02135   // Look up the window in our global map.
02136   WindowHandles::const_iterator wi;
02137   wi = _window_handles.find(hwnd);
02138   if (wi != _window_handles.end()) {
02139     // We found the window.
02140     return (*wi).second->window_proc(hwnd, msg, wparam, lparam);
02141   }
02142 
02143   // The window wasn't in the map; we must be creating it right now.
02144   if (_creating_window != (WinGraphicsWindow *)NULL) {
02145     return _creating_window->window_proc(hwnd, msg, wparam, lparam);
02146   }
02147 
02148   // Oops, we weren't creating a window!  Don't know how to handle the
02149   // message, so just pass it on to Windows to deal with it.
02150   return DefWindowProcW(hwnd, msg, wparam, lparam);
02151 }
02152 
02153 ////////////////////////////////////////////////////////////////////
02154 //     Function: WinGraphicsWindow::process_1_event
02155 //       Access: Private, Static
02156 //  Description: Handles one event from the message queue.
02157 ////////////////////////////////////////////////////////////////////
02158 void WinGraphicsWindow::
02159 process_1_event() {
02160   MSG msg;
02161 
02162   if (!GetMessage(&msg, NULL, 0, 0)) {
02163     // WM_QUIT received.  We need a cleaner way to deal with this.
02164     //    DestroyAllWindows(false);
02165     exit(msg.wParam);  // this will invoke AtExitFn
02166   }
02167 
02168   // Translate virtual key messages
02169   TranslateMessage(&msg);
02170   // Call window_proc
02171   DispatchMessage(&msg);
02172 }
02173 
02174 ////////////////////////////////////////////////////////////////////
02175 //     Function: WinGraphicsWindow::resend_lost_keypresses
02176 //       Access: Private, Static
02177 //  Description: Called when the keyboard focus has been restored to
02178 //               the window after it has been lost for a time, this
02179 //               rechecks the keyboard state and generates key up/down
02180 //               messages for keys that have changed state in the
02181 //               meantime.
02182 ////////////////////////////////////////////////////////////////////
02183 void WinGraphicsWindow::
02184 resend_lost_keypresses() {
02185   nassertv(_lost_keypresses);
02186   // This is now a no-op.  Not sure we really want to generate new
02187   // "down" or "resume" events for keys that were held while the
02188   // window focus is restored.
02189 
02190   _lost_keypresses = false;
02191 }
02192 
02193 ////////////////////////////////////////////////////////////////////
02194 //     Function: WinGraphicsWindow::update_cursor_window
02195 //       Access: Private, Static
02196 //  Description: Changes _cursor_window from its current value to the
02197 //               indicated value.  This also changes the cursor
02198 //               properties appropriately.
02199 ////////////////////////////////////////////////////////////////////
02200 void WinGraphicsWindow::
02201 update_cursor_window(WinGraphicsWindow *to_window) {
02202   bool hide_cursor = false;
02203   if (to_window == (WinGraphicsWindow *)NULL) {
02204     // We are leaving a graphics window; we should restore the Win2000
02205     // effects.
02206     if (_got_saved_params) {
02207       SystemParametersInfo(SPI_SETMOUSETRAILS, NULL, 
02208                            (PVOID)_saved_mouse_trails, NULL);
02209       SystemParametersInfo(SPI_SETCURSORSHADOW, NULL, 
02210                            (PVOID)_saved_cursor_shadow, NULL);
02211       SystemParametersInfo(SPI_SETMOUSEVANISH, NULL,
02212                            (PVOID)_saved_mouse_vanish, NULL);
02213       _got_saved_params = false;
02214     }
02215 
02216   } else {
02217     const WindowProperties &to_props = to_window->get_properties();
02218     hide_cursor = to_props.get_cursor_hidden();
02219 
02220     // We are entering a graphics window; we should save and disable
02221     // the Win2000 effects.  These don't work at all well over a 3-D
02222     // window.
02223 
02224     // These parameters are only defined for Win2000/XP, but they
02225     // should just cause a silent error on earlier OS's, which is OK.
02226     if (!_got_saved_params) {
02227       SystemParametersInfo(SPI_GETMOUSETRAILS, NULL, 
02228                            &_saved_mouse_trails, NULL);
02229       SystemParametersInfo(SPI_GETCURSORSHADOW, NULL, 
02230                            &_saved_cursor_shadow, NULL);
02231       SystemParametersInfo(SPI_GETMOUSEVANISH, NULL, 
02232                            &_saved_mouse_vanish, NULL);
02233       _got_saved_params = true;
02234 
02235       SystemParametersInfo(SPI_SETMOUSETRAILS, NULL, (PVOID)0, NULL);
02236       SystemParametersInfo(SPI_SETCURSORSHADOW, NULL, (PVOID)false, NULL);
02237       SystemParametersInfo(SPI_SETMOUSEVANISH, NULL, (PVOID)false, NULL);
02238     }
02239 
02240     SetCursor(to_window->_cursor);
02241   }
02242   
02243   hide_or_show_cursor(hide_cursor);
02244 
02245   _cursor_window = to_window;
02246 }
02247 
02248 ////////////////////////////////////////////////////////////////////
02249 //     Function: WinGraphicsWindow::hide_or_show_cursor
02250 //       Access: Private, Static
02251 //  Description: Hides or shows the mouse cursor according to the
02252 //               indicated parameter.  This is normally called when
02253 //               the mouse wanders into or out of a window with the
02254 //               cursor_hidden properties.
02255 ////////////////////////////////////////////////////////////////////
02256 void WinGraphicsWindow::
02257 hide_or_show_cursor(bool hide_cursor) {
02258   if (hide_cursor) {
02259     if (!_cursor_hidden) {
02260       ShowCursor(false);
02261       _cursor_hidden = true;
02262     }
02263   } else {
02264     if (_cursor_hidden) {
02265       ShowCursor(true);
02266       _cursor_hidden = false;
02267     }
02268   }
02269 }
02270 
02271 // don't pick any video modes < MIN_REFRESH_RATE Hz
02272 #define MIN_REFRESH_RATE 60
02273 // EnumDisplaySettings may indicate 0 or 1 for refresh rate, which means use driver default rate (assume its >min_refresh_rate)
02274 #define ACCEPTABLE_REFRESH_RATE(RATE) ((RATE >= MIN_REFRESH_RATE) || (RATE==0) || (RATE==1))
02275 
02276 ////////////////////////////////////////////////////////////////////
02277 //     Function: WinGraphicsWindow::find_acceptable_display_mode
02278 //       Access: Private, Static
02279 //  Description: Looks for a fullscreen mode that meets the specified
02280 //               size and bitdepth requirements.  Returns true if a
02281 //               suitable mode is found, false otherwise.
02282 ////////////////////////////////////////////////////////////////////
02283 bool WinGraphicsWindow::
02284 find_acceptable_display_mode(DWORD dwWidth, DWORD dwHeight, DWORD bpp,
02285                              DEVMODE &dm) {
02286   int modenum = 0;
02287 
02288   while (1) {
02289     ZeroMemory(&dm, sizeof(dm));
02290     dm.dmSize = sizeof(dm);
02291     
02292     if (!EnumDisplaySettings(NULL, modenum, &dm)) {
02293       break;
02294     }
02295     
02296     if ((dm.dmPelsWidth == dwWidth) && (dm.dmPelsHeight == dwHeight) &&
02297         (dm.dmBitsPerPel == bpp)) {
02298       return true;
02299     }
02300     modenum++;
02301   }
02302   
02303   return false;
02304 }
02305 
02306 ////////////////////////////////////////////////////////////////////
02307 //     Function: WinGraphicsWindow::show_error_message
02308 //       Access: Private, Static
02309 //  Description: Pops up a dialog box with the indicated Windows error
02310 //               message ID (or the last error message generated) for
02311 //               meaningful display to the user.
02312 ////////////////////////////////////////////////////////////////////
02313 void WinGraphicsWindow::
02314 show_error_message(DWORD message_id) {
02315   LPTSTR message_buffer;
02316 
02317   if (message_id == 0) {
02318     message_id = GetLastError();
02319   }
02320   
02321   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
02322                 NULL, message_id,
02323                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //The user default language
02324                 (LPTSTR)&message_buffer,  // the weird ptrptr->ptr cast is intentional, see FORMAT_MESSAGE_ALLOCATE_BUFFER
02325                 1024, NULL);
02326   MessageBox(GetDesktopWindow(), message_buffer, _T(errorbox_title), MB_OK);
02327   windisplay_cat.fatal() << "System error msg: " << message_buffer << endl;
02328   LocalFree(message_buffer);
02329 }
02330 
02331 ////////////////////////////////////////////////////////////////////
02332 //     Function: WinGraphicsWindow::handle_keypress
02333 //       Access: Private
02334 //  Description:
02335 ////////////////////////////////////////////////////////////////////
02336 void WinGraphicsWindow::
02337 handle_keypress(ButtonHandle key, int x, int y, double time) {
02338   _input_devices[0].set_pointer_in_window(x, y);
02339   if (key != ButtonHandle::none()) {
02340     _input_devices[0].button_down(key, time);
02341   }
02342 }
02343 
02344 ////////////////////////////////////////////////////////////////////
02345 //     Function: WinGraphicsWindow::handle_keyresume
02346 //       Access: Private
02347 //  Description: Indicates we detected a key was already down when the
02348 //               focus is restored to the window.  Mainly useful for
02349 //               tracking the state of modifier keys.
02350 ////////////////////////////////////////////////////////////////////
02351 void WinGraphicsWindow::
02352 handle_keyresume(ButtonHandle key, double time) {
02353   if (key != ButtonHandle::none()) {
02354     _input_devices[0].button_resume_down(key, time);
02355   }
02356 }
02357 
02358 ////////////////////////////////////////////////////////////////////
02359 //     Function: WinGraphicsWindow::handle_keyrelease
02360 //       Access: Private
02361 //  Description:
02362 ////////////////////////////////////////////////////////////////////
02363 void WinGraphicsWindow::
02364 handle_keyrelease(ButtonHandle key, double time) {
02365   if (key != ButtonHandle::none()) {
02366     _input_devices[0].button_up(key, time);
02367   }
02368 }
02369 
02370 ////////////////////////////////////////////////////////////////////
02371 //     Function: WinGraphicsWindow::lookup_key
02372 //       Access: Private
02373 //  Description: Translates the keycode reported by Windows to an
02374 //               appropriate Panda ButtonHandle.
02375 ////////////////////////////////////////////////////////////////////
02376 ButtonHandle WinGraphicsWindow::
02377 lookup_key(WPARAM wparam) const {
02378   // First, check for a few buttons that we filter out when the IME
02379   // window is open.
02380   if (!_ime_active) {
02381     switch(wparam) {
02382     case VK_BACK: return KeyboardButton::backspace();
02383     case VK_DELETE: return KeyboardButton::del();
02384     case VK_ESCAPE: return KeyboardButton::escape();
02385     case VK_SPACE: return KeyboardButton::space();
02386     case VK_UP: return KeyboardButton::up();
02387     case VK_DOWN: return KeyboardButton::down();
02388     case VK_LEFT: return KeyboardButton::left();
02389     case VK_RIGHT: return KeyboardButton::right();
02390     }
02391   }
02392 
02393   // Now check for the rest of the buttons, including the ones that
02394   // we allow through even when the IME window is open.
02395   switch(wparam) {
02396   case VK_TAB: return KeyboardButton::tab();
02397   case VK_PRIOR: return KeyboardButton::page_up();
02398   case VK_NEXT: return KeyboardButton::page_down();
02399   case VK_HOME: return KeyboardButton::home();
02400   case VK_END: return KeyboardButton::end();
02401   case VK_F1: return KeyboardButton::f1();
02402   case VK_F2: return KeyboardButton::f2();
02403   case VK_F3: return KeyboardButton::f3();
02404   case VK_F4: return KeyboardButton::f4();
02405   case VK_F5: return KeyboardButton::f5();
02406   case VK_F6: return KeyboardButton::f6();
02407   case VK_F7: return KeyboardButton::f7();
02408   case VK_F8: return KeyboardButton::f8();
02409   case VK_F9: return KeyboardButton::f9();
02410   case VK_F10: return KeyboardButton::f10();
02411   case VK_F11: return KeyboardButton::f11();
02412   case VK_F12: return KeyboardButton::f12();
02413   case VK_INSERT: return KeyboardButton::insert();
02414   case VK_CAPITAL: return KeyboardButton::caps_lock();
02415   case VK_NUMLOCK: return KeyboardButton::num_lock();
02416   case VK_SCROLL: return KeyboardButton::scroll_lock();
02417   case VK_PAUSE: return KeyboardButton::pause();
02418   case VK_SNAPSHOT: return KeyboardButton::print_screen();
02419 
02420   case VK_SHIFT: return KeyboardButton::shift();
02421   case VK_LSHIFT: return KeyboardButton::lshift();
02422   case VK_RSHIFT: return KeyboardButton::rshift();
02423 
02424   case VK_CONTROL: return KeyboardButton::control();
02425   case VK_LCONTROL: return KeyboardButton::lcontrol();
02426   case VK_RCONTROL: return KeyboardButton::rcontrol();
02427 
02428   case VK_MENU: return KeyboardButton::alt();
02429   case VK_LMENU: return KeyboardButton::lalt();
02430   case VK_RMENU: return KeyboardButton::ralt();
02431 
02432   default:
02433     int key = MapVirtualKey(wparam, 2);
02434     if (isascii(key) && key != 0) {
02435       // We used to try to remap lowercase to uppercase keys
02436       // here based on the state of the shift and/or caps lock
02437       // keys.  But that's a mistake, and doesn't allow for
02438       // international or user-defined keyboards; let Windows
02439       // do that mapping.
02440 
02441       // Nowadays, we make a distinction between a "button"
02442       // and a "keystroke".  A button corresponds to a
02443       // physical button on the keyboard and has a down and up
02444       // event associated.  A keystroke may or may not
02445       // correspond to a physical button, but will be some
02446       // Unicode character and will not have a corresponding
02447       // up event.
02448       return KeyboardButton::ascii_key(tolower(key));
02449     }
02450     break;
02451   }
02452   return ButtonHandle::none();
02453 }
02454 
02455 ////////////////////////////////////////////////////////////////////
02456 //     Function: WinGraphicsWindow::handle_raw_input
02457 //       Access: Private
02458 //  Description:
02459 ////////////////////////////////////////////////////////////////////
02460 void WinGraphicsWindow::
02461 handle_raw_input(HRAWINPUT hraw) {
02462   LPBYTE lpb;
02463   UINT dwSize;
02464 
02465   if (hraw == 0) {
02466     return;
02467   }
02468   if (pGetRawInputData(hraw, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)) == -1) {
02469     return;
02470   }
02471   
02472   lpb = (LPBYTE)alloca(sizeof(LPBYTE) * dwSize);
02473   if (lpb == NULL) {
02474     return;
02475   }
02476   
02477   if (pGetRawInputData(hraw, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) {
02478     return;
02479   }
02480   
02481   RAWINPUT *raw = (RAWINPUT *)lpb;
02482   if (raw->header.hDevice == 0) {
02483     return;
02484   }
02485   
02486   for (int i = 1; i < (int)(_input_devices.size()); ++i) {
02487     if (_input_device_handle[i] == raw->header.hDevice) {
02488       int adjx = raw->data.mouse.lLastX;
02489       int adjy = raw->data.mouse.lLastY;
02490       
02491       if (raw->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) {
02492         _input_devices[i].set_pointer_in_window(adjx, adjy);
02493       } else {
02494         int oldx = _input_devices[i].get_raw_pointer().get_x();
02495         int oldy = _input_devices[i].get_raw_pointer().get_y();
02496         _input_devices[i].set_pointer_in_window(oldx + adjx, oldy + adjy);
02497       }
02498       
02499       if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) {
02500         _input_devices[i].button_down(MouseButton::button(0), get_message_time());
02501       }
02502       if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP) {
02503         _input_devices[i].button_up(MouseButton::button(0), get_message_time());
02504       }
02505       if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) {
02506         _input_devices[i].button_down(MouseButton::button(2), get_message_time());
02507       }
02508       if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP) {
02509         _input_devices[i].button_up(MouseButton::button(2), get_message_time());
02510       }
02511       if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) {
02512         _input_devices[i].button_down(MouseButton::button(1), get_message_time());
02513       }
02514       if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP) {
02515         _input_devices[i].button_up(MouseButton::button(1), get_message_time());
02516       }
02517       if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) {
02518         _input_devices[i].button_down(MouseButton::button(3), get_message_time());
02519       }
02520       if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP) {
02521         _input_devices[i].button_up(MouseButton::button(3), get_message_time());
02522       }
02523       if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) {
02524         _input_devices[i].button_down(MouseButton::button(4), get_message_time());
02525       }
02526       if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP) {
02527         _input_devices[i].button_up(MouseButton::button(4), get_message_time());
02528       }
02529     }
02530   }
02531 }
02532 
02533 ////////////////////////////////////////////////////////////////////
02534 //     Function: WinGraphicsWindow::handle_mouse_motion
02535 //       Access: Private
02536 //  Description:
02537 ////////////////////////////////////////////////////////////////////
02538 bool WinGraphicsWindow::
02539 handle_mouse_motion(int x, int y) {
02540   _input_devices[0].set_pointer_in_window(x, y);
02541   return false;
02542 }
02543 
02544 ////////////////////////////////////////////////////////////////////
02545 //     Function: WinGraphicsWindow::handle_mouse_exit
02546 //       Access: Private
02547 //  Description:
02548 ////////////////////////////////////////////////////////////////////
02549 void WinGraphicsWindow::
02550 handle_mouse_exit() {
02551   // note: 'mouse_motion' is considered the 'entry' event
02552   _input_devices[0].set_pointer_out_of_window();
02553 }
02554 
02555 ////////////////////////////////////////////////////////////////////
02556 //     Function: WinGraphicsWindow::get_icon
02557 //       Access: Private, Static
02558 //  Description: Loads and returns an HICON corresponding to the
02559 //               indicated filename.  If the file cannot be loaded,
02560 //               returns 0.
02561 ////////////////////////////////////////////////////////////////////
02562 HICON WinGraphicsWindow::
02563 get_icon(const Filename &filename) {
02564   // First, look for the unresolved filename in our index.
02565   IconFilenames::iterator fi = _icon_filenames.find(filename);
02566   if (fi != _icon_filenames.end()) {
02567     return (HICON)((*fi).second);
02568   }
02569 
02570   // If it wasn't found, resolve the filename and search for that.
02571 
02572   // Since we have to use a Windows call to load the image from a
02573   // filename, we can't load a virtual file and we can't use the
02574   // virtual file system.
02575   Filename resolved = filename;
02576   if (!resolved.resolve_filename(get_model_path())) {
02577     // The filename doesn't exist along the search path.
02578     if (resolved.is_fully_qualified() && resolved.exists()) {
02579       // But it does exist locally, so accept it.
02580 
02581     } else {
02582       windisplay_cat.warning()
02583         << "Could not find icon filename " << filename << "\n";
02584       return 0;
02585     }
02586   }
02587   fi = _icon_filenames.find(resolved);
02588   if (fi != _icon_filenames.end()) {
02589     _icon_filenames[filename] = (*fi).second;
02590     return (HICON)((*fi).second);
02591   }
02592 
02593   Filename os = resolved.to_os_specific();
02594 
02595   HANDLE h = LoadImage(NULL, os.c_str(),
02596                        IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
02597   if (h == 0) {
02598     windisplay_cat.warning()
02599       << "windows icon filename '" << os << "' could not be loaded!!\n";
02600   }
02601 
02602   _icon_filenames[filename] = h;
02603   _icon_filenames[resolved] = h;
02604   return (HICON)h;
02605 }
02606 
02607 ////////////////////////////////////////////////////////////////////
02608 //     Function: WinGraphicsWindow::get_cursor
02609 //       Access: Private, Static
02610 //  Description: Loads and returns an HCURSOR corresponding to the
02611 //               indicated filename.  If the file cannot be loaded,
02612 //               returns 0.
02613 ////////////////////////////////////////////////////////////////////
02614 HCURSOR WinGraphicsWindow::
02615 get_cursor(const Filename &filename) {
02616   // The empty filename means to disable a custom cursor.
02617   if (filename.empty()) {
02618     return 0;
02619   }
02620 
02621   // First, look for the unresolved filename in our index.
02622   IconFilenames::iterator fi = _cursor_filenames.find(filename);
02623   if (fi != _cursor_filenames.end()) {
02624     return (HCURSOR)((*fi).second);
02625   }
02626 
02627   // If it wasn't found, resolve the filename and search for that.
02628 
02629   // Since we have to use a Windows call to load the image from a
02630   // filename, we can't load a virtual file and we can't use the
02631   // virtual file system.
02632   Filename resolved = filename;
02633   if (!resolved.resolve_filename(get_model_path())) {
02634     // The filename doesn't exist.
02635     windisplay_cat.warning()
02636       << "Could not find cursor filename " << filename << "\n";
02637     return 0;
02638   }
02639   fi = _cursor_filenames.find(resolved);
02640   if (fi != _cursor_filenames.end()) {
02641     _cursor_filenames[filename] = (*fi).second;
02642     return (HCURSOR)((*fi).second);
02643   }
02644 
02645   Filename os = resolved.to_os_specific();
02646 
02647   HANDLE h = LoadImage(NULL, os.c_str(),
02648                        IMAGE_CURSOR, 0, 0, LR_LOADFROMFILE);
02649   if (h == 0) {
02650     windisplay_cat.warning()
02651       << "windows cursor filename '" << os << "' could not be loaded!!\n";
02652     show_error_message();
02653   }
02654 
02655   _cursor_filenames[filename] = h;
02656   _cursor_filenames[resolved] = h;
02657   return (HCURSOR)h;
02658 }
02659 
02660 static HCURSOR get_cursor(const Filename &filename);
02661   
02662 ////////////////////////////////////////////////////////////////////
02663 //     Function: WinGraphicsWindow::register_window_class
02664 //       Access: Private, Static
02665 //  Description: Registers a Window class appropriate for the
02666 //               indicated properties.  This class may be shared by
02667 //               multiple windows.
02668 ////////////////////////////////////////////////////////////////////
02669 const WinGraphicsWindow::WindowClass &WinGraphicsWindow::
02670 register_window_class(const WindowProperties &props) {
02671   WindowClass wcreg(props);
02672   wostringstream wclass_name;
02673   wclass_name << L"WinGraphicsWindow" << _window_class_index;
02674   wcreg._name = wclass_name.str();
02675 
02676   pair<WindowClasses::iterator, bool> found = _window_classes.insert(wcreg);
02677   const WindowClass &wclass = (*found.first);
02678 
02679   if (!found.second) {
02680     // We have already created a window class.
02681     return wclass;
02682   }
02683 
02684   // We have not yet created this window class.
02685   _window_class_index++;
02686 
02687   WNDCLASSW wc;
02688 
02689   HINSTANCE instance = GetModuleHandle(NULL);
02690 
02691   // Clear before filling in window structure!
02692   ZeroMemory(&wc, sizeof(wc));
02693   wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
02694   wc.lpfnWndProc = (WNDPROC)static_window_proc;
02695   wc.hInstance = instance;
02696 
02697   wc.hIcon = wclass._icon;
02698 
02699   wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
02700   wc.lpszMenuName = NULL;
02701   wc.lpszClassName = wclass._name.c_str();
02702   
02703   if (!RegisterClassW(&wc)) {
02704     windisplay_cat.error()
02705       << "could not register window class " << wclass._name << "!" << endl;
02706     return wclass;
02707   }
02708 
02709   return wclass;
02710 }
02711 
02712 ////////////////////////////////////////////////////////////////////
02713 //     Function: WinGraphicsWindow::WinWindowHandle::Constructor
02714 //       Access: Public
02715 //  Description: 
02716 ////////////////////////////////////////////////////////////////////
02717 WinGraphicsWindow::WinWindowHandle::
02718 WinWindowHandle(WinGraphicsWindow *window, const WindowHandle &copy) :
02719   WindowHandle(copy),
02720   _window(window)
02721 {
02722 }
02723 
02724 ////////////////////////////////////////////////////////////////////
02725 //     Function: WinGraphicsWindow::WinWindowHandle::clear_window
02726 //       Access: Public
02727 //  Description: Should be called by the WinGraphicsWindow's
02728 //               destructor, so that we don't end up with a floating
02729 //               pointer should this object persist beyond the
02730 //               lifespan of its window.
02731 ////////////////////////////////////////////////////////////////////
02732 void WinGraphicsWindow::WinWindowHandle::
02733 clear_window() {
02734   _window = NULL;
02735 }
02736 
02737 ////////////////////////////////////////////////////////////////////
02738 //     Function: WinGraphicsWindow::WinWindowHandle::receive_windows_message
02739 //       Access: Public, Virtual
02740 //  Description: Called on a child handle to deliver a keyboard button
02741 //               event generated in the parent window.
02742 ////////////////////////////////////////////////////////////////////
02743 void WinGraphicsWindow::WinWindowHandle::
02744 receive_windows_message(unsigned int msg, int wparam, int lparam) {
02745   if (_window != NULL) {
02746     _window->receive_windows_message(msg, wparam, lparam);
02747   }
02748 }
02749 
02750 
02751 // pops up MsgBox w/system error msg
02752 void PrintErrorMessage(DWORD msgID) {
02753   LPTSTR pMessageBuffer;
02754 
02755   if (msgID==PRINT_LAST_ERROR)
02756     msgID=GetLastError();
02757 
02758   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
02759                 NULL,msgID,
02760                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //The user default language
02761                 (LPTSTR) &pMessageBuffer,  // the weird ptrptr->ptr cast is intentional, see FORMAT_MESSAGE_ALLOCATE_BUFFER
02762                 1024, NULL);
02763   MessageBox(GetDesktopWindow(),pMessageBuffer,_T(errorbox_title),MB_OK);
02764   windisplay_cat.fatal() << "System error msg: " << pMessageBuffer << endl;
02765   LocalFree( pMessageBuffer );
02766 }
02767 
02768 void
02769 ClearToBlack(HWND hWnd, const WindowProperties &props) {
02770   if (!props.has_origin()) {
02771     if (windisplay_cat.is_debug()) {
02772       windisplay_cat.debug()
02773         << "Skipping ClearToBlack, no origin specified yet.\n";
02774     }
02775     return;
02776   }
02777 
02778   if (windisplay_cat.is_debug()) {
02779     windisplay_cat.debug()
02780       << "ClearToBlack(" << hWnd << ", " << props << ")\n";
02781   }
02782   // clear to black
02783   HDC hDC=GetDC(hWnd);  // GetDC is not particularly fast.  if this needs to be super-quick, we should cache GetDC's hDC
02784   RECT clrRect = {
02785     props.get_x_origin(), props.get_y_origin(),
02786     props.get_x_origin() + props.get_x_size(),
02787     props.get_y_origin() + props.get_y_size()
02788   };
02789   FillRect(hDC,&clrRect,(HBRUSH)GetStockObject(BLACK_BRUSH));
02790   ReleaseDC(hWnd,hDC);
02791   GdiFlush();
02792 }
02793 
02794 ////////////////////////////////////////////////////////////////////
02795 //     Function: get_client_rect_screen
02796 //  Description: Fills view_rect with the coordinates of the client
02797 //               area of the indicated window, converted to screen
02798 //               coordinates.
02799 ////////////////////////////////////////////////////////////////////
02800 void get_client_rect_screen(HWND hwnd, RECT *view_rect) {
02801   GetClientRect(hwnd, view_rect);
02802 
02803   POINT ul, lr;
02804   ul.x = view_rect->left;
02805   ul.y = view_rect->top;
02806   lr.x = view_rect->right;
02807   lr.y = view_rect->bottom;
02808 
02809   ClientToScreen(hwnd, &ul);
02810   ClientToScreen(hwnd, &lr);
02811 
02812   view_rect->left = ul.x;
02813   view_rect->top = ul.y;
02814   view_rect->right = lr.x;
02815   view_rect->bottom = lr.y;
02816 }
02817 
02818 ////////////////////////////////////////////////////////////////////
02819 //     Function: WinGraphicsWindow::add_window_proc
02820 //       Access: Public, Virtual
02821 //  Description: Adds the specified Windows proc event handler to be called
02822 //               whenever a Windows event occurs.
02823 //               
02824 ////////////////////////////////////////////////////////////////////
02825 void WinGraphicsWindow::add_window_proc( const GraphicsWindowProc* wnd_proc ){
02826   nassertv(wnd_proc != NULL);
02827   _window_proc_classes.insert( (GraphicsWindowProc*)wnd_proc );
02828 }
02829 
02830 ////////////////////////////////////////////////////////////////////
02831 //     Function: WinGraphicsWindow::remove_window_proc
02832 //       Access: Public, Virtual
02833 //  Description: Removes the specified Windows proc event handler.
02834 //               
02835 ////////////////////////////////////////////////////////////////////
02836 void WinGraphicsWindow::remove_window_proc( const GraphicsWindowProc* wnd_proc ){
02837   nassertv(wnd_proc != NULL);
02838   _window_proc_classes.erase( (GraphicsWindowProc*)wnd_proc );
02839 }
02840 
02841 ////////////////////////////////////////////////////////////////////
02842 //     Function: WinGraphicsWindow::clear_window_procs
02843 //       Access: Public, Virtual
02844 //  Description: Removes all Windows proc event handlers.
02845 //               
02846 ////////////////////////////////////////////////////////////////////
02847 void WinGraphicsWindow::clear_window_procs(){
02848   _window_proc_classes.clear();
02849 }
02850 
02851 ////////////////////////////////////////////////////////////////////
02852 //     Function: WinGraphicsWindow::supports_window_procs
02853 //       Access: Public, Virtual
02854 //  Description: Returns whether this window supports adding of windows proc handlers.
02855 //               
02856 ////////////////////////////////////////////////////////////////////
02857 bool WinGraphicsWindow::supports_window_procs() const{
02858   return true;
02859 }
02860 
02861 ////////////////////////////////////////////////////////////////////
02862 //     Function: WinGraphicsWindow::is_touch_event
02863 //       Access: Public, Virtual
02864 //  Description: Returns whether the specified event msg is a touch message.
02865 //               
02866 ////////////////////////////////////////////////////////////////////
02867 bool WinGraphicsWindow::
02868 is_touch_event(GraphicsWindowProcCallbackData* callbackData){
02869 #ifdef HAVE_WIN_TOUCHINPUT
02870   return callbackData->get_msg() == WM_TOUCH;
02871 #else
02872   return false;
02873 #endif
02874 }
02875 
02876 ////////////////////////////////////////////////////////////////////
02877 //     Function: WinGraphicsWindow::get_num_touches
02878 //       Access: Public, Virtual
02879 //  Description: Returns the current number of touches on this window.
02880 //               
02881 ////////////////////////////////////////////////////////////////////
02882 int WinGraphicsWindow::
02883 get_num_touches(){
02884 #ifdef HAVE_WIN_TOUCHINPUT
02885   return _numTouches;
02886 #else
02887   return 0;
02888 #endif
02889 }
02890 
02891 ////////////////////////////////////////////////////////////////////
02892 //     Function: WinGraphicsWindow::get_touch_info
02893 //       Access: Public, Virtual
02894 //  Description: Returns the TouchInfo object describing the specified touch.
02895 //               
02896 ////////////////////////////////////////////////////////////////////
02897 TouchInfo WinGraphicsWindow::
02898 get_touch_info(int index){
02899 #ifdef HAVE_WIN_TOUCHINPUT
02900   TOUCHINPUT ti = _touches[index];
02901   POINT point;
02902   point.x = TOUCH_COORD_TO_PIXEL(ti.x);
02903   point.y = TOUCH_COORD_TO_PIXEL(ti.y);
02904   ScreenToClient(_hWnd, &point);
02905 
02906   TouchInfo ret = TouchInfo();
02907   ret.set_x(point.x);
02908   ret.set_y(point.y);
02909   ret.set_id(ti.dwID);
02910   ret.set_flags(ti.dwFlags);
02911   return ret;
02912 #else
02913   return TouchInfo();
02914 #endif
02915 }
 All Classes Functions Variables Enumerations