00001 /*************************************************************************** 00002 *cr 00003 *cr (C) Copyright 1995-2019 The Board of Trustees of the 00004 *cr University of Illinois 00005 *cr All Rights Reserved 00006 *cr 00007 ***************************************************************************/ 00008 /*************************************************************************** 00009 * RCS INFORMATION: 00010 * 00011 * $RCSfile: Win32OpenGLDisplayDevice.C,v $ 00012 * $Author: johns $ $Locker: $ $State: Exp $ 00013 * $Revision: 1.131 $ $Date: 2021年12月21日 23:20:58 $ 00014 * 00015 ***************************************************************************/ 00024 #ifndef _WIN32_WINNT 00025 #define _WIN32_WINNT 0x0400 // hack to allow access to mouse wheel events 00026 #endif 00027 #include <windows.h> // Mouse wheel events and related macros 00028 #include <winuser.h> // Mouse wheel events and related macros 00029 00030 #include "VMDApp.h" 00031 #include "OpenGLDisplayDevice.h" 00032 #include "Inform.h" 00033 #include "utilities.h" 00034 #include "config.h" // VMD version strings etc 00035 00036 #include <stdio.h> 00037 #include <stdlib.h> 00038 #include <math.h> 00039 #include <GL/gl.h> 00040 00041 #include "../msvc/winvmd/res/resource.h" // VMD icon resource 00042 00043 #if 1 00044 // 00045 // Compile-time constant to provide hybrid graphics drivers with a hint to 00046 // favor the use of the high performance GPU when one exists. 00047 // 00048 extern "C" { 00049 // trigger AMD PowerXpress drivers to use the high performance GPU 00050 __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 1; 00051 00052 // trigger NVIDIA Optimus drivers to use the high performance GPU 00053 __declspec(dllexport) DWORD NvOptimusEnablement = 1; 00054 } 00055 #endif 00056 00057 00058 // NOTE: you may have to get copies of the latest OpenGL extension headers 00059 // from the OpenGL web site if your Linux or Win32 machine lacks them: 00060 // http://oss.sgi.com/projects/ogl-sample/registry/ 00061 #include <GL/glext.h> // include OpenGL extension headers 00062 #include <GL/wglext.h> // include OpenGL extension headers 00063 00064 // static data for this object 00065 static const char *glStereoNameStr[OPENGL_STEREO_MODES] = 00066 { "Off", 00067 "QuadBuffered", 00068 "HDTV SideBySide", 00069 "Checkerboard", 00070 "ColumnInterleaved", 00071 "RowInterleaved", 00072 "Anaglyph", 00073 "SideBySide", 00074 "AboveBelow", 00075 "Left", 00076 "Right" }; 00077 00078 static const char *glRenderNameStr[OPENGL_RENDER_MODES] = 00079 { "Normal", 00080 "GLSL", 00081 "Acrobat3D" }; 00082 00083 static const char *glCacheNameStr[OPENGL_CACHE_MODES] = 00084 { "Off", 00085 "On" }; 00086 00087 static char szAppName[] = "VMD"; 00088 static char szAppTitle[]="VMD " VMDVERSION " OpenGL Display"; 00089 00090 LRESULT WINAPI vmdWindowProc( HWND, UINT, WPARAM, LPARAM ); 00091 00092 static int OpenWin32Connection(wgldata * glwsrv) { 00093 WNDCLASS wc; 00094 HINSTANCE hInstance = GetModuleHandle(NULL); 00095 00096 /* Clear (important!) and then fill in the window class structure. */ 00097 memset(&wc, 0, sizeof(WNDCLASS)); 00098 wc.style = CS_OWNDC; 00099 wc.lpfnWndProc = (WNDPROC) vmdWindowProc; 00100 wc.hInstance = hInstance; 00101 #if 1 00102 // use our VMD icon 00103 wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1)); 00104 #else 00105 wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); 00106 #endif 00107 wc.hCursor = LoadCursor(hInstance, IDC_ARROW); 00108 wc.hbrBackground = NULL; /* Default color */ 00109 wc.lpszMenuName = NULL; 00110 wc.lpszClassName = szAppName; 00111 00112 if(!RegisterClass(&wc)) { 00113 printf("Cannot register window class.\n"); 00114 return -1; 00115 } 00116 00117 // get screen size 00118 // XXX There's no Win32 API to get the full multi-monitor desktop, 00119 // so this code doesn't correctly handle multi-monitor systems yet. 00120 // To correctly handle multiple monitors, we'd have to 00121 // walk the device tree, take into account monitor layout/positioning, 00122 // and compute the desktop dimensions from that. Since these values 00123 // are currently only used by do_reposition_window() method, we can 00124 // live with primary-monitor values for the time being. 00125 glwsrv->scrwidth = GetSystemMetrics(SM_CXSCREEN); 00126 glwsrv->scrheight = GetSystemMetrics(SM_CYSCREEN); 00127 00128 return 0; 00129 } 00130 00131 static int PFDHasStereo(int ID, HDC hDC) { 00132 PIXELFORMATDESCRIPTOR pfd; 00133 DescribePixelFormat(hDC, ID, sizeof(PIXELFORMATDESCRIPTOR), &pfd); 00134 00135 #if 0 00136 // print a message if we find out we've got an accelerated mode 00137 if ((pfd.dwFlags & PFD_GENERIC_ACCELERATED) || 00138 !(pfd.dwFlags & PFD_GENERIC_FORMAT)) 00139 msgInfo << "Hardware 3D Acceleration enabled." << sendmsg; 00140 else 00141 msgInfo << "No hardware 3D Acceleration found." << sendmsg; 00142 #endif 00143 00144 if (pfd.dwFlags & PFD_STEREO) 00145 return 1; 00146 00147 return 0; 00148 } 00149 00150 #if 0 00151 static void PrintPFD(int ID, HDC hDC) { 00152 PIXELFORMATDESCRIPTOR pfd; 00153 FILE * ofp; 00154 00155 if (ID == 0) { 00156 int i, num; 00157 num = DescribePixelFormat(hDC, 1, sizeof(PIXELFORMATDESCRIPTOR), &pfd); 00158 for (i=1; i<num; i++) 00159 PrintPFD(i, hDC); 00160 00161 return; 00162 } 00163 00164 DescribePixelFormat(hDC, ID, sizeof(PIXELFORMATDESCRIPTOR), &pfd); 00165 00166 ofp=fopen("c:/video.txt", "a+"); 00167 if (ofp == NULL) 00168 ofp=stdout; 00169 00170 if (pfd.cColorBits < 15) { 00171 fprintf(ofp, "Windows Pixel Format ID: %d -- not enough color bits\n", ID); 00172 } 00173 else { 00174 fprintf(ofp, "\nWindows Pixel Format ID: %d\n", ID); 00175 fprintf(ofp, " Color Buffer Depth: %d bits\n", pfd.cColorBits); 00176 fprintf(ofp, " Z Buffer Depth: %d bits\n", pfd.cDepthBits); 00177 if (pfd.dwFlags & PFD_DOUBLEBUFFER) 00178 fprintf(ofp, " PFD_DOUBLEBUFFER\n"); 00179 if (pfd.dwFlags & PFD_STEREO) 00180 fprintf(ofp, " PFD_STEREO\n"); 00181 if (pfd.dwFlags & PFD_DRAW_TO_WINDOW) 00182 fprintf(ofp, " PFD_DRAW_TO_WINDOW\n"); 00183 if (pfd.dwFlags & PFD_SUPPORT_GDI) 00184 fprintf(ofp, " PFD_SUPPORT_GDI\n"); 00185 if (pfd.dwFlags & PFD_SUPPORT_OPENGL) 00186 fprintf(ofp, " PFD_SUPPORT_OPENGL\n"); 00187 if (pfd.dwFlags & PFD_SWAP_EXCHANGE) 00188 fprintf(ofp, " PFD_SWAP_EXCHANGE\n"); 00189 if (pfd.dwFlags & PFD_SWAP_COPY) 00190 fprintf(ofp, " PFD_SWAP_COPY\n"); 00191 if (pfd.dwFlags & PFD_SWAP_LAYER_BUFFERS) 00192 fprintf(ofp, " PFD_SWAP_LAYER_BUFFERS\n"); 00193 if (pfd.dwFlags & PFD_GENERIC_ACCELERATED) 00194 fprintf(ofp, " PFD_GENERIC_ACCELERATED\n"); 00195 if (pfd.dwFlags & PFD_GENERIC_FORMAT) 00196 fprintf(ofp, " PFD_GENERIC_FORMAT\n"); 00197 } 00198 if (ofp != NULL && ofp != stdout) 00199 fclose(ofp); 00200 } 00201 #endif 00202 00203 static HGLRC SetupOpenGL(wgldata *glwsrv) { 00204 int ID; 00205 HDC hDC; 00206 HGLRC hRC; 00207 00208 PIXELFORMATDESCRIPTOR pfd = { 00209 sizeof (PIXELFORMATDESCRIPTOR), /* struct size */ 00210 1, /* Version number */ 00211 PFD_DRAW_TO_WINDOW /* Flags, draw to a window, */ 00212 | PFD_DOUBLEBUFFER /* Requires Doublebuffer hw */ 00213 | PFD_STEREO /* we want stereo if possible */ 00214 | PFD_SUPPORT_OPENGL, /* use OpenGL */ 00215 PFD_TYPE_RGBA, /* RGBA pixel values */ 00216 16, /* 24-bit color */ 00217 0, 0, 0, /* RGB bits & shift sizes. */ 00218 0, 0, 0, /* Don't care about them */ 00219 0, 0, /* No alpha buffer info */ 00220 0, 0, 0, 0, 0, /* No accumulation buffer */ 00221 16, /* depth buffer */ 00222 1, /* stencil buffer */ 00223 0, /* No auxiliary buffers */ 00224 PFD_MAIN_PLANE, /* Layer type */ 00225 0, /* Reserved (must be 0) */ 00226 0, /* No layer mask */ 00227 0, /* No visible mask */ 00228 0 /* No damage mask */ 00229 }; 00230 00231 hDC = GetDC(glwsrv->hWnd); 00232 ID = ChoosePixelFormat(hDC, &pfd); 00233 00234 /* 00235 * catch errors here. 00236 * If ID is zero, then there's 00237 * something wrong... most likely the window's 00238 * style bits are incorrect (in CreateWindow() ) 00239 * or OpenGL isn't installed on this machine 00240 */ 00241 00242 if (ID == 0) { 00243 printf("Error selecting OpenGL Pixel Format!!\n"); 00244 return NULL; 00245 } 00246 00247 glwsrv->PFDisStereo = PFDHasStereo(ID, hDC); 00248 //PrintPFD(ID, hDC); 00249 //printf("*** Setting Windows OpenGL Pixel Format to ID %d ***\n", ID); 00250 SetPixelFormat( hDC, ID, &pfd ); 00251 00252 hRC = wglCreateContext(hDC); 00253 ReleaseDC(glwsrv->hWnd, hDC); 00254 00255 return hRC; 00256 } 00257 00258 static int myCreateWindow(OpenGLDisplayDevice *ogldispdev, 00259 int xpos, int ypos, int xs, int ys) { 00260 /* Create a main window for this application instance. */ 00261 ogldispdev->glwsrv.hWnd = 00262 CreateWindow( 00263 szAppName, /* app name */ 00264 szAppTitle, /* Text for window title bar */ 00265 WS_OVERLAPPEDWINDOW /* Window style */ 00266 | WS_CLIPCHILDREN 00267 | WS_CLIPSIBLINGS, /* NEED THESE for OpenGL calls to work! */ 00268 xpos, ypos, 00269 xs, ys, 00270 NULL, /* no parent window */ 00271 NULL, /* Use the window class menu. */ 00272 GetModuleHandle(NULL), /* This instance owns this window */ 00273 ogldispdev /* We pass in the caller class ptr */ 00274 ); 00275 00276 if (!ogldispdev->glwsrv.hWnd) { 00277 printf("Couldn't Open Window!!\n"); 00278 return -1; 00279 } 00280 00281 ogldispdev->glwsrv.hDC = GetDC(ogldispdev->glwsrv.hWnd); 00282 wglMakeCurrent(ogldispdev->glwsrv.hDC, ogldispdev->glwsrv.hRC); 00283 00284 /* Make the window visible & update its client area */ 00285 ShowWindow(ogldispdev->glwsrv.hWnd, SW_SHOW); /* Show the window */ 00286 UpdateWindow(ogldispdev->glwsrv.hWnd ); /* Sends WM_PAINT msg */ 00287 DragAcceptFiles(ogldispdev->glwsrv.hWnd, TRUE); /* Enable Drag & Drop */ 00288 00289 return 0; 00290 } 00291 00292 static void vmd_transwin32mouse(OpenGLDisplayDevice * d, LPARAM l) { 00293 int x, y; 00294 x = LOWORD(l); 00295 y = HIWORD(l); 00296 if(x & 1 << 15) x -= (1 << 16); // handle mouse capture in negative range 00297 if(y & 1 << 15) y -= (1 << 16); // handle mouse capture in negative range 00298 d->glwsrv.MouseX = x; 00299 d->glwsrv.MouseY = (d->ySize) - y; // translate to coords VMD likes (GL-like) 00300 } 00301 00302 00303 #ifdef VMDSPACEWARE 00304 // Windows code to talk to Spaceball device 00305 static void vmd_setupwin32spaceball(wgldata *glwsrv) { 00306 SiOpenData oData; 00307 enum SpwRetVal res; 00308 00309 // init the sball pointer to NULL by default, used to determine if we 00310 // had a healthy init later on. 00311 glwsrv->sball = NULL; 00312 00313 switch (SiInitialize()) { 00314 case SPW_NO_ERROR: 00315 break; 00316 00317 case SPW_DLL_LOAD_ERROR: 00318 msgInfo << "Spaceball driver not installed. Spaceball interface disabled." << sendmsg; 00319 return; 00320 00321 default: 00322 msgInfo << "Spaceball did not initialize properly. Spaceball interface disabled." << sendmsg; 00323 return; 00324 } 00325 00326 SiOpenWinInit(&oData, glwsrv->hWnd); // init win platform data 00327 SiSetUiMode(glwsrv->sball, SI_UI_ALL_CONTROLS); // config softbutton display 00328 00329 // actually start a connection to the device now that the UI mode 00330 // and window system data are setup. 00331 glwsrv->sball = SiOpen("VMD", SI_ANY_DEVICE, SI_NO_MASK, SI_EVENT, &oData); 00332 if ((glwsrv->sball == NULL) || (glwsrv->sball == SI_NO_HANDLE)) { 00333 SiTerminate(); // shutdown spaceware input library 00334 msgInfo << "Spaceball is unresponsive. Spaceball interface disabled." << sendmsg; 00335 glwsrv->sball = NULL; // NULL out the handle for sure. 00336 return; 00337 } 00338 00339 res = SiBeep(glwsrv->sball, "CcCc"); // beep the spaceball 00340 if ((glwsrv->sball != NULL) && (glwsrv->sball != SI_NO_HANDLE)) 00341 msgInfo << "Spaceball found, software interface initialized." << sendmsg; 00342 } 00343 00344 static void vmd_closewin32spaceball(wgldata *glwsrv) { 00345 enum SpwRetVal res; 00346 00347 if (glwsrv->sball != NULL) { 00348 res = SiClose(glwsrv->sball); // close spaceball device 00349 if (res != SPW_NO_ERROR) 00350 msgInfo << "An error occured while shutting down the Spaceball device." << sendmsg; 00351 00352 SiTerminate(); // shutdown spaceware input library 00353 } 00354 00355 glwsrv->sball = NULL; // NULL out the handle. 00356 } 00357 00358 static int vmd_processwin32spaceballevent(wgldata *glwsrv, UINT msg, WPARAM wParam, LPARAM lParam) { 00359 00360 if (glwsrv == NULL) 00361 return 0; 00362 00363 if (glwsrv->sball == NULL) 00364 return 0; // no spaceball attached/running 00365 00366 // Check to see if this message is a spaceball message 00367 SiGetEventWinInit(&glwsrv->spwedata, msg, wParam, lParam); 00368 00369 if (SiGetEvent(glwsrv->sball, 0, &glwsrv->spwedata, &glwsrv->spwevent) == SI_IS_EVENT) { 00370 return 1; 00371 } 00372 00373 return 0; 00374 } 00375 #endif 00376 00377 00378 LRESULT WINAPI vmdWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { 00379 PAINTSTRUCT ps; /* Paint structure. */ 00380 00381 // XXX this enum has to be replicated here since its otherwise 00382 // private to the DisplayDevice class and children. 00383 enum EventCodes { WIN_REDRAW, WIN_LEFT, WIN_MIDDLE, WIN_RIGHT, 00384 WIN_WHEELUP, WIN_WHEELDOWN, WIN_MOUSEX, WIN_MOUSEY, 00385 WIN_KBD, 00386 WIN_KBD_ESCAPE, 00387 WIN_KBD_UP, 00388 WIN_KBD_DOWN, 00389 WIN_KBD_LEFT, 00390 WIN_KBD_RIGHT, 00391 WIN_KBD_PAGE_UP, 00392 WIN_KBD_PAGE_DOWN, 00393 WIN_KBD_HOME, 00394 WIN_KBD_END, 00395 WIN_KBD_INSERT, 00396 WIN_KBD_DELETE, 00397 WIN_KBD_F1, WIN_KBD_F2, WIN_KBD_F3, WIN_KBD_F4, 00398 WIN_KBD_F5, WIN_KBD_F6, WIN_KBD_F7, WIN_KBD_F8, 00399 WIN_KBD_F9, WIN_KBD_F10, WIN_KBD_F11, WIN_KBD_F12, 00400 WIN_NOEVENT }; 00401 wgldata *glwsrv; 00402 OpenGLDisplayDevice * ogldispdev; 00403 00404 // Upon first window creation, immediately set our user-data field 00405 // to store caller-provided handles for this window instance 00406 if (msg == WM_NCCREATE) { 00407 #if defined(_M_X64) || defined(_WIN64) || defined(_Wp64) 00408 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) (((CREATESTRUCT *) lParam)->lpCreateParams)); 00409 #else 00410 SetWindowLong(hwnd, GWL_USERDATA, (LONG) (((CREATESTRUCT *) lParam)->lpCreateParams)); 00411 #endif 00412 } 00413 00414 // check to make sure we have a valid window data structure in case 00415 // it is destroyed while there are still pending messages... 00416 #if defined(_M_X64) || defined(_WIN64) || defined(_Wp64) 00417 ogldispdev = (OpenGLDisplayDevice *) GetWindowLongPtr(hwnd, GWLP_USERDATA); 00418 #else 00419 ogldispdev = (OpenGLDisplayDevice *) GetWindowLong(hwnd, GWL_USERDATA); 00420 #endif 00421 00422 // when VMD destroys its window data structures it is possible that 00423 // the window could still get messages briefly thereafter, this prevents 00424 // us from attempting to handle any messages when the VMD state that goes 00425 // with the window has already been destructed. (most notably when using 00426 // the spaceball..) If we have a NULL pointer, let windows handle the 00427 // event for us using the default window proc. 00428 if (ogldispdev == NULL) 00429 return DefWindowProc(hwnd, msg, wParam, lParam); 00430 00431 glwsrv = &ogldispdev->glwsrv; 00432 00433 #ifdef VMDSPACEWARE 00434 // see if it is a spaceball event, if so do something about it. 00435 if (vmd_processwin32spaceballevent(glwsrv, msg, wParam, lParam)) 00436 return 0; // 00437 #endif 00438 00439 switch(msg) { 00440 case WM_CREATE: 00441 glwsrv->hWnd = hwnd; 00442 glwsrv->hRC = SetupOpenGL(glwsrv); 00443 glwsrv->WEvents = WIN_REDRAW; 00444 return 0; 00445 00446 case WM_SIZE: 00447 wglMakeCurrent(glwsrv->hDC, glwsrv->hRC); 00448 ogldispdev->xSize = LOWORD(lParam); 00449 ogldispdev->ySize = HIWORD(lParam); 00450 ogldispdev->reshape(); 00451 glViewport(0, 0, (GLsizei) ogldispdev->xSize, (GLsizei) ogldispdev->ySize); 00452 glwsrv->WEvents = WIN_REDRAW; 00453 return 0; 00454 00455 case WM_SIZING: 00456 wglMakeCurrent(glwsrv->hDC, glwsrv->hRC); 00457 glClear(GL_COLOR_BUFFER_BIT); 00458 SwapBuffers(glwsrv->hDC); 00459 glDrawBuffer(GL_BACK); 00460 return 0; 00461 00462 case WM_CLOSE: 00463 PostQuitMessage(0); 00464 return 0; 00465 00466 case WM_PAINT: 00467 BeginPaint(hwnd, &ps); 00468 EndPaint(hwnd, &ps); 00469 glwsrv->WEvents = WIN_REDRAW; 00470 return 0; 00471 00472 case WM_KEYDOWN: 00473 glwsrv->KeyFlag = MapVirtualKey((UINT) wParam, 2); // map to ASCII 00474 glwsrv->WEvents = WIN_KBD; 00475 if (glwsrv->KeyFlag == 0) { 00476 unsigned int keysym = (unsigned int) wParam; 00477 switch (keysym) { 00478 case VK_ESCAPE: glwsrv->WEvents = WIN_KBD_ESCAPE; break; 00479 case VK_UP: glwsrv->WEvents = WIN_KBD_UP; break; 00480 case VK_DOWN: glwsrv->WEvents = WIN_KBD_DOWN; break; 00481 case VK_LEFT: glwsrv->WEvents = WIN_KBD_LEFT; break; 00482 case VK_RIGHT: glwsrv->WEvents = WIN_KBD_RIGHT; break; 00483 case VK_PRIOR: glwsrv->WEvents = WIN_KBD_PAGE_UP; break; 00484 case VK_NEXT: glwsrv->WEvents = WIN_KBD_PAGE_DOWN; break; 00485 case VK_HOME: glwsrv->WEvents = WIN_KBD_HOME; break; 00486 case VK_END: glwsrv->WEvents = WIN_KBD_END; break; 00487 case VK_INSERT: glwsrv->WEvents = WIN_KBD_INSERT; break; 00488 case VK_DELETE: glwsrv->WEvents = WIN_KBD_DELETE; break; 00489 case VK_F1: glwsrv->WEvents = WIN_KBD_F1; break; 00490 case VK_F2: glwsrv->WEvents = WIN_KBD_F2; break; 00491 case VK_F3: glwsrv->WEvents = WIN_KBD_F3; break; 00492 case VK_F4: glwsrv->WEvents = WIN_KBD_F4; break; 00493 case VK_F5: glwsrv->WEvents = WIN_KBD_F5; break; 00494 case VK_F6: glwsrv->WEvents = WIN_KBD_F6; break; 00495 case VK_F7: glwsrv->WEvents = WIN_KBD_F7; break; 00496 case VK_F8: glwsrv->WEvents = WIN_KBD_F8; break; 00497 case VK_F9: glwsrv->WEvents = WIN_KBD_F9; break; 00498 case VK_F10: glwsrv->WEvents = WIN_KBD_F10; break; 00499 case VK_F11: glwsrv->WEvents = WIN_KBD_F11; break; 00500 case VK_F12: glwsrv->WEvents = WIN_KBD_F12; break; 00501 default: 00502 glwsrv->WEvents = WIN_NOEVENT; 00503 break; 00504 } 00505 } 00506 return 0; 00507 00508 case WM_MOUSEMOVE: 00509 vmd_transwin32mouse(ogldispdev, lParam); 00510 glwsrv->MouseFlags = (long) wParam; 00511 return 0; 00512 00513 case WM_MOUSEWHEEL: 00514 { 00515 int zDelta = ((short) HIWORD(wParam)); 00516 // XXX 00517 // zDelta is in positive or negative multiples of WHEEL_DELTA for 00518 // clicky type scroll wheels on existing mice, may need to 00519 // recode this for continuous wheels at some future point in time. 00520 // WHEEL_DELTA is 120 in current versions of Windows. 00521 // We only activate an event if the user moves the mouse wheel at 00522 // least half of WHEEL_DELTA, so that they don't do it by accident 00523 // all the time. 00524 if (zDelta > (WHEEL_DELTA / 2)) { 00525 glwsrv->WEvents = WIN_WHEELUP; 00526 } else if (zDelta < -(WHEEL_DELTA / 2)) { 00527 glwsrv->WEvents = WIN_WHEELDOWN; 00528 } 00529 } 00530 return 0; 00531 00532 case WM_LBUTTONDOWN: 00533 SetCapture(hwnd); 00534 vmd_transwin32mouse(ogldispdev, lParam); 00535 glwsrv->MouseFlags = (long) wParam; 00536 glwsrv->WEvents = WIN_LEFT; 00537 return 0; 00538 00539 case WM_LBUTTONUP: 00540 vmd_transwin32mouse(ogldispdev, lParam); 00541 glwsrv->MouseFlags = (long) wParam; 00542 glwsrv->WEvents = WIN_LEFT; 00543 if (!(glwsrv->MouseFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON))) 00544 ReleaseCapture(); 00545 return 0; 00546 00547 case WM_MBUTTONDOWN: 00548 SetCapture(hwnd); 00549 vmd_transwin32mouse(ogldispdev, lParam); 00550 glwsrv->MouseFlags = (long) wParam; 00551 glwsrv->WEvents = WIN_MIDDLE; 00552 return 0; 00553 00554 case WM_MBUTTONUP: 00555 vmd_transwin32mouse(ogldispdev, lParam); 00556 glwsrv->MouseFlags = (long) wParam; 00557 glwsrv->WEvents = WIN_MIDDLE; 00558 if (!(glwsrv->MouseFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON))) 00559 ReleaseCapture(); 00560 return 0; 00561 00562 case WM_RBUTTONDOWN: 00563 SetCapture(hwnd); 00564 vmd_transwin32mouse(ogldispdev, lParam); 00565 glwsrv->MouseFlags = (long) wParam; 00566 glwsrv->WEvents = WIN_RIGHT; 00567 return 0; 00568 00569 case WM_RBUTTONUP: 00570 vmd_transwin32mouse(ogldispdev, lParam); 00571 glwsrv->MouseFlags = (long) wParam; 00572 glwsrv->WEvents = WIN_RIGHT; 00573 if (!(glwsrv->MouseFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON))) 00574 ReleaseCapture(); 00575 return 0; 00576 00577 case WM_SETCURSOR: 00578 // We process the mouse cursor hit test codes here, they tell us 00579 // what part of the window we're over, which helps us set the cursor 00580 // to the correct style for sizing borders, moves, etc. 00581 switch (LOWORD(lParam)) { 00582 case HTBOTTOM: 00583 case HTTOP: 00584 SetCursor(LoadCursor(NULL, IDC_SIZENS)); 00585 break; 00586 00587 case HTLEFT: 00588 case HTRIGHT: 00589 SetCursor(LoadCursor(NULL, IDC_SIZEWE)); 00590 break; 00591 00592 case HTTOPRIGHT: 00593 case HTBOTTOMLEFT: 00594 SetCursor(LoadCursor(NULL, IDC_SIZENESW)); 00595 break; 00596 00597 case HTTOPLEFT: 00598 case HTBOTTOMRIGHT: 00599 SetCursor(LoadCursor(NULL, IDC_SIZENWSE)); 00600 break; 00601 00602 case HTCAPTION: 00603 SetCursor(LoadCursor(NULL, IDC_ARROW)); 00604 break; 00605 00606 case HTCLIENT: 00607 default: 00608 ogldispdev->set_cursor(glwsrv->cursornum); 00609 } 00610 return 0; 00611 00612 // 00613 // Handle Windows File Drag and Drop Operations 00614 // This code needs to be linked against SHELL32.DLL 00615 // 00616 case WM_DROPFILES: 00617 { 00618 char lpszFile[4096]; 00619 UINT numfiles, fileindex, numc; 00620 HDROP hDropInfo = (HDROP)wParam; 00621 00622 // Get the number of simultaneous dragged/dropped files. 00623 numfiles = DragQueryFile(hDropInfo, (DWORD)(-1), (LPSTR)NULL, 0); 00624 00625 msgInfo << "Ignoring Drag and Drop operation, received " 00626 << ((int) numfiles) << " files:" << sendmsg; 00627 00628 FileSpec spec; 00629 for (fileindex=0; fileindex<numfiles; fileindex++) { 00630 // lpszFile: complete pathname with device, colon and backslashes 00631 numc = DragQueryFile(hDropInfo, fileindex, (char *) &lpszFile, 4096); 00632 00633 // VMD loads the file(s) here, or queues them up in its own 00634 // list to decide how to cope with them. Deciding how to deal 00635 // with these files is definitely the tricky part. 00636 msgInfo << " File(" << ((int) fileindex) << "): " << lpszFile 00637 << " (numc=" << ((int) numc) << ")" << sendmsg; 00638 00639 // attempt to load the file into a new molecule 00640 ogldispdev->vmdapp->molecule_load(-1, lpszFile, NULL, &spec); 00641 } 00642 DragFinish(hDropInfo); // finish drop operation and release memory 00643 } 00644 return 0; 00645 00646 default: 00647 return DefWindowProc(hwnd, msg, wParam, lParam); 00648 } 00649 00650 return 0; 00651 } 00652 00653 00655 00656 OpenGLDisplayDevice::OpenGLDisplayDevice() 00657 : OpenGLRenderer("VMD " VMDVERSION " OpenGL Display") { 00658 // set up data possible before opening window 00659 stereoNames = glStereoNameStr; 00660 stereoModes = OPENGL_STEREO_MODES; 00661 00662 renderNames = glRenderNameStr; 00663 renderModes = OPENGL_RENDER_MODES; 00664 00665 cacheNames = glCacheNameStr; 00666 cacheModes = OPENGL_CACHE_MODES; 00667 00668 memset(&glwsrv, 0, sizeof(glwsrv)); 00669 have_window = FALSE; 00670 screenX = screenY = 0; 00671 vmdapp = NULL; 00672 } 00673 00674 // init ... open a window and set initial default values 00675 int OpenGLDisplayDevice::init(int argc, char **argv, VMDApp *app, int *size, int *loc) { 00676 vmdapp = app; // save VMDApp handle for use by drag-and-drop handlers 00677 00678 // open the window 00679 if (open_window(name, size, loc, argc, argv) != 0) return FALSE; 00680 if (!have_window) return FALSE; 00681 00682 // get screen size 00683 // XXX There's no Win32 API to get the full multi-monitor desktop, 00684 // so this code doesn't correctly handle multi-monitor systems yet. 00685 // To correctly handle multiple monitors, we'd have to 00686 // walk the device tree, take into account monitor layout/positioning, 00687 // and compute the desktop dimensions from that. Since these values 00688 // are currently only used by do_reposition_window() method, we can 00689 // live with primary-monitor values for the time being. 00690 screenX = GetSystemMetrics(SM_CXSCREEN); 00691 screenY = GetSystemMetrics(SM_CYSCREEN); 00692 00693 // set flags for the capabilities of this display 00694 ext->hasmultisample = FALSE; // no code for this extension yet 00695 ext->nummultisamples = 0; 00696 aaAvailable = FALSE; 00697 00698 // set default settings 00699 if (ext->hasmultisample) { 00700 aa_on(); // enable fast multisample based antialiasing by default 00701 // other antialiasing techniques are slow, so only multisample 00702 // makes sense to enable by default. 00703 } 00704 00705 cueingAvailable = TRUE; 00706 cueing_on(); // leave depth cueing on by default, despite the speed hit. 00707 00708 cullingAvailable = TRUE; 00709 culling_off(); 00710 00711 set_sphere_mode(sphereMode); 00712 set_sphere_res(sphereRes); 00713 set_line_width(lineWidth); 00714 set_line_style(lineStyle); 00715 00716 // reshape and clear the display, which initializes some other variables 00717 reshape(); 00718 normal(); 00719 clear(); 00720 update(); 00721 00722 // successfully created window 00723 return TRUE; 00724 } 00725 00726 // destructor ... close the window 00727 OpenGLDisplayDevice::~OpenGLDisplayDevice(void) { 00728 if (have_window) { 00729 // close and delete windows, contexts, and display connections 00730 free_opengl_ctx(); // free display lists, textures, etc 00731 00732 #if VMDSPACEWARE 00733 vmd_closewin32spaceball(&glwsrv); 00734 #endif 00735 } 00736 } 00737 00738 00740 00741 // create a new window and set it's characteristics 00742 int OpenGLDisplayDevice::open_window(char *nm, int *size, int *loc, 00743 int argc, char** argv) { 00744 int SX = 596, SY = 190; 00745 if (loc) { 00746 SX = loc[0]; 00747 // X screen uses Y increasing from upper-left corner down; this is 00748 // opposite to what GL does, which is the way VMD was set up originally 00749 SY = screenY - loc[1] - size[1]; 00750 } 00751 glwsrv.cursornum = 0; // initialize cursor number 00752 00753 // window opening stuff goes here 00754 int rc = OpenWin32Connection(&glwsrv); 00755 if (rc != 0) { 00756 return -1; 00757 } 00758 00759 xOrig = 0; 00760 yOrig = 0; 00761 xSize = size[0]; 00762 ySize = size[1]; 00763 glwsrv.width = xSize; 00764 glwsrv.height = ySize; 00765 rc = myCreateWindow(this, 0, 0, glwsrv.width, glwsrv.height); 00766 if (rc != 0) { 00767 return -1; 00768 } 00769 00770 // Determine if stereo is available 00771 if (glwsrv.PFDisStereo == 0) { 00772 ext->hasstereo = FALSE; 00773 } else { 00774 ext->hasstereo = TRUE; 00775 } 00776 ext->stereodrawforced = FALSE; // don't force stereo draws initially 00777 00778 setup_initial_opengl_state(); 00779 00780 #ifdef VMDSPACEWARE 00781 vmd_setupwin32spaceball(&glwsrv); 00782 #endif 00783 00784 // normal return: window was successfully created 00785 have_window = TRUE; 00786 // return window id 00787 return 0; 00788 } 00789 00790 00791 int OpenGLDisplayDevice::prepare3D(int do_clear) { 00792 // force reset of OpenGL context back to ours in case something 00793 // else modified the OpenGL state 00794 wglMakeCurrent(glwsrv.hDC, glwsrv.hRC); 00795 00796 return OpenGLRenderer::prepare3D(do_clear); 00797 } 00798 00799 void OpenGLDisplayDevice::do_resize_window(int width, int height) { 00800 RECT rcClient, rcWindow; 00801 POINT ptDiff; 00802 GetClientRect(glwsrv.hWnd, &rcClient); 00803 GetWindowRect(glwsrv.hWnd, &rcWindow); 00804 ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right; 00805 ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom; 00806 MoveWindow(glwsrv.hWnd, rcWindow.left, rcWindow.top, width + ptDiff.x, height + ptDiff.y, TRUE); 00807 } 00808 00809 void OpenGLDisplayDevice::do_reposition_window(int xpos, int ypos) { 00810 RECT rcClient, rcWindow; 00811 GetClientRect(glwsrv.hWnd, &rcClient); 00812 GetWindowRect(glwsrv.hWnd, &rcWindow); 00813 MoveWindow(glwsrv.hWnd, xpos, ypos, rcWindow.right-rcWindow.left, rcWindow.bottom-rcWindow.top, TRUE); 00814 } 00815 00816 00818 00819 // 00820 // get the current state of the device's pointer (i.e. cursor if it has one) 00821 // 00822 00823 // abs X pos of cursor from lower-left corner of display 00824 int OpenGLDisplayDevice::x(void) { 00825 return glwsrv.MouseX; 00826 } 00827 00828 // same, for Y direction 00829 int OpenGLDisplayDevice::y(void) { 00830 return glwsrv.MouseY; 00831 } 00832 00833 // return the current state of the shift, control, and alt keys 00834 int OpenGLDisplayDevice::shift_state(void) { 00835 int retval = 0; 00836 00837 if ((glwsrv.MouseFlags & MK_SHIFT) != 0) 00838 retval |= SHIFT; 00839 00840 if ((glwsrv.MouseFlags & MK_CONTROL) != 0) 00841 retval |= CONTROL; 00842 00843 return retval; 00844 } 00845 00846 // return the spaceball state, if any 00847 int OpenGLDisplayDevice::spaceball(int *rx, int *ry, int *rz, int *tx, int *ty, int *tz, int *buttons) { 00848 00849 #ifdef VMDSPACEWARE 00850 if (glwsrv.sball != NULL) { 00851 *rx = glwsrv.spwevent.u.spwData.mData[SI_RX]; 00852 *ry = glwsrv.spwevent.u.spwData.mData[SI_RY]; 00853 *rz = glwsrv.spwevent.u.spwData.mData[SI_RZ]; 00854 *tx = glwsrv.spwevent.u.spwData.mData[SI_TX]; 00855 *ty = glwsrv.spwevent.u.spwData.mData[SI_TY]; 00856 *tz = glwsrv.spwevent.u.spwData.mData[SI_TZ]; 00857 *buttons = glwsrv.spwevent.u.spwData.bData.current; 00858 return 1; 00859 } 00860 #endif 00861 00862 return 0; 00863 } 00864 00865 00866 // set the Nth cursor shape as the current one. 00867 void OpenGLDisplayDevice::set_cursor(int n) { 00868 glwsrv.cursornum = n; // hack to save cursor state when mouse enters/leaves 00869 00870 switch (n) { 00871 default: 00872 case DisplayDevice::NORMAL_CURSOR: 00873 SetCursor(LoadCursor(NULL, IDC_ARROW)); 00874 break; 00875 00876 case DisplayDevice::TRANS_CURSOR: 00877 SetCursor(LoadCursor(NULL, IDC_SIZEALL)); 00878 break; 00879 00880 case DisplayDevice::SCALE_CURSOR: 00881 SetCursor(LoadCursor(NULL, IDC_SIZEWE)); 00882 break; 00883 00884 case DisplayDevice::PICK_CURSOR: 00885 SetCursor(LoadCursor(NULL, IDC_CROSS)); 00886 break; 00887 00888 case DisplayDevice::WAIT_CURSOR: 00889 SetCursor(LoadCursor(NULL, IDC_WAIT)); 00890 break; 00891 } 00892 } 00893 00894 00895 // 00896 // event handling routines 00897 // 00898 00899 // queue the standard events (need only be called once ... but this is 00900 // not done automatically by the window because it may not be necessary or 00901 // even wanted) 00902 void OpenGLDisplayDevice::queue_events(void) { 00903 } 00904 00905 // read the next event ... returns an event type (one of the above ones), 00906 // and a value. Returns success, and sets arguments. 00907 int OpenGLDisplayDevice::read_event(long &retdev, long &retval) { 00908 MSG msg; 00909 00910 // This pumps the Windows message queue, forcing WEvents to be updated 00911 // by the time we return from DispatchMessage. 00912 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { 00913 TranslateMessage(&msg); // translate the message 00914 DispatchMessage(&msg); // fire it off to the window proc 00915 } 00916 00917 retdev = glwsrv.WEvents; 00918 00919 switch (glwsrv.WEvents) { 00920 case WIN_REDRAW: 00921 glwsrv.WEvents = WIN_NOEVENT; 00922 // reshape() is already called from within the window proc. 00923 _needRedraw = 1; 00924 return FALSE; 00925 00926 case WIN_KBD: 00927 if (glwsrv.KeyFlag != '0円') { 00928 retval = glwsrv.KeyFlag; 00929 glwsrv.WEvents = WIN_NOEVENT; 00930 return TRUE; 00931 } 00932 break; 00933 00934 case WIN_KBD_ESCAPE: 00935 case WIN_KBD_UP: 00936 case WIN_KBD_DOWN: 00937 case WIN_KBD_LEFT: 00938 case WIN_KBD_RIGHT: 00939 case WIN_KBD_PAGE_UP: 00940 case WIN_KBD_PAGE_DOWN: 00941 case WIN_KBD_HOME: 00942 case WIN_KBD_END: 00943 case WIN_KBD_INSERT: 00944 case WIN_KBD_DELETE: 00945 case WIN_KBD_F1: 00946 case WIN_KBD_F2: 00947 case WIN_KBD_F3: 00948 case WIN_KBD_F4: 00949 case WIN_KBD_F5: 00950 case WIN_KBD_F6: 00951 case WIN_KBD_F7: 00952 case WIN_KBD_F8: 00953 case WIN_KBD_F9: 00954 case WIN_KBD_F10: 00955 case WIN_KBD_F11: 00956 case WIN_KBD_F12: 00957 retval = glwsrv.KeyFlag; 00958 glwsrv.WEvents = WIN_NOEVENT; 00959 return TRUE; 00960 00961 case WIN_WHEELUP: 00962 retval = 1; 00963 glwsrv.WEvents = WIN_NOEVENT; 00964 return TRUE; 00965 00966 case WIN_WHEELDOWN: 00967 retval = 1; 00968 glwsrv.WEvents = WIN_NOEVENT; 00969 return TRUE; 00970 00971 case WIN_LEFT: 00972 // retval _must_ be either 1 or 0, nothing else... 00973 retval = (glwsrv.MouseFlags & MK_LBUTTON) != 0; 00974 glwsrv.WEvents = WIN_NOEVENT; 00975 return TRUE; 00976 00977 case WIN_MIDDLE: 00978 // retval _must_ be either 1 or 0, nothing else... 00979 retval = (glwsrv.MouseFlags & MK_MBUTTON) != 0; 00980 glwsrv.WEvents = WIN_NOEVENT; 00981 return TRUE; 00982 00983 case WIN_RIGHT: 00984 // retval _must_ be either 1 or 0, nothing else... 00985 retval = (glwsrv.MouseFlags & MK_RBUTTON) != 0; 00986 glwsrv.WEvents = WIN_NOEVENT; 00987 return TRUE; 00988 } 00989 00990 retval = 0; 00991 glwsrv.WEvents = WIN_NOEVENT; 00992 return FALSE; 00993 } 00994 00995 00996 // 00997 // virtual routines for preparing to draw, drawing, and finishing drawing 00998 // 00999 01000 // reshape the display after a shape change 01001 void OpenGLDisplayDevice::reshape(void) { 01002 // this code assumes that the xSize and ySize variables have 01003 // been updated (magically) already by the time this gets called. 01004 01005 switch (inStereo) { 01006 case OPENGL_STEREO_SIDE: 01007 set_screen_pos(0.5f * (float)xSize / (float)ySize); 01008 break; 01009 01010 case OPENGL_STEREO_ABOVEBELOW: 01011 set_screen_pos(2.0f * (float)xSize / (float)ySize); 01012 break; 01013 01014 case OPENGL_STEREO_STENCIL_CHECKERBOARD: 01015 case OPENGL_STEREO_STENCIL_COLUMNS: 01016 case OPENGL_STEREO_STENCIL_ROWS: 01017 enable_stencil_stereo(inStereo); 01018 set_screen_pos((float)xSize / (float)ySize); 01019 break; 01020 01021 default: 01022 set_screen_pos((float)xSize / (float)ySize); 01023 break; 01024 } 01025 } 01026 01027 unsigned char * OpenGLDisplayDevice::readpixels_rgb3u(int &x, int &y) { 01028 unsigned char * img; 01029 01030 x = xSize; 01031 y = ySize; 01032 01033 if ((img = (unsigned char *) malloc(x * y * 3)) != NULL) { 01034 glPixelStorei(GL_PACK_ALIGNMENT, 1); 01035 glReadPixels(0, 0, x, y, GL_RGB, GL_UNSIGNED_BYTE, img); 01036 } else { 01037 x = 0; 01038 y = 0; 01039 } 01040 01041 return img; 01042 } 01043 01044 01045 unsigned char * OpenGLDisplayDevice::readpixels_rgba4u(int &xs, int &ys) { 01046 unsigned char * img = NULL; 01047 xs = xSize; 01048 ys = ySize; 01049 01050 // fall back to normal glReadPixels() if better methods fail 01051 if ((img = (unsigned char *) malloc(xs * ys * 4)) != NULL) { 01052 glPixelStorei(GL_PACK_ALIGNMENT, 1); 01053 glReadPixels(0, 0, xs, ys, GL_RGBA, GL_UNSIGNED_BYTE, img); 01054 return img; 01055 } 01056 01057 // else bail out 01058 xs = 0; 01059 ys = 0; 01060 return NULL; 01061 } 01062 01063 01064 int OpenGLDisplayDevice::drawpixels_rgba4u(unsigned char *rgba, int &xs, int &ys) { 01065 01066 #if 0 01067 // glDrawBuffer(GL_BACK); 01068 // glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 01069 // glClearColor(0.0, 0.0, 0.0, 1.0); /* black */ 01070 // glClear(GL_COLOR_BUFFER_BIT); 01071 01072 glPushMatrix(); 01073 glDisable(GL_DEPTH_TEST); 01074 01075 glViewport(0, 0, xs, ys); 01076 01077 glShadeModel(GL_FLAT); 01078 glMatrixMode(GL_PROJECTION); 01079 glLoadIdentity(); 01080 glOrtho(0.0, xs, 0.0, ys, -1.0, 1.0); 01081 glMatrixMode(GL_MODELVIEW); 01082 glLoadIdentity(); 01083 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 01084 glPixelZoom(1.0, 1.0); 01085 01086 glRasterPos2i(0, 0); 01087 glDrawPixels(xs, ys, GL_RGBA, GL_UNSIGNED_BYTE, rgba); 01088 01089 glEnable(GL_DEPTH_TEST); 01090 glPopMatrix(); 01091 #elif 1 01092 // glDrawBuffer(GL_BACK); 01093 // glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 01094 // glClearColor(0.0, 0.0, 0.0, 1.0); /* black */ 01095 // glClear(GL_COLOR_BUFFER_BIT); 01096 01097 glPushMatrix(); 01098 glDisable(GL_DEPTH_TEST); 01099 01100 glViewport(0, 0, xs, ys); 01101 01102 glShadeModel(GL_FLAT); 01103 glMatrixMode(GL_PROJECTION); 01104 glLoadIdentity(); 01105 glOrtho(0.0, xs, 0.0, ys, -1.0, 1.0); 01106 glMatrixMode(GL_MODELVIEW); 01107 01108 GLuint texName = 0; 01109 GLfloat texborder[4] = {0.0, 0.0, 0.0, 1.0}; 01110 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 01111 glBindTexture(GL_TEXTURE_2D, texName); 01112 01113 /* black borders if we go rendering anything beyond texture coordinates */ 01114 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, texborder); 01115 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); 01116 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); 01117 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 01118 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 01119 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 01120 01121 glLoadIdentity(); 01122 glColor3f(1.0, 1.0, 1.0); 01123 01124 #if 1 01125 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xs, ys, 0, 01126 GL_RGBA, GL_UNSIGNED_BYTE, rgba); 01127 glEnable(GL_TEXTURE_2D); 01128 #endif 01129 01130 glBegin(GL_QUADS); 01131 glTexCoord2f(0.0f, 0.0f); 01132 glVertex2f(0, 0); 01133 glTexCoord2f(0.0f, 1.0f); 01134 glVertex2f(0, GLfloat(ys)); 01135 glTexCoord2f(1.0f, 1.0f); 01136 glVertex2f(GLfloat(xs), GLfloat(ys)); 01137 glTexCoord2f(1.0f, 0.0f); 01138 glVertex2f(GLfloat(xs), 0.0f); 01139 glEnd(); 01140 01141 #if 1 01142 glDisable(GL_TEXTURE_2D); 01143 #endif 01144 01145 glEnable(GL_DEPTH_TEST); 01146 glPopMatrix(); 01147 #endif 01148 01149 update(); 01150 01151 return 0; 01152 } 01153 01154 01155 01156 01157 // update after drawing 01158 void OpenGLDisplayDevice::update(int do_update) { 01159 glFlush(); 01160 01161 if(do_update) 01162 SwapBuffers(glwsrv.hDC); 01163 01164 glDrawBuffer(GL_BACK); 01165 } 01166 01167 01168 void OpenGLDisplayDevice::set_window_title(char *newtitlestr) { 01169 #if defined(_MSC_VER) 01170 // not yet implemented 01171 #endif 01172 } 01173