glw_imp.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3. This program is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU General Public License
  5. as published by the Free Software Foundation; either version 2
  6. of the License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. See the GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  14. */
  15. /*
  16. ** GLW_IMP.C
  17. **
  18. ** This file contains ALL Win32 specific stuff having to do with the
  19. ** OpenGL refresh. When a port is being made the following functions
  20. ** must be implemented by the port:
  21. **
  22. ** GLimp_EndFrame
  23. ** GLimp_Init
  24. ** GLimp_Shutdown
  25. ** GLimp_SwitchFullscreen
  26. **
  27. */
  28. #include <assert.h>
  29. #include <windows.h>
  30. #include "../ref_gl/gl_local.h"
  31. #include "glw_win.h"
  32. #include "winquake.h"
  33. static qboolean GLimp_SwitchFullscreen( int width, int height );
  34. qboolean GLimp_InitGL (void);
  35. glwstate_t glw_state;
  36. extern cvar_t *vid_fullscreen;
  37. extern cvar_t *vid_ref;
  38. static qboolean VerifyDriver( void )
  39. {
  40. char buffer[1024];
  41. strcpy( buffer, qglGetString( GL_RENDERER ) );
  42. strlwr( buffer );
  43. if ( strcmp( buffer, "gdi generic" ) == 0 )
  44. if ( !glw_state.mcd_accelerated )
  45. return false;
  46. return true;
  47. }
  48. /*
  49. ** VID_CreateWindow
  50. */
  51. #define WINDOW_CLASS_NAME "Quake 2"
  52. qboolean VID_CreateWindow( int width, int height, qboolean fullscreen )
  53. {
  54. WNDCLASS wc;
  55. RECT r;
  56. cvar_t *vid_xpos, *vid_ypos;
  57. int stylebits;
  58. int x, y, w, h;
  59. int exstyle;
  60. /* Register the frame class */
  61. wc.style = 0;
  62. wc.lpfnWndProc = (WNDPROC)glw_state.wndproc;
  63. wc.cbClsExtra = 0;
  64. wc.cbWndExtra = 0;
  65. wc.hInstance = glw_state.hInstance;
  66. wc.hIcon = 0;
  67. wc.hCursor = LoadCursor (NULL,IDC_ARROW);
  68. wc.hbrBackground = (void *)COLOR_GRAYTEXT;
  69. wc.lpszMenuName = 0;
  70. wc.lpszClassName = WINDOW_CLASS_NAME;
  71. if (!RegisterClass (&wc) )
  72. ri.Sys_Error (ERR_FATAL, "Couldn't register window class");
  73. if (fullscreen)
  74. {
  75. exstyle = WS_EX_TOPMOST;
  76. stylebits = WS_POPUP|WS_VISIBLE;
  77. }
  78. else
  79. {
  80. exstyle = 0;
  81. stylebits = WINDOW_STYLE;
  82. }
  83. r.left = 0;
  84. r.top = 0;
  85. r.right = width;
  86. r.bottom = height;
  87. AdjustWindowRect (&r, stylebits, FALSE);
  88. w = r.right - r.left;
  89. h = r.bottom - r.top;
  90. if (fullscreen)
  91. {
  92. x = 0;
  93. y = 0;
  94. }
  95. else
  96. {
  97. vid_xpos = ri.Cvar_Get ("vid_xpos", "0", 0);
  98. vid_ypos = ri.Cvar_Get ("vid_ypos", "0", 0);
  99. x = vid_xpos->value;
  100. y = vid_ypos->value;
  101. }
  102. glw_state.hWnd = CreateWindowEx (
  103. exstyle,
  104. WINDOW_CLASS_NAME,
  105. "Quake 2",
  106. stylebits,
  107. x, y, w, h,
  108. NULL,
  109. NULL,
  110. glw_state.hInstance,
  111. NULL);
  112. if (!glw_state.hWnd)
  113. ri.Sys_Error (ERR_FATAL, "Couldn't create window");
  114. ShowWindow( glw_state.hWnd, SW_SHOW );
  115. UpdateWindow( glw_state.hWnd );
  116. // init all the gl stuff for the window
  117. if (!GLimp_InitGL ())
  118. {
  119. ri.Con_Printf( PRINT_ALL, "VID_CreateWindow() - GLimp_InitGL failed\n");
  120. return false;
  121. }
  122. SetForegroundWindow( glw_state.hWnd );
  123. SetFocus( glw_state.hWnd );
  124. // let the sound and input subsystems know about the new window
  125. ri.Vid_NewWindow (width, height);
  126. return true;
  127. }
  128. /*
  129. ** GLimp_SetMode
  130. */
  131. rserr_t GLimp_SetMode( int *pwidth, int *pheight, int mode, qboolean fullscreen )
  132. {
  133. int width, height;
  134. const char *win_fs[] = { "W", "FS" };
  135. ri.Con_Printf( PRINT_ALL, "Initializing OpenGL display\n");
  136. ri.Con_Printf (PRINT_ALL, "...setting mode %d:", mode );
  137. if ( !ri.Vid_GetModeInfo( &width, &height, mode ) )
  138. {
  139. ri.Con_Printf( PRINT_ALL, " invalid mode\n" );
  140. return rserr_invalid_mode;
  141. }
  142. ri.Con_Printf( PRINT_ALL, " %d %d %s\n", width, height, win_fs[fullscreen] );
  143. // destroy the existing window
  144. if (glw_state.hWnd)
  145. {
  146. GLimp_Shutdown ();
  147. }
  148. // do a CDS if needed
  149. if ( fullscreen )
  150. {
  151. DEVMODE dm;
  152. ri.Con_Printf( PRINT_ALL, "...attempting fullscreen\n" );
  153. memset( &dm, 0, sizeof( dm ) );
  154. dm.dmSize = sizeof( dm );
  155. dm.dmPelsWidth = width;
  156. dm.dmPelsHeight = height;
  157. dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
  158. if ( gl_bitdepth->value != 0 )
  159. {
  160. dm.dmBitsPerPel = gl_bitdepth->value;
  161. dm.dmFields |= DM_BITSPERPEL;
  162. ri.Con_Printf( PRINT_ALL, "...using gl_bitdepth of %d\n", ( int ) gl_bitdepth->value );
  163. }
  164. else
  165. {
  166. HDC hdc = GetDC( NULL );
  167. int bitspixel = GetDeviceCaps( hdc, BITSPIXEL );
  168. ri.Con_Printf( PRINT_ALL, "...using desktop display depth of %d\n", bitspixel );
  169. ReleaseDC( 0, hdc );
  170. }
  171. ri.Con_Printf( PRINT_ALL, "...calling CDS: " );
  172. if ( ChangeDisplaySettings( &dm, CDS_FULLSCREEN ) == DISP_CHANGE_SUCCESSFUL )
  173. {
  174. *pwidth = width;
  175. *pheight = height;
  176. gl_state.fullscreen = true;
  177. ri.Con_Printf( PRINT_ALL, "ok\n" );
  178. if ( !VID_CreateWindow (width, height, true) )
  179. return rserr_invalid_mode;
  180. return rserr_ok;
  181. }
  182. else
  183. {
  184. *pwidth = width;
  185. *pheight = height;
  186. ri.Con_Printf( PRINT_ALL, "failed\n" );
  187. ri.Con_Printf( PRINT_ALL, "...calling CDS assuming dual monitors:" );
  188. dm.dmPelsWidth = width * 2;
  189. dm.dmPelsHeight = height;
  190. dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
  191. if ( gl_bitdepth->value != 0 )
  192. {
  193. dm.dmBitsPerPel = gl_bitdepth->value;
  194. dm.dmFields |= DM_BITSPERPEL;
  195. }
  196. /*
  197. ** our first CDS failed, so maybe we're running on some weird dual monitor
  198. ** system
  199. */
  200. if ( ChangeDisplaySettings( &dm, CDS_FULLSCREEN ) != DISP_CHANGE_SUCCESSFUL )
  201. {
  202. ri.Con_Printf( PRINT_ALL, " failed\n" );
  203. ri.Con_Printf( PRINT_ALL, "...setting windowed mode\n" );
  204. ChangeDisplaySettings( 0, 0 );
  205. *pwidth = width;
  206. *pheight = height;
  207. gl_state.fullscreen = false;
  208. if ( !VID_CreateWindow (width, height, false) )
  209. return rserr_invalid_mode;
  210. return rserr_invalid_fullscreen;
  211. }
  212. else
  213. {
  214. ri.Con_Printf( PRINT_ALL, " ok\n" );
  215. if ( !VID_CreateWindow (width, height, true) )
  216. return rserr_invalid_mode;
  217. gl_state.fullscreen = true;
  218. return rserr_ok;
  219. }
  220. }
  221. }
  222. else
  223. {
  224. ri.Con_Printf( PRINT_ALL, "...setting windowed mode\n" );
  225. ChangeDisplaySettings( 0, 0 );
  226. *pwidth = width;
  227. *pheight = height;
  228. gl_state.fullscreen = false;
  229. if ( !VID_CreateWindow (width, height, false) )
  230. return rserr_invalid_mode;
  231. }
  232. return rserr_ok;
  233. }
  234. /*
  235. ** GLimp_Shutdown
  236. **
  237. ** This routine does all OS specific shutdown procedures for the OpenGL
  238. ** subsystem. Under OpenGL this means NULLing out the current DC and
  239. ** HGLRC, deleting the rendering context, and releasing the DC acquired
  240. ** for the window. The state structure is also nulled out.
  241. **
  242. */
  243. void GLimp_Shutdown( void )
  244. {
  245. if ( qwglMakeCurrent && !qwglMakeCurrent( NULL, NULL ) )
  246. ri.Con_Printf( PRINT_ALL, "ref_gl::R_Shutdown() - wglMakeCurrent failed\n");
  247. if ( glw_state.hGLRC )
  248. {
  249. if ( qwglDeleteContext && !qwglDeleteContext( glw_state.hGLRC ) )
  250. ri.Con_Printf( PRINT_ALL, "ref_gl::R_Shutdown() - wglDeleteContext failed\n");
  251. glw_state.hGLRC = NULL;
  252. }
  253. if (glw_state.hDC)
  254. {
  255. if ( !ReleaseDC( glw_state.hWnd, glw_state.hDC ) )
  256. ri.Con_Printf( PRINT_ALL, "ref_gl::R_Shutdown() - ReleaseDC failed\n" );
  257. glw_state.hDC = NULL;
  258. }
  259. if (glw_state.hWnd)
  260. {
  261. DestroyWindow ( glw_state.hWnd );
  262. glw_state.hWnd = NULL;
  263. }
  264. if ( glw_state.log_fp )
  265. {
  266. fclose( glw_state.log_fp );
  267. glw_state.log_fp = 0;
  268. }
  269. UnregisterClass (WINDOW_CLASS_NAME, glw_state.hInstance);
  270. if ( gl_state.fullscreen )
  271. {
  272. ChangeDisplaySettings( 0, 0 );
  273. gl_state.fullscreen = false;
  274. }
  275. }
  276. /*
  277. ** GLimp_Init
  278. **
  279. ** This routine is responsible for initializing the OS specific portions
  280. ** of OpenGL. Under Win32 this means dealing with the pixelformats and
  281. ** doing the wgl interface stuff.
  282. */
  283. qboolean GLimp_Init( void *hinstance, void *wndproc )
  284. {
  285. #define OSR2_BUILD_NUMBER 1111
  286. OSVERSIONINFO vinfo;
  287. vinfo.dwOSVersionInfoSize = sizeof(vinfo);
  288. glw_state.allowdisplaydepthchange = false;
  289. if ( GetVersionEx( &vinfo) )
  290. {
  291. if ( vinfo.dwMajorVersion > 4 )
  292. {
  293. glw_state.allowdisplaydepthchange = true;
  294. }
  295. else if ( vinfo.dwMajorVersion == 4 )
  296. {
  297. if ( vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT )
  298. {
  299. glw_state.allowdisplaydepthchange = true;
  300. }
  301. else if ( vinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
  302. {
  303. if ( LOWORD( vinfo.dwBuildNumber ) >= OSR2_BUILD_NUMBER )
  304. {
  305. glw_state.allowdisplaydepthchange = true;
  306. }
  307. }
  308. }
  309. }
  310. else
  311. {
  312. ri.Con_Printf( PRINT_ALL, "GLimp_Init() - GetVersionEx failed\n" );
  313. return false;
  314. }
  315. glw_state.hInstance = ( HINSTANCE ) hinstance;
  316. glw_state.wndproc = wndproc;
  317. return true;
  318. }
  319. qboolean GLimp_InitGL (void)
  320. {
  321. PIXELFORMATDESCRIPTOR pfd =
  322. {
  323. sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
  324. 1, // version number
  325. PFD_DRAW_TO_WINDOW | // support window
  326. PFD_SUPPORT_OPENGL | // support OpenGL
  327. PFD_DOUBLEBUFFER, // double buffered
  328. PFD_TYPE_RGBA, // RGBA type
  329. 24, // 24-bit color depth
  330. 0, 0, 0, 0, 0, 0, // color bits ignored
  331. 0, // no alpha buffer
  332. 0, // shift bit ignored
  333. 0, // no accumulation buffer
  334. 0, 0, 0, 0, // accum bits ignored
  335. 32, // 32-bit z-buffer
  336. 0, // no stencil buffer
  337. 0, // no auxiliary buffer
  338. PFD_MAIN_PLANE, // main layer
  339. 0, // reserved
  340. 0, 0, 0 // layer masks ignored
  341. };
  342. int pixelformat;
  343. cvar_t *stereo;
  344. stereo = ri.Cvar_Get( "cl_stereo", "0", 0 );
  345. /*
  346. ** set PFD_STEREO if necessary
  347. */
  348. if ( stereo->value != 0 )
  349. {
  350. ri.Con_Printf( PRINT_ALL, "...attempting to use stereo\n" );
  351. pfd.dwFlags |= PFD_STEREO;
  352. gl_state.stereo_enabled = true;
  353. }
  354. else
  355. {
  356. gl_state.stereo_enabled = false;
  357. }
  358. /*
  359. ** figure out if we're running on a minidriver or not
  360. */
  361. if ( strstr( gl_driver->string, "opengl32" ) != 0 )
  362. glw_state.minidriver = false;
  363. else
  364. glw_state.minidriver = true;
  365. /*
  366. ** Get a DC for the specified window
  367. */
  368. if ( glw_state.hDC != NULL )
  369. ri.Con_Printf( PRINT_ALL, "GLimp_Init() - non-NULL DC exists\n" );
  370. if ( ( glw_state.hDC = GetDC( glw_state.hWnd ) ) == NULL )
  371. {
  372. ri.Con_Printf( PRINT_ALL, "GLimp_Init() - GetDC failed\n" );
  373. return false;
  374. }
  375. if ( glw_state.minidriver )
  376. {
  377. if ( (pixelformat = qwglChoosePixelFormat( glw_state.hDC, &pfd)) == 0 )
  378. {
  379. ri.Con_Printf (PRINT_ALL, "GLimp_Init() - qwglChoosePixelFormat failed\n");
  380. return false;
  381. }
  382. if ( qwglSetPixelFormat( glw_state.hDC, pixelformat, &pfd) == FALSE )
  383. {
  384. ri.Con_Printf (PRINT_ALL, "GLimp_Init() - qwglSetPixelFormat failed\n");
  385. return false;
  386. }
  387. qwglDescribePixelFormat( glw_state.hDC, pixelformat, sizeof( pfd ), &pfd );
  388. }
  389. else
  390. {
  391. if ( ( pixelformat = ChoosePixelFormat( glw_state.hDC, &pfd)) == 0 )
  392. {
  393. ri.Con_Printf (PRINT_ALL, "GLimp_Init() - ChoosePixelFormat failed\n");
  394. return false;
  395. }
  396. if ( SetPixelFormat( glw_state.hDC, pixelformat, &pfd) == FALSE )
  397. {
  398. ri.Con_Printf (PRINT_ALL, "GLimp_Init() - SetPixelFormat failed\n");
  399. return false;
  400. }
  401. DescribePixelFormat( glw_state.hDC, pixelformat, sizeof( pfd ), &pfd );
  402. if ( !( pfd.dwFlags & PFD_GENERIC_ACCELERATED ) )
  403. {
  404. extern cvar_t *gl_allow_software;
  405. if ( gl_allow_software->value )
  406. glw_state.mcd_accelerated = true;
  407. else
  408. glw_state.mcd_accelerated = false;
  409. }
  410. else
  411. {
  412. glw_state.mcd_accelerated = true;
  413. }
  414. }
  415. /*
  416. ** report if stereo is desired but unavailable
  417. */
  418. if ( !( pfd.dwFlags & PFD_STEREO ) && ( stereo->value != 0 ) )
  419. {
  420. ri.Con_Printf( PRINT_ALL, "...failed to select stereo pixel format\n" );
  421. ri.Cvar_SetValue( "cl_stereo", 0 );
  422. gl_state.stereo_enabled = false;
  423. }
  424. /*
  425. ** startup the OpenGL subsystem by creating a context and making
  426. ** it current
  427. */
  428. if ( ( glw_state.hGLRC = qwglCreateContext( glw_state.hDC ) ) == 0 )
  429. {
  430. ri.Con_Printf (PRINT_ALL, "GLimp_Init() - qwglCreateContext failed\n");
  431. goto fail;
  432. }
  433. if ( !qwglMakeCurrent( glw_state.hDC, glw_state.hGLRC ) )
  434. {
  435. ri.Con_Printf (PRINT_ALL, "GLimp_Init() - qwglMakeCurrent failed\n");
  436. goto fail;
  437. }
  438. if ( !VerifyDriver() )
  439. {
  440. ri.Con_Printf( PRINT_ALL, "GLimp_Init() - no hardware acceleration detected\n" );
  441. goto fail;
  442. }
  443. /*
  444. ** print out PFD specifics
  445. */
  446. ri.Con_Printf( PRINT_ALL, "GL PFD: color(%d-bits) Z(%d-bit)\n", ( int ) pfd.cColorBits, ( int ) pfd.cDepthBits );
  447. return true;
  448. fail:
  449. if ( glw_state.hGLRC )
  450. {
  451. qwglDeleteContext( glw_state.hGLRC );
  452. glw_state.hGLRC = NULL;
  453. }
  454. if ( glw_state.hDC )
  455. {
  456. ReleaseDC( glw_state.hWnd, glw_state.hDC );
  457. glw_state.hDC = NULL;
  458. }
  459. return false;
  460. }
  461. /*
  462. ** GLimp_BeginFrame
  463. */
  464. void GLimp_BeginFrame( float camera_separation )
  465. {
  466. if ( gl_bitdepth->modified )
  467. {
  468. if ( gl_bitdepth->value != 0 && !glw_state.allowdisplaydepthchange )
  469. {
  470. ri.Cvar_SetValue( "gl_bitdepth", 0 );
  471. ri.Con_Printf( PRINT_ALL, "gl_bitdepth requires Win95 OSR2.x or WinNT 4.x\n" );
  472. }
  473. gl_bitdepth->modified = false;
  474. }
  475. if ( camera_separation < 0 && gl_state.stereo_enabled )
  476. {
  477. qglDrawBuffer( GL_BACK_LEFT );
  478. }
  479. else if ( camera_separation > 0 && gl_state.stereo_enabled )
  480. {
  481. qglDrawBuffer( GL_BACK_RIGHT );
  482. }
  483. else
  484. {
  485. qglDrawBuffer( GL_BACK );
  486. }
  487. }
  488. /*
  489. ** GLimp_EndFrame
  490. **
  491. ** Responsible for doing a swapbuffers and possibly for other stuff
  492. ** as yet to be determined. Probably better not to make this a GLimp
  493. ** function and instead do a call to GLimp_SwapBuffers.
  494. */
  495. void GLimp_EndFrame (void)
  496. {
  497. int err;
  498. err = qglGetError();
  499. assert( err == GL_NO_ERROR );
  500. if ( stricmp( gl_drawbuffer->string, "GL_BACK" ) == 0 )
  501. {
  502. if ( !qwglSwapBuffers( glw_state.hDC ) )
  503. ri.Sys_Error( ERR_FATAL, "GLimp_EndFrame() - SwapBuffers() failed!\n" );
  504. }
  505. }
  506. /*
  507. ** GLimp_AppActivate
  508. */
  509. void GLimp_AppActivate( qboolean active )
  510. {
  511. if ( active )
  512. {
  513. SetForegroundWindow( glw_state.hWnd );
  514. ShowWindow( glw_state.hWnd, SW_RESTORE );
  515. }
  516. else
  517. {
  518. if ( vid_fullscreen->value )
  519. ShowWindow( glw_state.hWnd, SW_MINIMIZE );
  520. }
  521. }