Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/windisplay/winGraphicsWindow.cxx

Go to the documentation of this file.
00001 // Filename: winGraphicsWindow.cxx
00002 // Created by:  drose (20Dec02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "winGraphicsWindow.h"
00020 #include "config_windisplay.h"
00021 #include "winGraphicsPipe.h"
00022 
00023 #include "graphicsPipe.h"
00024 #include "keyboardButton.h"
00025 #include "mouseButton.h"
00026 #include "clockObject.h"
00027 
00028 #include <tchar.h>
00029 
00030 TypeHandle WinGraphicsWindow::_type_handle;
00031 
00032 bool WinGraphicsWindow::_loaded_custom_cursor;
00033 HCURSOR WinGraphicsWindow::_mouse_cursor;
00034 const char * const WinGraphicsWindow::_window_class_name = "WinGraphicsWindow";
00035 bool WinGraphicsWindow::_window_class_registered = false;
00036 
00037 WinGraphicsWindow::WindowHandles WinGraphicsWindow::_window_handles;
00038 WinGraphicsWindow *WinGraphicsWindow::_creating_window = NULL;
00039 
00040 WinGraphicsWindow *WinGraphicsWindow::_cursor_window = NULL;
00041 bool WinGraphicsWindow::_cursor_hidden = false;
00042 
00043 // These are used to save the previous state of the fancy Win2000
00044 // effects that interfere with rendering when the mouse wanders into a
00045 // window's client area.
00046 bool WinGraphicsWindow::_got_saved_params = false;
00047 int WinGraphicsWindow::_saved_mouse_trails;
00048 BOOL WinGraphicsWindow::_saved_cursor_shadow;
00049 BOOL WinGraphicsWindow::_saved_mouse_vanish;
00050 
00051 static const char * const errorbox_title = "Panda3D Error";
00052 
00053 ////////////////////////////////////////////////////////////////////
00054 //     Function: WinGraphicsWindow::Constructor
00055 //       Access: Public
00056 //  Description:
00057 ////////////////////////////////////////////////////////////////////
00058 WinGraphicsWindow::
00059 WinGraphicsWindow(GraphicsPipe *pipe, GraphicsStateGuardian *gsg) :
00060   GraphicsWindow(pipe, gsg) 
00061 {
00062   GraphicsWindowInputDevice device =
00063   GraphicsWindowInputDevice::pointer_and_keyboard("keyboard/mouse");
00064   _input_devices.push_back(device);
00065   _hWnd = (HWND)0;
00066   _ime_open = false;
00067   _ime_active = false;
00068   _ime_composition_w = false;
00069   _tracking_mouse_leaving = false;
00070   _maximized = false;
00071   memset(_keyboard_state, 0, sizeof(BYTE) * num_virtual_keys);
00072 }
00073 
00074 ////////////////////////////////////////////////////////////////////
00075 //     Function: WinGraphicsWindow::Destructor
00076 //       Access: Public, Virtual
00077 //  Description:
00078 ////////////////////////////////////////////////////////////////////
00079 WinGraphicsWindow::
00080 ~WinGraphicsWindow() {
00081 }
00082 
00083 ////////////////////////////////////////////////////////////////////
00084 //     Function: WinGraphicsWindow::begin_flip
00085 //       Access: Public, Virtual
00086 //  Description: This function will be called within the draw thread
00087 //               after end_frame() has been called on all windows, to
00088 //               initiate the exchange of the front and back buffers.
00089 //
00090 //               This should instruct the window to prepare for the
00091 //               flip at the next video sync, but it should not wait.
00092 //
00093 //               We have the two separate functions, begin_flip() and
00094 //               end_flip(), to make it easier to flip all of the
00095 //               windows at the same time.
00096 ////////////////////////////////////////////////////////////////////
00097 void WinGraphicsWindow::
00098 begin_flip() {
00099 }
00100 
00101 ////////////////////////////////////////////////////////////////////
00102 //     Function: WinGraphicsWindow::process_events
00103 //       Access: Public, Virtual
00104 //  Description: Do whatever processing is necessary to ensure that
00105 //               the window responds to user events.  Also, honor any
00106 //               requests recently made via request_properties()
00107 //
00108 //               This function is called only within the window
00109 //               thread.
00110 ////////////////////////////////////////////////////////////////////
00111 void WinGraphicsWindow::
00112 process_events() {
00113   GraphicsWindow::process_events();
00114 
00115   // We can't treat the message loop specially just because the window
00116   // is minimized, because we might be reading messages queued up for
00117   // some other window, which is not minimized.
00118   /*
00119   if (!_window_active) {
00120       // Get 1 msg at a time until no more are left and we block and sleep,
00121       // or message changes _return_control_to_app or !_window_active status
00122 
00123       while(!_window_active && (!_return_control_to_app)) {
00124           process_1_event();
00125       }
00126       _return_control_to_app = false;
00127 
00128   } else 
00129   */
00130 
00131   MSG msg;
00132     
00133   // Handle all the messages on the queue in a row.  Some of these
00134   // might be for another window, but they will get dispatched
00135   // appropriately.
00136   while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
00137     process_1_event();
00138   }
00139 }
00140 ////////////////////////////////////////////////////////////////////
00141 //     Function: WinGraphicsWindow::set_properties_now
00142 //       Access: Public, Virtual
00143 //  Description: Applies the requested set of properties to the
00144 //               window, if possible, for instance to request a change
00145 //               in size or minimization status.
00146 //
00147 //               The window properties are applied immediately, rather
00148 //               than waiting until the next frame.  This implies that
00149 //               this method may *only* be called from within the
00150 //               window thread.
00151 //
00152 //               The properties that have been applied are cleared
00153 //               from the structure by this function; so on return,
00154 //               whatever remains in the properties structure are
00155 //               those that were unchanged for some reason (probably
00156 //               because the underlying interface does not support
00157 //               changing that property on an open window).
00158 ////////////////////////////////////////////////////////////////////
00159 void WinGraphicsWindow::
00160 set_properties_now(WindowProperties &properties) {
00161   GraphicsWindow::set_properties_now(properties);
00162   if (!properties.is_any_specified()) {
00163     // The base class has already handled this case.
00164     return;
00165   }
00166 }
00167 
00168 ////////////////////////////////////////////////////////////////////
00169 //     Function: WinGraphicsWindow::close_window
00170 //       Access: Protected, Virtual
00171 //  Description: Closes the window right now.  Called from the window
00172 //               thread.
00173 ////////////////////////////////////////////////////////////////////
00174 void WinGraphicsWindow::
00175 close_window() {
00176   set_cursor_out_of_window();
00177   DestroyWindow(_hWnd);
00178 
00179   // Remove the window handle from our global map.
00180   _window_handles.erase(_hWnd);
00181   _hWnd = (HWND)0;
00182 
00183   if (is_fullscreen()) {
00184     // revert to default display mode.
00185     ChangeDisplaySettings(NULL, 0x0);
00186   }
00187 }
00188 
00189 ////////////////////////////////////////////////////////////////////
00190 //     Function: WinGraphicsWindow::open_window
00191 //       Access: Protected, Virtual
00192 //  Description: Opens the window right now.  Called from the window
00193 //               thread.  Returns true if the window is successfully
00194 //               opened, or false if there was a problem.
00195 ////////////////////////////////////////////////////////////////////
00196 bool WinGraphicsWindow::
00197 open_window() {
00198   // Store the current window pointer in _creating_window, so we can
00199   // call CreateWindow() and know which window it is sending events to
00200   // even before it gives us a handle.  Warning: this is not thread
00201   // safe!
00202   _creating_window = this;
00203   bool opened;
00204   if (is_fullscreen()) {
00205     opened = open_fullscreen_window();
00206   } else {
00207     opened = open_regular_window();
00208   }
00209   _creating_window = (WinGraphicsWindow *)NULL;
00210 
00211   if (!opened) {
00212     return false;
00213   }
00214 
00215   // Now that we have a window handle, store it in our global map, so
00216   // future messages for this window can be routed properly.
00217   _window_handles.insert(WindowHandles::value_type(_hWnd, this));
00218   
00219   // move window to top of zorder.
00220   SetWindowPos(_hWnd, HWND_TOP, 0,0,0,0, 
00221                SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE);
00222   
00223   // need to do twice to override any minimized flags in StartProcessInfo
00224   ShowWindow(_hWnd, SW_SHOWNORMAL);
00225   ShowWindow(_hWnd, SW_SHOWNORMAL);
00226   
00227   if (!SetForegroundWindow(_hWnd)) {
00228     windisplay_cat.warning()
00229       << "SetForegroundWindow() failed!\n";
00230   }
00231 
00232   // Determine the initial open status of the IME.
00233   _ime_open = false;
00234   _ime_active = false;
00235   HIMC hIMC = ImmGetContext(_hWnd);
00236   if (hIMC != 0) {
00237     _ime_open = (ImmGetOpenStatus(hIMC) != 0);
00238     ImmReleaseContext(_hWnd, hIMC);
00239   }
00240 
00241   // Check the version of the OS we are running.  If we are running
00242   // win2000, we must use ImmGetCompositionStringW() to report the
00243   // characters returned by the IME, since WM_CHAR and
00244   // ImmGetCompositionStringA() both just return question marks.
00245   // However, this function doesn't work for Win98; on this OS, we
00246   // have to use ImmGetCompositionStringA() instead, which returns an
00247   // encoded string in shift-jis (which we then have to decode).
00248 
00249   // For now, this is user-configurable, to allow testing of this code
00250   // on both OS's.  After we verify that truth of the above claim, we
00251   // should base this decision on GetVersionEx() or maybe
00252   // VerifyVersionInfo().
00253   _ime_composition_w = ime_composition_w;
00254   
00255   // need to re-evaluate above in light of this, it seems that
00256   // ImmGetCompositionStringW should work on both:
00257   //     The Input Method Editor and Unicode Windows 98/Me, Windows
00258   //     NT/2000/XP: Windows supports a Unicode interface for the
00259   //     IME, in addition to the ANSI interface originally supported.
00260   //     Windows 98/Me supports all the Unicode functions except
00261   //     ImmIsUIMessage.  Also, all the messages in Windows 98/Me are
00262   //     ANSI based.  Since Windows 98/Me does not support Unicode
00263   //     messages, applications can use ImmGetCompositionString to
00264   //     receive Unicode characters from a Unicode based IME on
00265   //     Windows 98/Me.  There are two issues involved with Unicode
00266   //     handling and the IME.  One is that the Unicode versions of
00267   //     IME routines return the size of a buffer in bytes rather
00268   //     than 16-bit Unicode characters,and the other is the IME
00269   //     normally returns Unicode characters (rather than DBCS) in
00270   //     the WM_CHAR and WM_IME_CHAR messages.  Use RegisterClassW
00271   //     to cause the WM_CHAR and WM_IME_CHAR messages to return
00272   //     Unicode characters in the wParam parameter rather than DBCS
00273   //     characters.  This is only available under Windows NT; it is
00274   //     stubbed out in Windows 95/98/Me.
00275 
00276   return true;
00277 }
00278 
00279 ////////////////////////////////////////////////////////////////////
00280 //     Function: WinGraphicsWindow::fullscreen_minimized
00281 //       Access: Protected, Virtual
00282 //  Description: This is a hook for derived classes to do something
00283 //               special, if necessary, when a fullscreen window has
00284 //               been minimized.  The given WindowProperties struct
00285 //               will be applied to this window's properties after
00286 //               this function returns.
00287 ////////////////////////////////////////////////////////////////////
00288 void WinGraphicsWindow::
00289 fullscreen_minimized(WindowProperties &) {
00290 }
00291 
00292 ////////////////////////////////////////////////////////////////////
00293 //     Function: WinGraphicsWindow::fullscreen_restored
00294 //       Access: Protected, Virtual
00295 //  Description: This is a hook for derived classes to do something
00296 //               special, if necessary, when a fullscreen window has
00297 //               been restored after being minimized.  The given
00298 //               WindowProperties struct will be applied to this
00299 //               window's properties after this function returns.
00300 ////////////////////////////////////////////////////////////////////
00301 void WinGraphicsWindow::
00302 fullscreen_restored(WindowProperties &) {
00303 }
00304 
00305 ////////////////////////////////////////////////////////////////////
00306 //     Function: WinGraphicsWindow::open_window
00307 //       Access: Protected, Virtual
00308 //  Description: Called from the window thread in response to a request
00309 //               from within the code (via request_properties()) to
00310 //               change the size and/or position of the window.
00311 //               Returns true if the window is successfully changed,
00312 //               or false if there was a problem.
00313 ////////////////////////////////////////////////////////////////////
00314 bool WinGraphicsWindow::
00315 do_reshape_request(int x_origin, int y_origin, int x_size, int y_size) {
00316   windisplay_cat.info()
00317     << "Got reshape request (" << x_origin << ", " << y_origin
00318     << ", " << x_size << ", " << y_size << ")\n";
00319   if (!is_fullscreen()) {
00320     // Compute the appropriate size and placement for the window,
00321     // including decorations.
00322     RECT view_rect;
00323     SetRect(&view_rect, x_origin, y_origin,
00324             x_origin + x_size, y_origin + y_size);
00325     WINDOWINFO wi;
00326     GetWindowInfo(_hWnd, &wi);
00327     AdjustWindowRectEx(&view_rect, wi.dwStyle, false, wi.dwExStyle);
00328 
00329     SetWindowPos(_hWnd, NULL, view_rect.left, view_rect.top,
00330                  view_rect.right - view_rect.left,
00331                  view_rect.bottom - view_rect.top,
00332                  SWP_NOZORDER | SWP_NOMOVE | SWP_NOSENDCHANGING);
00333 
00334     // This isn't quite right, because handle_reshape() calls
00335     // system_changed_properties(), generating the event indicating
00336     // the window has changed size externally--even though it changed
00337     // due to an internal request.
00338     handle_reshape();
00339     return true;
00340   }
00341 
00342   // Resizing a fullscreen window is a little trickier.
00343   return do_fullscreen_resize(x_size, y_size);
00344 }
00345 
00346 ////////////////////////////////////////////////////////////////////
00347 //     Function: WinGraphicsWindow::handle_reshape
00348 //       Access: Protected, Virtual
00349 //  Description: Called in the window thread when the window size or
00350 //               location is changed, this updates the properties
00351 //               structure accordingly.
00352 ////////////////////////////////////////////////////////////////////
00353 void WinGraphicsWindow::
00354 handle_reshape() {
00355   RECT view_rect;
00356   GetClientRect(_hWnd, &view_rect);
00357   ClientToScreen(_hWnd, (POINT*)&view_rect.left);   // translates top,left pnt
00358   ClientToScreen(_hWnd, (POINT*)&view_rect.right);  // translates right,bottom pnt
00359   
00360   WindowProperties properties;
00361   properties.set_size((view_rect.right - view_rect.left), 
00362                       (view_rect.bottom - view_rect.top));
00363 
00364   // _props origin should reflect upper left of view rectangle
00365   properties.set_origin(view_rect.left, view_rect.top);
00366   
00367   if (windisplay_cat.is_spam()) {
00368     windisplay_cat.spam()
00369       << "reshape to origin: (" << properties.get_x_origin() << "," 
00370       << properties.get_y_origin() << "), size: (" << properties.get_x_size()
00371       << "," << properties.get_y_size() << ")\n";
00372   }
00373 
00374   system_changed_properties(properties);
00375 }
00376 
00377 ////////////////////////////////////////////////////////////////////
00378 //     Function: WinGraphicsWindow::do_fullscreen_resize
00379 //       Access: Protected, Virtual
00380 //  Description: Called in the window thread to resize a fullscreen
00381 //               window.
00382 ////////////////////////////////////////////////////////////////////
00383 bool WinGraphicsWindow::
00384 do_fullscreen_resize(int x_size, int y_size) {
00385   HWND hDesktopWindow = GetDesktopWindow();
00386   HDC scrnDC = GetDC(hDesktopWindow);
00387   DWORD dwFullScreenBitDepth = GetDeviceCaps(scrnDC, BITSPIXEL);
00388   ReleaseDC(hDesktopWindow, scrnDC);
00389 
00390   // resize will always leave screen bitdepth unchanged
00391 
00392   // allowing resizing of lowvidmem cards to > 640x480.  why?  I'll
00393   // assume check was already done by caller, so he knows what he
00394   // wants
00395 
00396   DEVMODE dm;
00397   if (!find_acceptable_display_mode(x_size, y_size,
00398                                     dwFullScreenBitDepth, dm)) {
00399     windisplay_cat.error()
00400       << "window resize(" << x_size << ", " << y_size 
00401       << ") failed, no compatible fullscreen display mode found!\n";
00402     return false;
00403   }
00404 
00405   // this causes WM_SIZE msg to be produced
00406   SetWindowPos(_hWnd, NULL, 0,0, x_size, y_size, 
00407                SWP_NOZORDER | SWP_NOMOVE | SWP_NOSENDCHANGING);
00408   int chg_result = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
00409 
00410   if (chg_result != DISP_CHANGE_SUCCESSFUL) {
00411     windisplay_cat.error()
00412       << "resize ChangeDisplaySettings failed (error code: " 
00413       << chg_result << ") for specified res (" << x_size << " x "
00414       << y_size << " x " << dwFullScreenBitDepth << "), " 
00415       << dm.dmDisplayFrequency << "Hz\n";
00416     return false;
00417   }
00418 
00419   _fullscreen_display_mode = dm;
00420 
00421   windisplay_cat.info()
00422     << "Resized fullscreen window to " << x_size << ", " << y_size 
00423     << " bitdepth " << dwFullScreenBitDepth << ", "
00424     << dm.dmDisplayFrequency << "Hz\n";
00425 
00426   return true;
00427 }
00428 
00429 ////////////////////////////////////////////////////////////////////
00430 //     Function: WinGraphicsWindow::reconsider_fullscreen_size
00431 //       Access: Protected, Virtual
00432 //  Description: Called before creating a fullscreen window to give
00433 //               the driver a chance to adjust the particular
00434 //               resolution request, if necessary.
00435 ////////////////////////////////////////////////////////////////////
00436 void WinGraphicsWindow::
00437 reconsider_fullscreen_size(DWORD &, DWORD &, DWORD &) {
00438 }
00439 
00440 ////////////////////////////////////////////////////////////////////
00441 //     Function: WinGraphicsWindow::open_fullscreen_window
00442 //       Access: Private
00443 //  Description: Creates a fullscreen-style window.
00444 ////////////////////////////////////////////////////////////////////
00445 bool WinGraphicsWindow::
00446 open_fullscreen_window() {
00447   //  from MSDN:
00448   //  An OpenGL window has its own pixel format. Because of this, only
00449   //  device contexts retrieved for the client area of an OpenGL
00450   //  window are allowed to draw into the window. As a result, an
00451   //  OpenGL window should be created with the WS_CLIPCHILDREN and
00452   //  WS_CLIPSIBLINGS styles. Additionally, the window class attribute
00453   //  should not include the CS_PARENTDC style.
00454   DWORD window_style = 
00455     WS_POPUP | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
00456 
00457   if (!_properties.has_size()) {
00458     // Just pick a stupid default size if one isn't specified.
00459     _properties.set_size(640, 480);
00460   }
00461 
00462   HWND hDesktopWindow = GetDesktopWindow();
00463   HDC scrnDC = GetDC(hDesktopWindow);
00464   DWORD cur_bitdepth = GetDeviceCaps(scrnDC, BITSPIXEL);
00465   //  DWORD drvr_ver = GetDeviceCaps(scrnDC, DRIVERVERSION);
00466   //  DWORD cur_scrnwidth = GetDeviceCaps(scrnDC, HORZRES);
00467   //  DWORD cur_scrnheight = GetDeviceCaps(scrnDC, VERTRES);
00468   ReleaseDC(hDesktopWindow, scrnDC);
00469 
00470   DWORD dwWidth = _properties.get_x_size();
00471   DWORD dwHeight = _properties.get_y_size();
00472   DWORD dwFullScreenBitDepth = cur_bitdepth;
00473   
00474   reconsider_fullscreen_size(dwWidth, dwHeight, dwFullScreenBitDepth);
00475   if (!find_acceptable_display_mode(dwWidth, dwHeight, dwFullScreenBitDepth,
00476                                     _fullscreen_display_mode)) {
00477     windisplay_cat.error() 
00478       << "Videocard has no supported display resolutions at specified res ("
00479       << dwWidth << " x " << dwHeight << " x " << dwFullScreenBitDepth <<")\n";
00480     return false;
00481   }
00482 
00483   string title;
00484   if (_properties.has_title()) {
00485     title = _properties.get_title();
00486   }
00487 
00488   // I'd prefer to CreateWindow after DisplayChange in case it messes
00489   // up GL somehow, but I need the window's black background to cover
00490   // up the desktop during the mode change
00491   register_window_class();
00492   HINSTANCE hinstance = GetModuleHandle(NULL);
00493   _hWnd = CreateWindow(_window_class_name, title.c_str(), window_style,
00494                           0, 0, dwWidth, dwHeight, 
00495                           hDesktopWindow, NULL, hinstance, 0);
00496   if (!_hWnd) {
00497     windisplay_cat.error()
00498       << "CreateWindow() failed!" << endl;
00499     show_error_message();
00500     return false;
00501   }
00502    
00503   int chg_result = ChangeDisplaySettings(&_fullscreen_display_mode, 
00504                                          CDS_FULLSCREEN);
00505   if (chg_result != DISP_CHANGE_SUCCESSFUL) {
00506     windisplay_cat.error()
00507       << "ChangeDisplaySettings failed (error code: "
00508       << chg_result << ") for specified res (" << dwWidth
00509       << " x " << dwHeight << " x " << dwFullScreenBitDepth
00510       << "), " << _fullscreen_display_mode.dmDisplayFrequency  << "Hz\n";
00511     return false;
00512   }
00513 
00514   _properties.set_origin(0, 0);
00515   _properties.set_size(dwWidth, dwHeight);
00516   return true;
00517 }
00518 
00519 ////////////////////////////////////////////////////////////////////
00520 //     Function: WinGraphicsWindow::open_regular_window
00521 //       Access: Private
00522 //  Description: Creates a non-fullscreen window, on the desktop.
00523 ////////////////////////////////////////////////////////////////////
00524 bool WinGraphicsWindow::
00525 open_regular_window() {
00526   //  from MSDN:
00527   //  An OpenGL window has its own pixel format. Because of this, only
00528   //  device contexts retrieved for the client area of an OpenGL
00529   //  window are allowed to draw into the window. As a result, an
00530   //  OpenGL window should be created with the WS_CLIPCHILDREN and
00531   //  WS_CLIPSIBLINGS styles. Additionally, the window class attribute
00532   //  should not include the CS_PARENTDC style.
00533   DWORD window_style = 
00534     WS_POPUP | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
00535   
00536   if (!_properties.has_undecorated() || !_properties.get_undecorated()) {
00537     window_style |= WS_OVERLAPPEDWINDOW;
00538   }
00539 
00540   if (!_properties.has_origin()) {
00541     _properties.set_origin(0, 0);
00542   }
00543   if (!_properties.has_size()) {
00544     _properties.set_size(100, 100);
00545   }
00546 
00547   RECT win_rect;
00548   SetRect(&win_rect, 
00549           _properties.get_x_origin(),
00550           _properties.get_y_origin(),
00551           _properties.get_x_origin() + _properties.get_x_size(),
00552           _properties.get_y_origin() + _properties.get_y_size());
00553   
00554   // compute window size based on desired client area size
00555   if (!AdjustWindowRect(&win_rect, window_style, FALSE)) {
00556     windisplay_cat.error()
00557       << "AdjustWindowRect failed!" << endl;
00558     return false;
00559   }
00560   
00561   // make sure origin is on screen; slide far bounds over if necessary
00562   if (win_rect.left < 0) {
00563     win_rect.right += abs(win_rect.left); 
00564     win_rect.left = 0;
00565   }
00566   if (win_rect.top < 0) {
00567     win_rect.bottom += abs(win_rect.top); 
00568     win_rect.top = 0;
00569   }
00570 
00571   string title;
00572   if (_properties.has_title()) {
00573     title = _properties.get_title();
00574   }
00575 
00576   register_window_class();
00577   HINSTANCE hinstance = GetModuleHandle(NULL);
00578   _hWnd = CreateWindow(_window_class_name, title.c_str(), window_style, 
00579                           win_rect.left, win_rect.top,
00580                           win_rect.right - win_rect.left,
00581                           win_rect.bottom - win_rect.top,
00582                           NULL, NULL, hinstance, 0);
00583 
00584   if (!_hWnd) {
00585     windisplay_cat.error()
00586       << "CreateWindow() failed!" << endl;
00587     show_error_message();
00588     return false;
00589   }
00590 
00591   return true;
00592 }
00593 
00594 ////////////////////////////////////////////////////////////////////
00595 //     Function: WinGraphicsWindow::track_mouse_leaving
00596 //       Access: Private
00597 //  Description: Intended to be called whenever mouse motion is
00598 //               detected within the window, this indicates that the
00599 //               mouse is within the window and tells Windows that we
00600 //               want to be told when the mouse leaves the window.
00601 ////////////////////////////////////////////////////////////////////
00602 void WinGraphicsWindow::
00603 track_mouse_leaving(HWND hwnd) {
00604   // Note: could use _TrackMouseEvent in comctrl32.dll (part of IE
00605   // 3.0+) which emulates TrackMouseEvent on w95, but that requires
00606   // another 500K of memory to hold that DLL, which is lame just to
00607   // support w95, which probably has other issues anyway
00608   WinGraphicsPipe *winpipe;
00609   DCAST_INTO_V(winpipe, _pipe);
00610 
00611   if (winpipe->_pfnTrackMouseEvent != NULL) {
00612     TRACKMOUSEEVENT tme = {
00613       sizeof(TRACKMOUSEEVENT),
00614       TME_LEAVE,
00615       hwnd,
00616       0
00617     };
00618 
00619     // tell win32 to post WM_MOUSELEAVE msgs
00620     BOOL bSucceeded = winpipe->_pfnTrackMouseEvent(&tme);  
00621     
00622     if ((!bSucceeded) && windisplay_cat.is_debug()) {
00623       windisplay_cat.debug()
00624         << "TrackMouseEvent failed!, LastError=" << GetLastError() << endl;
00625     }
00626     
00627     _tracking_mouse_leaving = true;
00628   }
00629 }
00630 
00631 ////////////////////////////////////////////////////////////////////
00632 //     Function: WinGraphicsWindow::window_proc
00633 //       Access: Private
00634 //  Description: This is the nonstatic window_proc function.  It is
00635 //               called to handle window events for this particular
00636 //               window.
00637 ////////////////////////////////////////////////////////////////////
00638 LONG WinGraphicsWindow::
00639 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
00640   /*
00641   cerr << ClockObject::get_global_clock()->get_real_time() 
00642        << " window_proc(" << (void *)this << ", " << hwnd << ", "
00643        << msg << ", " << wparam << ", " << lparam << ")\n";
00644   */
00645   WindowProperties properties;
00646   int button = -1;
00647 
00648   switch (msg) {
00649       case WM_MOUSEMOVE: 
00650         if (!_tracking_mouse_leaving) {
00651           // need to re-call TrackMouseEvent every time mouse re-enters window
00652           track_mouse_leaving(hwnd);
00653         }
00654         set_cursor_in_window();
00655         if(handle_mouse_motion(translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam))))
00656             return 0;
00657         break;
00658     
00659       case WM_MOUSELEAVE:
00660         _tracking_mouse_leaving = false;
00661         handle_mouse_exit();
00662         set_cursor_out_of_window();
00663         break;
00664     
00665       // if cursor is invisible, make it visible when moving in the window bars & menus, so user can use click in them
00666       case WM_NCMOUSEMOVE: {
00667             if(!_properties.get_cursor_hidden()) {
00668                 if(!_bCursor_in_WindowClientArea) {
00669                     // SetCursor(_pParentWindowGroup->_hMouseCursor);
00670                     ShowCursor(true);
00671                     _bCursor_in_WindowClientArea=true;
00672                 }
00673             }
00674             break;
00675       }
00676     
00677       case WM_NCMOUSELEAVE: {
00678             if(!_properties.get_cursor_hidden()) {
00679                 ShowCursor(false);
00680                 // SetCursor(NULL);
00681                 _bCursor_in_WindowClientArea=false;
00682             }
00683             break;
00684       }
00685     
00686       case WM_CREATE: {
00687         track_mouse_leaving(hwnd);
00688         _bCursor_in_WindowClientArea=false;
00689         ClearToBlack(hwnd,_properties);
00690     
00691         POINT cpos;
00692         GetCursorPos(&cpos);
00693         ScreenToClient(hwnd,&cpos);
00694         RECT clientRect;
00695         GetClientRect(hwnd, &clientRect);
00696         if(PtInRect(&clientRect,cpos))
00697            set_cursor_in_window();  // should window focus be true as well?
00698         else set_cursor_out_of_window();
00699     
00700         break;
00701       }
00702     
00703       case WM_CLOSE:
00704         properties.set_open(false);
00705         system_changed_properties(properties);
00706     
00707         // TODO: make sure we release the GSG properly.
00708         break;
00709     
00710       case WM_ACTIVATE:
00711         properties.set_minimized((wparam & 0xffff0000) != 0);
00712         if ((wparam & 0xffff) != WA_INACTIVE) {
00713           properties.set_foreground(true);
00714           if (is_fullscreen()) {
00715             // When a fullscreen window goes active, it automatically gets
00716             // un-minimized.
00717             ChangeDisplaySettings(&_fullscreen_display_mode, CDS_FULLSCREEN);
00718             GdiFlush();
00719             SetWindowPos(_hWnd, HWND_TOP, 0,0,0,0, 
00720                          SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOOWNERZORDER);
00721             fullscreen_restored(properties);
00722           }
00723         } else {
00724           properties.set_foreground(false);
00725           if (is_fullscreen()) {
00726             // When a fullscreen window goes inactive, it automatically
00727             // gets minimized.
00728             properties.set_minimized(true);
00729     
00730             // It seems order is important here.  We must minimize the
00731             // window before restoring the display settings, or risk
00732             // losing the graphics context.
00733             ShowWindow(_hWnd, SW_MINIMIZE);
00734             GdiFlush();
00735             ChangeDisplaySettings(NULL, 0x0);
00736             fullscreen_minimized(properties);
00737           }
00738         }
00739         system_changed_properties(properties);
00740         break;
00741     
00742       case WM_SIZE:
00743         // for maximized, unmaximize, need to call resize code
00744         // artificially since no WM_EXITSIZEMOVE is generated.
00745         if (wparam == SIZE_MAXIMIZED) {
00746           _maximized = true;
00747           handle_reshape();
00748     
00749         } else if (wparam == SIZE_RESTORED && _maximized) {
00750           // SIZE_RESTORED might mean we restored to its original size
00751           // before the maximize, but it might also be called while the
00752           // user is resizing the window by hand.  Checking the _maximized
00753           // flag that we set above allows us to differentiate the two
00754           // cases.
00755           _maximized = false;
00756           handle_reshape();
00757         }
00758         break;
00759     
00760       case WM_EXITSIZEMOVE:
00761         handle_reshape();
00762         break;
00763     
00764       case WM_LBUTTONDOWN:
00765         button = 0;
00766         // fall through
00767       case WM_MBUTTONDOWN:
00768         if (button < 0) {
00769           button = 1;
00770         }
00771         // fall through
00772       case WM_RBUTTONDOWN:
00773         if (button < 0) {
00774           button = 2;
00775         }
00776         SetCapture(hwnd);
00777         handle_keypress(MouseButton::button(button), 
00778                         translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam)));
00779         break;
00780     
00781       case WM_LBUTTONUP:
00782         button = 0;
00783         // fall through
00784       case WM_MBUTTONUP:
00785         if (button < 0) {
00786           button = 1;
00787         }
00788         // fall through
00789       case WM_RBUTTONUP:
00790         if (button < 0) {
00791           button = 2;
00792         }
00793         ReleaseCapture();
00794         handle_keyrelease(MouseButton::button(button));
00795         break;
00796     
00797       case WM_IME_NOTIFY:
00798         if (wparam == IMN_SETOPENSTATUS) {
00799           HIMC hIMC = ImmGetContext(hwnd);
00800           nassertr(hIMC != 0, 0);
00801           _ime_open = (ImmGetOpenStatus(hIMC) != 0);
00802           if (!_ime_open) {
00803             _ime_active = false;  // Sanity enforcement.
00804           }
00805           ImmReleaseContext(hwnd, hIMC);
00806         }
00807         break;
00808         
00809       case WM_IME_STARTCOMPOSITION:
00810         _ime_active = true;
00811         break;
00812         
00813       case WM_IME_ENDCOMPOSITION:
00814         _ime_active = false;
00815         break;
00816         
00817       case WM_IME_COMPOSITION:
00818         if (lparam & GCS_RESULTSTR) {
00819           HIMC hIMC = ImmGetContext(hwnd);
00820           nassertr(hIMC != 0, 0);
00821           
00822           static const int max_ime_result = 128;
00823           static char ime_result[max_ime_result];
00824           
00825           if (_ime_composition_w) {
00826             // Since ImmGetCompositionStringA() doesn't seem to work
00827             // for Win2000 (it always returns question mark
00828             // characters), we have to use ImmGetCompositionStringW()
00829             // on this OS.  This is actually the easier of the two
00830             // functions to use.
00831             
00832             DWORD result_size =
00833               ImmGetCompositionStringW(hIMC, GCS_RESULTSTR,
00834                                        ime_result, max_ime_result);
00835             
00836             // Add this string into the text buffer of the application.
00837             
00838             // ImmGetCompositionStringW() returns a string, but it's
00839             // filled in with wstring data: every two characters defines a
00840             // 16-bit unicode char.  The docs aren't clear on the
00841             // endianness of this.  I guess it's safe to assume all Win32
00842             // machines are little-endian.
00843             for (DWORD i = 0; i < result_size; i += 2) {
00844               int result =
00845                 ((int)(unsigned char)ime_result[i + 1] << 8) |
00846                 (unsigned char)ime_result[i];
00847               _input_devices[0].keystroke(result);
00848             }
00849           } else {
00850             // On the other hand, ImmGetCompositionStringW() doesn't
00851             // work on Win95 or Win98; for these OS's we must use
00852             // ImmGetCompositionStringA().
00853             DWORD result_size =
00854               ImmGetCompositionStringA(hIMC, GCS_RESULTSTR,
00855                                        ime_result, max_ime_result);
00856             
00857             // ImmGetCompositionStringA() returns an encoded ANSI
00858             // string, which we now have to map to wide-character
00859             // Unicode.
00860             static const int max_wide_result = 128;
00861             static wchar_t wide_result[max_wide_result];
00862             
00863             int wide_size =
00864               MultiByteToWideChar(CP_ACP, 0,
00865                                   ime_result, result_size,
00866                                   wide_result, max_wide_result);
00867             if (wide_size == 0) {
00868               show_error_message();
00869             }
00870             for (int i = 0; i < wide_size; i++) {
00871               _input_devices[0].keystroke(wide_result[i]);
00872             }
00873           }
00874           
00875           ImmReleaseContext(hwnd, hIMC);
00876           return 0;
00877         }
00878         break;
00879         
00880       case WM_CHAR:
00881         // Ignore WM_CHAR messages if we have the IME open, since
00882         // everything will come in through WM_IME_COMPOSITION.  (It's
00883         // supposed to come in through WM_CHAR, too, but there seems to
00884         // be a bug in Win2000 in that it only sends question mark
00885         // characters through here.)
00886         if (!_ime_open) {
00887           _input_devices[0].keystroke(wparam);
00888         }
00889         break;
00890     
00891       case WM_SYSKEYDOWN: 
00892         {
00893           // Alt and F10 are sent as WM_SYSKEYDOWN instead of WM_KEYDOWN
00894           // want to use defwindproc on Alt syskey so std windows cmd
00895           // Alt-F4 works, etc
00896           POINT point;
00897           GetCursorPos(&point);
00898           ScreenToClient(hwnd, &point);
00899           handle_keypress(lookup_key(wparam), point.x, point.y);
00900           if (wparam == VK_F10) {
00901             // bypass default windproc F10 behavior (it activates the main
00902             // menu, but we have none)
00903             return 0;
00904           }
00905         }
00906         break;
00907     
00908       case WM_SYSCOMMAND:
00909         if (wparam == SC_KEYMENU) {
00910           // if Alt is released (alone w/o other keys), defwindproc will
00911           // send this command, which will 'activate' the title bar menu
00912           // (we have none) and give focus to it.  we dont want this to
00913           // happen, so kill this msg
00914           return 0;
00915         }
00916         break;
00917         
00918       case WM_KEYDOWN: 
00919         {
00920           POINT point;
00921           
00922           GetCursorPos(&point);
00923           ScreenToClient(hwnd, &point);
00924           handle_keypress(lookup_key(wparam), point.x, point.y);
00925     
00926           // Handle Cntrl-V paste from clipboard.  Is there a better way
00927           // to detect this hotkey?
00928           if ((wparam=='V') && (GetKeyState(VK_CONTROL) < 0) &&
00929               !_input_devices.empty()) {
00930             HGLOBAL hglb;
00931             char *lptstr;
00932     
00933             if (IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL)) {
00934               // Maybe we should support CF_UNICODETEXT if it is available
00935               // too?
00936               hglb = GetClipboardData(CF_TEXT);
00937               if (hglb!=NULL) {
00938                 lptstr = (char *) GlobalLock(hglb);
00939                 if (lptstr != NULL)  {
00940                   char *pChar;
00941                   for (pChar=lptstr; *pChar!=NULL; pChar++) {
00942                     _input_devices[0].keystroke((uchar)*pChar);
00943                   }
00944                   GlobalUnlock(hglb);
00945                 }
00946               }
00947               CloseClipboard();
00948             }
00949           }
00950         }
00951         break;
00952     
00953       case WM_SYSKEYUP:
00954       case WM_KEYUP:
00955         handle_keyrelease(lookup_key(wparam));
00956         break;
00957     
00958       case WM_KILLFOCUS: 
00959         // Record the current state of the keyboard when the focus is
00960         // lost, so we can check it for changes when we regain focus.
00961         GetKeyboardState(_keyboard_state);
00962         break;
00963     
00964       case WM_SETFOCUS: 
00965         {
00966           // When we lose focus, the app may miss key-up events for keys
00967           // that were formerly held down (and vice-versa).  Therefore,
00968           // when focus is regained, compare the state of the keyboard to
00969           // the last known state (stored above, when focus was lost) to
00970           // regenerate the lost keyboard events.
00971     
00972           if (GetForegroundWindow() != _hWnd) {
00973             // Sometimes, particularly on window create, it appears we get
00974             // a WM_SETFOCUS event even though the window hasn't really
00975             // received focus yet.  That's bad and confuses the
00976             // GetKeyboardState logic, below.  The above check filters out
00977             // this case (while testing GetFocus() instead of
00978             // GetForegroundWindow() doesn't).
00979             if(windisplay_cat.is_debug())
00980                 windisplay_cat.debug() << "Ignoring non-foreground WM_SETFOCUS\n";
00981             break;
00982           }
00983     
00984           BYTE new_keyboard_state[num_virtual_keys];
00985           GetKeyboardState(new_keyboard_state);
00986           for (int i = 0; i < num_virtual_keys; i++) {
00987             // Filter out these particular three.  We don't want to test
00988             // these, because these are virtual duplicates for
00989             // VK_LSHIFT/VK_RSHIFT, etc.; and the left/right equivalent is
00990             // also in the table.  If we respect both VK_LSHIFT as well as
00991             // VK_SHIFT, we'll generate two keyboard messages when
00992             // VK_LSHIFT changes state.
00993             if (i != VK_SHIFT && i != VK_CONTROL && i != VK_MENU) {
00994               if (((new_keyboard_state[i] ^ _keyboard_state[i]) & 0x80) != 0) {
00995                 // This key has changed state.
00996                 if ((new_keyboard_state[i] & 0x80) != 0) {
00997                   // The key is now held down.
00998                   // cerr << "key is down: " << lookup_key(i) << "\n";
00999                   handle_keyresume(lookup_key(i));
01000                 } else {
01001                   // The key is now released.
01002                   handle_keyrelease(lookup_key(i));
01003                 }
01004               }
01005             }
01006           }
01007           
01008           // Save the new keyboard state, just for good measure.  This
01009           // really shouldn't be necessary, but it protects against
01010           // inadvertently getting WM_SETFOCUS twice in a row, for
01011           // instance.
01012           memcpy(_keyboard_state, new_keyboard_state,
01013                  sizeof(BYTE) * num_virtual_keys);
01014         }
01015         break;
01016   }
01017 
01018   return DefWindowProc(hwnd, msg, wparam, lparam);
01019 }
01020 
01021 ////////////////////////////////////////////////////////////////////
01022 //     Function: WinGraphicsWindow::static_window_proc
01023 //       Access: Private, Static
01024 //  Description: This is attached to the window class for all
01025 //               WinGraphicsWindow windows; it is called to handle
01026 //               window events.
01027 ////////////////////////////////////////////////////////////////////
01028 LONG WINAPI WinGraphicsWindow::
01029 static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
01030   // Look up the window in our global map.
01031   WindowHandles::const_iterator wi;
01032   wi = _window_handles.find(hwnd);
01033   if (wi != _window_handles.end()) {
01034     // We found the window.
01035     return (*wi).second->window_proc(hwnd, msg, wparam, lparam);
01036   }
01037 
01038   // The window wasn't in the map; we must be creating it right now.
01039   if (_creating_window != (WinGraphicsWindow *)NULL) {
01040     return _creating_window->window_proc(hwnd, msg, wparam, lparam);
01041   }
01042 
01043   // Oops, we weren't creating a window!  Don't know how to handle the
01044   // message, so just pass it on to Windows to deal with it.
01045   return DefWindowProc(hwnd, msg, wparam, lparam);
01046 }
01047 
01048 ////////////////////////////////////////////////////////////////////
01049 //     Function: WinGraphicsWindow::process_1_event
01050 //       Access: Private, Static
01051 //  Description: Handles one event from the message queue.
01052 ////////////////////////////////////////////////////////////////////
01053 void WinGraphicsWindow::
01054 process_1_event() {
01055   MSG msg;
01056 
01057   if (!GetMessage(&msg, NULL, 0, 0)) {
01058     // WM_QUIT received.  We need a cleaner way to deal with this.
01059     //    DestroyAllWindows(false);
01060     exit(msg.wParam);  // this will invoke AtExitFn
01061   }
01062 
01063   // Translate virtual key messages
01064   TranslateMessage(&msg);
01065   // Call window_proc
01066   DispatchMessage(&msg);
01067 }
01068 
01069 ////////////////////////////////////////////////////////////////////
01070 //     Function: WinGraphicsWindow::update_cursor_window
01071 //       Access: Private, Static
01072 //  Description: Changes _cursor_window from its current value to the
01073 //               indicated value.  This also changes the cursor
01074 //               properties appropriately.
01075 ////////////////////////////////////////////////////////////////////
01076 void WinGraphicsWindow::
01077 update_cursor_window(WinGraphicsWindow *to_window) {
01078   bool hide_cursor = false;
01079   if (to_window == (WinGraphicsWindow *)NULL) {
01080     // We are leaving a graphics window; we should restore the Win2000
01081     // effects.
01082     if (_got_saved_params) {
01083       SystemParametersInfo(SPI_SETMOUSETRAILS, NULL, 
01084                            (PVOID)_saved_mouse_trails, NULL);
01085       SystemParametersInfo(SPI_SETCURSORSHADOW, NULL, 
01086                            (PVOID)_saved_cursor_shadow, NULL);
01087       SystemParametersInfo(SPI_SETMOUSEVANISH, NULL,
01088                            (PVOID)_saved_mouse_vanish, NULL);
01089       _got_saved_params = false;
01090     }
01091 
01092   } else {
01093     const WindowProperties &to_props = to_window->get_properties();
01094     hide_cursor = to_props.get_cursor_hidden();
01095 
01096     // We are entering a graphics window; we should save and disable
01097     // the Win2000 effects.  These don't work at all well over a 3-D
01098     // window.
01099 
01100     // These parameters are only defined for Win2000/XP, but they
01101     // should just cause a silent error on earlier OS's, which is OK.
01102     if (!_got_saved_params) {
01103       SystemParametersInfo(SPI_GETMOUSETRAILS, NULL, 
01104                            &_saved_mouse_trails, NULL);
01105       SystemParametersInfo(SPI_GETCURSORSHADOW, NULL, 
01106                            &_saved_cursor_shadow, NULL);
01107       SystemParametersInfo(SPI_GETMOUSEVANISH, NULL, 
01108                            &_saved_mouse_vanish, NULL);
01109       _got_saved_params = true;
01110 
01111       SystemParametersInfo(SPI_SETMOUSETRAILS, NULL, (PVOID)0, NULL);
01112       SystemParametersInfo(SPI_SETCURSORSHADOW, NULL, (PVOID)false, NULL);
01113       SystemParametersInfo(SPI_SETMOUSEVANISH, NULL, (PVOID)false, NULL);
01114     }
01115   }
01116 
01117   if (hide_cursor) {
01118     // We should hide the cursor in the new window.
01119     if (!_cursor_hidden) {
01120       ShowCursor(false);
01121       _cursor_hidden = true;
01122     }
01123   } else {
01124     // We should reveal the cursor in the new window.
01125     if (_cursor_hidden) {
01126       ShowCursor(true);
01127       _cursor_hidden = false;
01128     }
01129   }
01130 
01131   _cursor_window = to_window;
01132 }
01133   
01134 ////////////////////////////////////////////////////////////////////
01135 //     Function: WinGraphicsWindow::register_window_class
01136 //       Access: Private, Static
01137 //  Description: Registers a Window class for all WinGraphicsWindows.
01138 //               This only needs to be done once per session.
01139 ////////////////////////////////////////////////////////////////////
01140 void WinGraphicsWindow::
01141 register_window_class() {
01142   if (_window_class_registered) {
01143     return;
01144   }
01145 
01146   WNDCLASS wc;
01147 
01148   HINSTANCE instance = GetModuleHandle(NULL);
01149 
01150   // Clear before filling in window structure!
01151   ZeroMemory(&wc, sizeof(WNDCLASS));
01152   wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
01153   wc.lpfnWndProc = (WNDPROC)static_window_proc;
01154   wc.hInstance = instance;
01155 
01156   // Might be nice to move these properties into the WindowProperties
01157   // structure, so they don't have to be global for all windows.
01158   string windows_icon_filename = get_icon_filename().to_os_specific();
01159   string windows_mono_cursor_filename = get_mono_cursor_filename().to_os_specific();
01160 
01161   if (!windows_icon_filename.empty()) {
01162     // Note: LoadImage seems to cause win2k internal heap corruption
01163     // (outputdbgstr warnings) if icon is more than 8bpp
01164 
01165     // loads a .ico fmt file
01166     wc.hIcon = (HICON)LoadImage(NULL, windows_icon_filename.c_str(),
01167                                 IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
01168 
01169     if (wc.hIcon == NULL) {
01170       windisplay_cat.warning()
01171         << "windows icon filename '" << windows_icon_filename
01172         << "' not found!!\n";
01173     }
01174   } else {
01175     wc.hIcon = NULL; // use default app icon
01176   }
01177 
01178   _loaded_custom_cursor = false;
01179   if (!windows_mono_cursor_filename.empty()) {
01180     // Note: LoadImage seems to cause win2k internal heap corruption
01181     // (outputdbgstr warnings) if icon is more than 8bpp (because it
01182     // was 'mapping' 16bpp colors to the device?)
01183     
01184     DWORD load_flags = LR_LOADFROMFILE;
01185 
01186     /*
01187     if (_props._fullscreen) {
01188       // I think cursors should use LR_CREATEDIBSECTION since they
01189       // should not be mapped to the device palette (in the case of
01190       // 256-color cursors) since they are not going to be used on the
01191       // desktop
01192       load_flags |= LR_CREATEDIBSECTION;
01193 
01194       // Of course, we can't make this determination here because one
01195       // window class is used for all windows, fullscreen as well as
01196       // desktop windows.
01197     }
01198     */
01199 
01200     // loads a .cur fmt file
01201     _mouse_cursor = (HCURSOR) LoadImage(NULL, windows_mono_cursor_filename.c_str(), IMAGE_CURSOR, 0, 0, load_flags);
01202     
01203     if (_mouse_cursor == NULL) {
01204       windisplay_cat.warning()
01205         << "windows cursor filename '" << windows_mono_cursor_filename
01206         << "' not found!!\n";
01207     } else {
01208       _loaded_custom_cursor = true;
01209     }
01210   }
01211 
01212   if (!_loaded_custom_cursor) {
01213     _mouse_cursor = LoadCursor(NULL, IDC_ARROW);
01214   }
01215 
01216   // even if cursor isnt visible, we need to load it so its visible
01217   // in client-area window border
01218   wc.hCursor = _mouse_cursor;  
01219   wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
01220   wc.lpszMenuName = NULL;
01221   wc.lpszClassName = _window_class_name;
01222   
01223   if (!RegisterClass(&wc)) {
01224     windisplay_cat.error()
01225       << "could not register window class!" << endl;
01226     return;
01227   }
01228   _window_class_registered = true;
01229 }
01230 
01231 // dont pick any video modes < MIN_REFRESH_RATE Hz
01232 #define MIN_REFRESH_RATE 60
01233 // EnumDisplaySettings may indicate 0 or 1 for refresh rate, which means use driver default rate (assume its >min_refresh_rate)
01234 #define ACCEPTABLE_REFRESH_RATE(RATE) ((RATE >= MIN_REFRESH_RATE) || (RATE==0) || (RATE==1))
01235 
01236 ////////////////////////////////////////////////////////////////////
01237 //     Function: WinGraphicsWindow::find_acceptable_display_mode
01238 //       Access: Private, Static
01239 //  Description: Looks for a fullscreen mode that meets the specified
01240 //               size and bitdepth requirements.  Returns true if a
01241 //               suitable mode is found, false otherwise.
01242 ////////////////////////////////////////////////////////////////////
01243 bool WinGraphicsWindow::
01244 find_acceptable_display_mode(DWORD dwWidth, DWORD dwHeight, DWORD bpp,
01245                              DEVMODE &dm) {
01246   int modenum = 0;
01247 
01248   while (1) {
01249     ZeroMemory(&dm, sizeof(dm));
01250     dm.dmSize = sizeof(dm);
01251     
01252     if (!EnumDisplaySettings(NULL, modenum, &dm)) {
01253       break;
01254     }
01255     
01256     if ((dm.dmPelsWidth == dwWidth) && (dm.dmPelsHeight == dwHeight) &&
01257         (dm.dmBitsPerPel == bpp) && 
01258         ACCEPTABLE_REFRESH_RATE(dm.dmDisplayFrequency)) {
01259       return true;
01260     }
01261     modenum++;
01262   }
01263   
01264   return false;
01265 }
01266 
01267 ////////////////////////////////////////////////////////////////////
01268 //     Function: WinGraphicsWindow::show_error_message
01269 //       Access: Private, Static
01270 //  Description: Pops up a dialog box with the indicated Windows error
01271 //               message ID (or the last error message generated) for
01272 //               meaningful display to the user.
01273 ////////////////////////////////////////////////////////////////////
01274 void WinGraphicsWindow::
01275 show_error_message(DWORD message_id) {
01276   LPTSTR message_buffer;
01277 
01278   if (message_id == 0) {
01279     message_id = GetLastError();
01280   }
01281   
01282   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
01283                 NULL, message_id,
01284                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //The user default language
01285                 (LPTSTR)&message_buffer,  // the weird ptrptr->ptr cast is intentional, see FORMAT_MESSAGE_ALLOCATE_BUFFER
01286                 1024, NULL);
01287   MessageBox(GetDesktopWindow(), message_buffer, _T(errorbox_title), MB_OK);
01288   windisplay_cat.fatal() << "System error msg: " << message_buffer << endl;
01289   LocalFree(message_buffer);
01290 }
01291 
01292 ////////////////////////////////////////////////////////////////////
01293 //     Function: WinGraphicsWindow::lookup_key
01294 //       Access: Private
01295 //  Description: Translates the keycode reported by Windows to an
01296 //               appropriate Panda ButtonHandle.
01297 ////////////////////////////////////////////////////////////////////
01298 ButtonHandle WinGraphicsWindow::
01299 lookup_key(WPARAM wparam) const {
01300   // First, check for a few buttons that we filter out when the IME
01301   // window is open.
01302   if (!_ime_active) {
01303     switch(wparam) {
01304     case VK_BACK: return KeyboardButton::backspace();
01305     case VK_DELETE: return KeyboardButton::del();
01306     case VK_ESCAPE: return KeyboardButton::escape();
01307     case VK_SPACE: return KeyboardButton::space();
01308     case VK_UP: return KeyboardButton::up();
01309     case VK_DOWN: return KeyboardButton::down();
01310     case VK_LEFT: return KeyboardButton::left();
01311     case VK_RIGHT: return KeyboardButton::right();
01312     }
01313   }
01314 
01315   // Now check for the rest of the buttons, including the ones that
01316   // we allow through even when the IME window is open.
01317   switch(wparam) {
01318   case VK_TAB: return KeyboardButton::tab();
01319   case VK_PRIOR: return KeyboardButton::page_up();
01320   case VK_NEXT: return KeyboardButton::page_down();
01321   case VK_HOME: return KeyboardButton::home();
01322   case VK_END: return KeyboardButton::end();
01323   case VK_F1: return KeyboardButton::f1();
01324   case VK_F2: return KeyboardButton::f2();
01325   case VK_F3: return KeyboardButton::f3();
01326   case VK_F4: return KeyboardButton::f4();
01327   case VK_F5: return KeyboardButton::f5();
01328   case VK_F6: return KeyboardButton::f6();
01329   case VK_F7: return KeyboardButton::f7();
01330   case VK_F8: return KeyboardButton::f8();
01331   case VK_F9: return KeyboardButton::f9();
01332   case VK_F10: return KeyboardButton::f10();
01333   case VK_F11: return KeyboardButton::f11();
01334   case VK_F12: return KeyboardButton::f12();
01335   case VK_INSERT: return KeyboardButton::insert();
01336   case VK_CAPITAL: return KeyboardButton::caps_lock();
01337   case VK_NUMLOCK: return KeyboardButton::num_lock();
01338   case VK_SCROLL: return KeyboardButton::scroll_lock();
01339   case VK_SNAPSHOT: return KeyboardButton::print_screen();
01340 
01341   case VK_SHIFT:
01342   case VK_LSHIFT:
01343   case VK_RSHIFT:
01344     return KeyboardButton::shift();
01345 
01346   case VK_CONTROL:
01347   case VK_LCONTROL:
01348   case VK_RCONTROL:
01349     return KeyboardButton::control();
01350 
01351   case VK_MENU:
01352   case VK_LMENU:
01353   case VK_RMENU:
01354     return KeyboardButton::alt();
01355 
01356   default:
01357     int key = MapVirtualKey(wparam, 2);
01358     if (isascii(key) && key != 0) {
01359       // We used to try to remap lowercase to uppercase keys
01360       // here based on the state of the shift and/or caps lock
01361       // keys.  But that's a mistake, and doesn't allow for
01362       // international or user-defined keyboards; let Windows
01363       // do that mapping.
01364 
01365       // Nowadays, we make a distinction between a "button"
01366       // and a "keystroke".  A button corresponds to a
01367       // physical button on the keyboard and has a down and up
01368       // event associated.  A keystroke may or may not
01369       // correspond to a physical button, but will be some
01370       // Unicode character and will not have a corresponding
01371       // up event.
01372       return KeyboardButton::ascii_key(tolower(key));
01373     }
01374     break;
01375   }
01376   return ButtonHandle::none();
01377 }
01378 
01379 ////////////////////////////////////////////////////////////////////
01380 //     Function: WinGraphicsWindow::handle_mouse_motion
01381 //       Access: Private
01382 //  Description:
01383 ////////////////////////////////////////////////////////////////////
01384 bool WinGraphicsWindow::
01385 handle_mouse_motion(int x, int y) {
01386   _input_devices[0].set_pointer_in_window(x, y);
01387   return false;
01388 }
01389 
01390 ////////////////////////////////////////////////////////////////////
01391 //     Function: WinGraphicsWindow::handle_mouse_exit
01392 //       Access: Private
01393 //  Description:
01394 ////////////////////////////////////////////////////////////////////
01395 void WinGraphicsWindow::
01396 handle_mouse_exit() {
01397   // note: 'mouse_motion' is considered the 'entry' event
01398   _input_devices[0].set_pointer_out_of_window();
01399 }
01400 
01401 // pops up MsgBox w/system error msg
01402 void PrintErrorMessage(DWORD msgID) {
01403   LPTSTR pMessageBuffer;
01404 
01405   if (msgID==PRINT_LAST_ERROR)
01406     msgID=GetLastError();
01407 
01408   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
01409                 NULL,msgID,
01410                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //The user default language
01411                 (LPTSTR) &pMessageBuffer,  // the weird ptrptr->ptr cast is intentional, see FORMAT_MESSAGE_ALLOCATE_BUFFER
01412                 1024, NULL);
01413   MessageBox(GetDesktopWindow(),pMessageBuffer,_T(errorbox_title),MB_OK);
01414   windisplay_cat.fatal() << "System error msg: " << pMessageBuffer << endl;
01415   LocalFree( pMessageBuffer );
01416 }
01417 
01418 void
01419 ClearToBlack(HWND hWnd, const WindowProperties &props) {
01420   if (!props.has_origin()) {
01421     windisplay_cat.info()
01422       << "Skipping ClearToBlack, no origin specified yet.\n";
01423     return;
01424   }
01425 
01426   if (windisplay_cat.is_debug()) {
01427     windisplay_cat.debug()
01428       << "ClearToBlack(" << hWnd << ", " << props << ")\n";
01429   }
01430   // clear to black
01431   HDC hDC=GetDC(hWnd);  // GetDC is not particularly fast.  if this needs to be super-quick, we should cache GetDC's hDC
01432   RECT clrRect = {
01433     props.get_x_origin(), props.get_y_origin(),
01434     props.get_x_origin() + props.get_x_size(),
01435     props.get_y_origin() + props.get_y_size()
01436   };
01437   FillRect(hDC,&clrRect,(HBRUSH)GetStockObject(BLACK_BRUSH));
01438   ReleaseDC(hWnd,hDC);
01439   GdiFlush();
01440 }
01441 
01442 ////////////////////////////////////////////////////////////////////
01443 //     Function: get_client_rect_screen
01444 //  Description: Fills view_rect with the coordinates of the client
01445 //               area of the indicated window, converted to screen
01446 //               coordinates.
01447 ////////////////////////////////////////////////////////////////////
01448 void get_client_rect_screen(HWND hwnd, RECT *view_rect) {
01449   GetClientRect(hwnd, view_rect);
01450 
01451   POINT ul, lr;
01452   ul.x = view_rect->left;
01453   ul.y = view_rect->top;
01454   lr.x = view_rect->right;
01455   lr.y = view_rect->bottom;
01456 
01457   ClientToScreen(hwnd, &ul);
01458   ClientToScreen(hwnd, &lr);
01459 
01460   view_rect->left = ul.x;
01461   view_rect->top = ul.y;
01462   view_rect->right = lr.x;
01463   view_rect->bottom = lr.y;
01464 }

Generated on Fri May 2 00:44:48 2003 for Panda by doxygen1.3