win_wndproc.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #pragma hdrstop
  21. #include "../../idlib/precompiled.h"
  22. #include "win_local.h"
  23. #include "../../renderer/tr_local.h"
  24. #include <Windowsx.h>
  25. LONG WINAPI MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
  26. static bool s_alttab_disabled;
  27. extern idCVar r_windowX;
  28. extern idCVar r_windowY;
  29. extern idCVar r_windowWidth;
  30. extern idCVar r_windowHeight;
  31. static void WIN_DisableAltTab() {
  32. if ( s_alttab_disabled || win32.win_allowAltTab.GetBool() ) {
  33. return;
  34. }
  35. if ( !idStr::Icmp( cvarSystem->GetCVarString( "sys_arch" ), "winnt" ) ) {
  36. RegisterHotKey( 0, 0, MOD_ALT, VK_TAB );
  37. } else {
  38. BOOL old;
  39. SystemParametersInfo( SPI_SCREENSAVERRUNNING, 1, &old, 0 );
  40. }
  41. s_alttab_disabled = true;
  42. }
  43. static void WIN_EnableAltTab() {
  44. if ( !s_alttab_disabled || win32.win_allowAltTab.GetBool() ) {
  45. return;
  46. }
  47. if ( !idStr::Icmp( cvarSystem->GetCVarString( "sys_arch" ), "winnt" ) ) {
  48. UnregisterHotKey( 0, 0 );
  49. } else {
  50. BOOL old;
  51. SystemParametersInfo( SPI_SCREENSAVERRUNNING, 0, &old, 0 );
  52. }
  53. s_alttab_disabled = false;
  54. }
  55. void WIN_Sizing(WORD side, RECT *rect)
  56. {
  57. if ( !R_IsInitialized() || renderSystem->GetWidth() <= 0 || renderSystem->GetHeight() <= 0 ) {
  58. return;
  59. }
  60. // restrict to a standard aspect ratio
  61. int width = rect->right - rect->left;
  62. int height = rect->bottom - rect->top;
  63. // Adjust width/height for window decoration
  64. RECT decoRect = { 0, 0, 0, 0 };
  65. AdjustWindowRect( &decoRect, WINDOW_STYLE|WS_SYSMENU, FALSE );
  66. int decoWidth = decoRect.right - decoRect.left;
  67. int decoHeight = decoRect.bottom - decoRect.top;
  68. width -= decoWidth;
  69. height -= decoHeight;
  70. // Clamp to a minimum size
  71. if ( width < SCREEN_WIDTH / 4 ) {
  72. width = SCREEN_WIDTH / 4;
  73. }
  74. if ( height < SCREEN_HEIGHT / 4 ) {
  75. height = SCREEN_HEIGHT / 4;
  76. }
  77. const int minWidth = height * 4 / 3;
  78. const int maxHeight = width * 3 / 4;
  79. const int maxWidth = height * 16 / 9;
  80. const int minHeight = width * 9 / 16;
  81. // Set the new size
  82. switch ( side ) {
  83. case WMSZ_LEFT:
  84. rect->left = rect->right - width - decoWidth;
  85. rect->bottom = rect->top + idMath::ClampInt( minHeight, maxHeight, height ) + decoHeight;
  86. break;
  87. case WMSZ_RIGHT:
  88. rect->right = rect->left + width + decoWidth;
  89. rect->bottom = rect->top + idMath::ClampInt( minHeight, maxHeight, height ) + decoHeight;
  90. break;
  91. case WMSZ_BOTTOM:
  92. case WMSZ_BOTTOMRIGHT:
  93. rect->bottom = rect->top + height + decoHeight;
  94. rect->right = rect->left + idMath::ClampInt( minWidth, maxWidth, width ) + decoWidth;
  95. break;
  96. case WMSZ_TOP:
  97. case WMSZ_TOPRIGHT:
  98. rect->top = rect->bottom - height - decoHeight;
  99. rect->right = rect->left + idMath::ClampInt( minWidth, maxWidth, width ) + decoWidth;
  100. break;
  101. case WMSZ_BOTTOMLEFT:
  102. rect->bottom = rect->top + height + decoHeight;
  103. rect->left = rect->right - idMath::ClampInt( minWidth, maxWidth, width ) - decoWidth;
  104. break;
  105. case WMSZ_TOPLEFT:
  106. rect->top = rect->bottom - height - decoHeight;
  107. rect->left = rect->right - idMath::ClampInt( minWidth, maxWidth, width ) - decoWidth;
  108. break;
  109. }
  110. }
  111. /*
  112. ====================
  113. MainWndProc
  114. main window procedure
  115. ====================
  116. */
  117. LONG WINAPI MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
  118. int key;
  119. switch( uMsg ) {
  120. case WM_WINDOWPOSCHANGED:
  121. if (R_IsInitialized()) {
  122. RECT rect;
  123. if (::GetClientRect(win32.hWnd, &rect)) {
  124. if ( rect.right > rect.left && rect.bottom > rect.top ) {
  125. glConfig.nativeScreenWidth = rect.right - rect.left;
  126. glConfig.nativeScreenHeight = rect.bottom - rect.top;
  127. // save the window size in cvars if we aren't fullscreen
  128. int style = GetWindowLong( hWnd, GWL_STYLE );
  129. if ( ( style & WS_POPUP ) == 0 ) {
  130. r_windowWidth.SetInteger( glConfig.nativeScreenWidth );
  131. r_windowHeight.SetInteger( glConfig.nativeScreenHeight );
  132. }
  133. }
  134. }
  135. }
  136. break;
  137. case WM_MOVE: {
  138. int xPos, yPos;
  139. RECT r;
  140. // save the window origin in cvars if we aren't fullscreen
  141. int style = GetWindowLong( hWnd, GWL_STYLE );
  142. if ( ( style & WS_POPUP ) == 0 ) {
  143. xPos = (short) LOWORD(lParam); // horizontal position
  144. yPos = (short) HIWORD(lParam); // vertical position
  145. r.left = 0;
  146. r.top = 0;
  147. r.right = 1;
  148. r.bottom = 1;
  149. AdjustWindowRect( &r, style, FALSE );
  150. r_windowX.SetInteger( xPos + r.left );
  151. r_windowY.SetInteger( yPos + r.top );
  152. }
  153. break;
  154. }
  155. case WM_CREATE:
  156. win32.hWnd = hWnd;
  157. if ( win32.cdsFullscreen ) {
  158. WIN_DisableAltTab();
  159. } else {
  160. WIN_EnableAltTab();
  161. }
  162. // do the OpenGL setup
  163. void GLW_WM_CREATE( HWND hWnd );
  164. GLW_WM_CREATE( hWnd );
  165. break;
  166. case WM_DESTROY:
  167. // let sound and input know about this?
  168. win32.hWnd = NULL;
  169. if ( win32.cdsFullscreen ) {
  170. WIN_EnableAltTab();
  171. }
  172. break;
  173. case WM_CLOSE:
  174. soundSystem->SetMute( true );
  175. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
  176. break;
  177. case WM_ACTIVATE:
  178. // if we got here because of an alt-tab or maximize,
  179. // we should activate immediately. If we are here because
  180. // the mouse was clicked on a title bar or drag control,
  181. // don't activate until the mouse button is released
  182. {
  183. int fActive, fMinimized;
  184. fActive = LOWORD(wParam);
  185. fMinimized = (BOOL) HIWORD(wParam);
  186. win32.activeApp = (fActive != WA_INACTIVE);
  187. if ( win32.activeApp ) {
  188. idKeyInput::ClearStates();
  189. Sys_GrabMouseCursor( true );
  190. if ( common->IsInitialized() ) {
  191. SetCursor( NULL );
  192. }
  193. }
  194. if ( fActive == WA_INACTIVE ) {
  195. win32.movingWindow = false;
  196. if ( common->IsInitialized() ) {
  197. SetCursor( LoadCursor( 0, IDC_ARROW ) );
  198. }
  199. }
  200. // start playing the game sound world
  201. soundSystem->SetMute( !win32.activeApp );
  202. // we do not actually grab or release the mouse here,
  203. // that will be done next time through the main loop
  204. }
  205. break;
  206. case WM_TIMER: {
  207. if ( win32.win_timerUpdate.GetBool() ) {
  208. common->Frame();
  209. }
  210. break;
  211. }
  212. case WM_SYSCOMMAND:
  213. if ( wParam == SC_SCREENSAVE || wParam == SC_KEYMENU ) {
  214. return 0;
  215. }
  216. break;
  217. case WM_SYSKEYDOWN:
  218. if ( wParam == 13 ) { // alt-enter toggles full-screen
  219. cvarSystem->SetCVarBool( "r_fullscreen", !renderSystem->IsFullScreen() );
  220. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "vid_restart\n" );
  221. return 0;
  222. }
  223. // fall through for other keys
  224. case WM_KEYDOWN:
  225. key = ( ( lParam >> 16 ) & 0xFF ) | ( ( ( lParam >> 24 ) & 1 ) << 7 );
  226. if ( key == K_LCTRL || key == K_LALT || key == K_RCTRL || key == K_RALT ) {
  227. // let direct-input handle this because windows sends Alt-Gr
  228. // as two events (ctrl then alt)
  229. break;
  230. }
  231. // D
  232. if ( key == K_NUMLOCK ) {
  233. key = K_PAUSE;
  234. } else if ( key == K_PAUSE ) {
  235. key = K_NUMLOCK;
  236. }
  237. Sys_QueEvent( SE_KEY, key, true, 0, NULL, 0 );
  238. break;
  239. case WM_SYSKEYUP:
  240. case WM_KEYUP:
  241. key = ( ( lParam >> 16 ) & 0xFF ) | ( ( ( lParam >> 24 ) & 1 ) << 7 );
  242. if ( key == K_PRINTSCREEN ) {
  243. // don't queue printscreen keys. Since windows doesn't send us key
  244. // down events for this, we handle queueing them with DirectInput
  245. break;
  246. } else if ( key == K_LCTRL || key == K_LALT || key == K_RCTRL || key == K_RALT ) {
  247. // let direct-input handle this because windows sends Alt-Gr
  248. // as two events (ctrl then alt)
  249. break;
  250. }
  251. Sys_QueEvent( SE_KEY, key, false, 0, NULL, 0 );
  252. break;
  253. case WM_CHAR:
  254. Sys_QueEvent( SE_CHAR, wParam, 0, 0, NULL, 0 );
  255. break;
  256. case WM_NCLBUTTONDOWN:
  257. // win32.movingWindow = true;
  258. break;
  259. case WM_ENTERSIZEMOVE:
  260. win32.movingWindow = true;
  261. break;
  262. case WM_EXITSIZEMOVE:
  263. win32.movingWindow = false;
  264. break;
  265. case WM_SIZING:
  266. WIN_Sizing(wParam, (RECT *)lParam);
  267. break;
  268. case WM_MOUSEMOVE: {
  269. if ( !common->IsInitialized() ) {
  270. break;
  271. }
  272. const bool isShellActive = ( game && ( game->Shell_IsActive() || game->IsPDAOpen() ) );
  273. const bool isConsoleActive = console->Active();
  274. if ( win32.activeApp ) {
  275. if ( isShellActive ) {
  276. // If the shell is active, it will display its own cursor.
  277. SetCursor( NULL );
  278. } else if ( isConsoleActive ) {
  279. // The console is active but the shell is not.
  280. // Show the Windows cursor.
  281. SetCursor( LoadCursor( 0, IDC_ARROW ) );
  282. } else {
  283. // The shell not active and neither is the console.
  284. // This is normal gameplay, hide the cursor.
  285. SetCursor( NULL );
  286. }
  287. } else {
  288. if ( !isShellActive ) {
  289. // Always show the cursor when the window is in the background
  290. SetCursor( LoadCursor( 0, IDC_ARROW ) );
  291. } else {
  292. SetCursor( NULL );
  293. }
  294. }
  295. const int x = GET_X_LPARAM( lParam );
  296. const int y = GET_Y_LPARAM( lParam );
  297. // Generate an event
  298. Sys_QueEvent( SE_MOUSE_ABSOLUTE, x, y, 0, NULL, 0 );
  299. // Get a mouse leave message
  300. TRACKMOUSEEVENT tme = {
  301. sizeof( TRACKMOUSEEVENT ),
  302. TME_LEAVE,
  303. hWnd,
  304. 0
  305. };
  306. TrackMouseEvent( &tme );
  307. return 0;
  308. }
  309. case WM_MOUSELEAVE: {
  310. Sys_QueEvent( SE_MOUSE_LEAVE, 0, 0, 0, NULL, 0 );
  311. return 0;
  312. }
  313. case WM_LBUTTONDOWN: {
  314. Sys_QueEvent( SE_KEY, K_MOUSE1, 1, 0, NULL, 0 );
  315. return 0;
  316. }
  317. case WM_LBUTTONUP: {
  318. Sys_QueEvent( SE_KEY, K_MOUSE1, 0, 0, NULL, 0 );
  319. return 0;
  320. }
  321. case WM_RBUTTONDOWN: {
  322. Sys_QueEvent( SE_KEY, K_MOUSE2, 1, 0, NULL, 0 );
  323. return 0;
  324. }
  325. case WM_RBUTTONUP: {
  326. Sys_QueEvent( SE_KEY, K_MOUSE2, 0, 0, NULL, 0 );
  327. return 0;
  328. }
  329. case WM_MBUTTONDOWN: {
  330. Sys_QueEvent( SE_KEY, K_MOUSE3, 1, 0, NULL, 0 );
  331. return 0;
  332. }
  333. case WM_MBUTTONUP: {
  334. Sys_QueEvent( SE_KEY, K_MOUSE3, 0, 0, NULL, 0 );
  335. return 0;
  336. }
  337. case WM_XBUTTONDOWN: {
  338. int button = GET_XBUTTON_WPARAM( wParam );
  339. if ( button == 1 ) {
  340. Sys_QueEvent( SE_KEY, K_MOUSE4, 1, 0, NULL, 0 );
  341. } else if ( button == 2 ) {
  342. Sys_QueEvent( SE_KEY, K_MOUSE5, 1, 0, NULL, 0 );
  343. }
  344. return 0;
  345. }
  346. case WM_XBUTTONUP: {
  347. int button = GET_XBUTTON_WPARAM( wParam );
  348. if ( button == 1 ) {
  349. Sys_QueEvent( SE_KEY, K_MOUSE4, 0, 0, NULL, 0 );
  350. } else if ( button == 2 ) {
  351. Sys_QueEvent( SE_KEY, K_MOUSE5, 0, 0, NULL, 0 );
  352. }
  353. return 0;
  354. }
  355. case WM_MOUSEWHEEL: {
  356. int delta = GET_WHEEL_DELTA_WPARAM( wParam ) / WHEEL_DELTA;
  357. int key = delta < 0 ? K_MWHEELDOWN : K_MWHEELUP;
  358. delta = abs( delta );
  359. while( delta-- > 0 ) {
  360. Sys_QueEvent( SE_KEY, key, true, 0, NULL, 0 );
  361. Sys_QueEvent( SE_KEY, key, false, 0, NULL, 0 );
  362. }
  363. break;
  364. }
  365. }
  366. return DefWindowProc( hWnd, uMsg, wParam, lParam );
  367. }