winWindow.cc 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platformWin32/platformWin32.h"
  23. #include "platform/platform.h"
  24. #include "platformWin32/winWindow.h"
  25. #include "platformWin32/platformGL.h"
  26. #include "platform/platformVideo.h"
  27. #include "platformWin32/winOGLVideo.h"
  28. #include "platform/event.h"
  29. #include "console/console.h"
  30. #include "platformWin32/winConsole.h"
  31. #include "platformWin32/winDirectInput.h"
  32. #include "game/gameInterface.h"
  33. #include "math/mRandom.h"
  34. #include "io/fileStream.h"
  35. #include "game/resource.h"
  36. #include "string/unicode.h"
  37. #include "gui/guiCanvas.h"
  38. #include "platform/menus/popupMenu.h"
  39. #include "debug/profiler.h"
  40. //-------------------------------------- Resource Includes
  41. #include "graphics/gBitmap.h"
  42. #include <sys/stat.h>
  43. extern void createFontInit();
  44. extern void createFontShutdown();
  45. #ifdef UNICODE
  46. static const UTF16 *windowClassName = L"Darkstar Window Class";
  47. static UTF16 windowName[256] = L"Darkstar Window";
  48. #else
  49. static const char *windowClassName = "Darkstar Window Class";
  50. static char windowName[256] = "Darkstar Window";
  51. #endif
  52. static bool gWindowCreated = false;
  53. static bool windowNotActive = false;
  54. static RandomLCG sgPlatRandom;
  55. static bool sgQueueEvents;
  56. // is keyboard input a standard (non-changing) VK keycode
  57. #define dIsStandardVK(c) (((0x08 <= (c)) && ((c) <= 0x12)) || \
  58. ((c) == 0x1b) || \
  59. ((0x20 <= (c)) && ((c) <= 0x2e)) || \
  60. ((0x30 <= (c)) && ((c) <= 0x39)) || \
  61. ((0x41 <= (c)) && ((c) <= 0x5a)) || \
  62. ((0x70 <= (c)) && ((c) <= 0x7B)))
  63. extern U16 DIK_to_Key( U8 dikCode );
  64. Win32PlatState winState;
  65. //-----------------------------------------------------------------------------------------------------------------------------------------------------------
  66. //
  67. // Microsoft Layer for Unicode
  68. // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mslu/winprog/compiling_your_application_with_the_microsoft_layer_for_unicode.asp
  69. //
  70. //-----------------------------------------------------------------------------------------------------------------------------------------------------------
  71. #ifdef UNICODE
  72. HMODULE LoadUnicowsProc(void)
  73. {
  74. return(LoadLibraryA("unicows.dll"));
  75. }
  76. #ifdef _cplusplus
  77. extern "C" {
  78. #endif
  79. extern FARPROC _PfnLoadUnicows = (FARPROC) &LoadUnicowsProc;
  80. #ifdef _cplusplus
  81. }
  82. #endif
  83. #endif
  84. //--------------------------------------
  85. Win32PlatState::Win32PlatState()
  86. {
  87. log_fp = NULL;
  88. hinstOpenGL = NULL;
  89. hinstGLU = NULL;
  90. hinstOpenAL = NULL;
  91. appWindow = NULL;
  92. appDC = NULL;
  93. appInstance = NULL;
  94. currentTime = 0;
  95. processId = 0;
  96. appMenu = NULL;
  97. nMessagesPerFrame = 10;
  98. }
  99. static bool windowLocked = false;
  100. static BYTE keyboardState[256];
  101. static bool mouseButtonState[3];
  102. static bool capsLockDown = false;
  103. static S32 modifierKeys = 0;
  104. static bool windowActive = true;
  105. static Point2I lastCursorPos(0,0);
  106. static Point2I windowSize;
  107. static bool sgDoubleByteEnabled = false;
  108. //--------------------------------------
  109. static const char *getMessageName(S32 msg)
  110. {
  111. switch(msg)
  112. {
  113. case WM_KEYDOWN:
  114. return "WM_KEYDOWN";
  115. case WM_KEYUP:
  116. return "WM_KEYUP";
  117. case WM_SYSKEYUP:
  118. return "WM_SYSKEYUP";
  119. case WM_SYSKEYDOWN:
  120. return "WM_SYSKEYDOWN";
  121. default:
  122. return "Unknown!!";
  123. }
  124. }
  125. void Platform::restartInstance()
  126. {
  127. if( Game->isRunning() )
  128. {
  129. //Con::errorf( "Error restarting instance, Game is still running!");
  130. return;
  131. }
  132. STARTUPINFO si;
  133. PROCESS_INFORMATION pi;
  134. ZeroMemory( &si, sizeof(si) );
  135. si.cb = sizeof(si);
  136. ZeroMemory( &pi, sizeof(pi) );
  137. char cen_buf[2048];
  138. GetModuleFileNameA( NULL, cen_buf, 2047);
  139. #ifdef UNICODE
  140. UTF16 b[512];
  141. convertUTF8toUTF16((UTF8 *)cen_buf, b, sizeof(b));
  142. #else
  143. const char* b = cen_buf;
  144. #endif
  145. // Start the child process.
  146. if( CreateProcess( b,
  147. NULL, // Command line
  148. NULL, // Process handle not inheritable
  149. NULL, // Thread handle not inheritable
  150. FALSE, // Set handle inheritance to FALSE
  151. 0, // No creation flags
  152. NULL, // Use parent's environment block
  153. NULL, // Use parent's starting directory
  154. &si, // Pointer to STARTUPINFO structure
  155. &pi ) // Pointer to PROCESS_INFORMATION structure
  156. != false )
  157. {
  158. WaitForInputIdle( pi.hProcess, 5000 );
  159. CloseHandle( pi.hProcess );
  160. CloseHandle( pi.hThread );
  161. }
  162. }
  163. //--------------------------------------
  164. void Platform::AlertOK(const char *windowTitle, const char *message)
  165. {
  166. ShowCursor(true);
  167. #ifdef UNICODE
  168. UTF16 m[1024], t[512];
  169. convertUTF8toUTF16((UTF8 *)windowTitle, t, sizeof(t));
  170. convertUTF8toUTF16((UTF8 *)message, m, sizeof(m));
  171. MessageBox(winState.appWindow, m, t, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OK);
  172. #else
  173. MessageBox(winState.appWindow, message, windowTitle, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OK);
  174. #endif
  175. }
  176. //--------------------------------------
  177. bool Platform::AlertOKCancel(const char *windowTitle, const char *message)
  178. {
  179. ShowCursor(true);
  180. #ifdef UNICODE
  181. UTF16 m[1024], t[512];
  182. convertUTF8toUTF16((UTF8 *)windowTitle, t, sizeof(t));
  183. convertUTF8toUTF16((UTF8 *)message, m, sizeof(m));
  184. return MessageBox(winState.appWindow, m, t, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OKCANCEL) == IDOK;
  185. #else
  186. return MessageBox(winState.appWindow, message, windowTitle, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OKCANCEL) == IDOK;
  187. #endif
  188. }
  189. //--------------------------------------
  190. bool Platform::AlertRetry(const char *windowTitle, const char *message)
  191. {
  192. ShowCursor(true);
  193. #ifdef UNICODE
  194. UTF16 m[1024], t[512];
  195. convertUTF8toUTF16((UTF8 *)windowTitle, t, sizeof(t));
  196. convertUTF8toUTF16((UTF8 *)message, m, sizeof(m));
  197. return (MessageBox(winState.appWindow, m, t, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_RETRYCANCEL) == IDRETRY);
  198. #else
  199. return (MessageBox(winState.appWindow, message, windowTitle, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_RETRYCANCEL) == IDRETRY);
  200. #endif
  201. }
  202. //Luma: YesNo alert message
  203. bool Platform::AlertYesNo(const char *windowTitle, const char *message)
  204. {
  205. ShowCursor(true);
  206. #ifdef UNICODE
  207. UTF16 m[1024], t[512];
  208. convertUTF8toUTF16((UTF8 *)windowTitle, t, sizeof(t));
  209. convertUTF8toUTF16((UTF8 *)message, m, sizeof(m));
  210. return MessageBox(winState.appWindow, m, t, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_YESNO) == IDYES;
  211. #else
  212. return MessageBox(winState.appWindow, message, windowTitle, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_YESNO) == IDYES;
  213. #endif
  214. }
  215. //--------------------------------------
  216. HIMC gIMEContext;
  217. static void InitInput()
  218. {
  219. #ifndef TORQUE_LIB
  220. #ifdef UNICODE
  221. gIMEContext = ImmGetContext(winState.appWindow);
  222. ImmReleaseContext( winState.appWindow, gIMEContext );
  223. #endif
  224. #endif
  225. dMemset( keyboardState, 0, 256 );
  226. dMemset( mouseButtonState, 0, sizeof( mouseButtonState ) );
  227. capsLockDown = (GetKeyState(VK_CAPITAL) & 0x01);
  228. if (capsLockDown)
  229. {
  230. keyboardState[VK_CAPITAL] |= 0x01;
  231. }
  232. }
  233. //--------------------------------------
  234. static void setMouseClipping()
  235. {
  236. ClipCursor(NULL);
  237. if(windowActive)
  238. {
  239. RECT r;
  240. GetWindowRect(winState.appWindow, &r);
  241. if(windowLocked)
  242. {
  243. POINT p;
  244. GetCursorPos(&p);
  245. lastCursorPos.set(p.x - r.left, p.y - r.top);
  246. ClipCursor(&r);
  247. S32 centerX = (r.right + r.left) >> 1;
  248. S32 centerY = (r.bottom + r.top) >> 1;
  249. if(!windowNotActive)
  250. SetCursorPos(centerX, centerY);
  251. }
  252. else
  253. {
  254. if(!windowNotActive && false)
  255. SetCursorPos(lastCursorPos.x + r.left, lastCursorPos.y + r.top);
  256. }
  257. }
  258. }
  259. //--------------------------------------
  260. static bool sgTaskbarHidden = false;
  261. static HWND sgTaskbar = NULL;
  262. static void hideTheTaskbar()
  263. {
  264. // if ( !sgTaskbarHidden )
  265. // {
  266. // sgTaskbar = FindWindow( "Shell_TrayWnd", NULL );
  267. // if ( sgTaskbar )
  268. // {
  269. // APPBARDATA abData;
  270. // dMemset( &abData, 0, sizeof( abData ) );
  271. // abData.cbSize = sizeof( abData );
  272. // abData.hWnd = sgTaskbar;
  273. // SHAppBarMessage( ABM_REMOVE, &abData );
  274. // //ShowWindow( sgTaskbar, SW_HIDE );
  275. // sgTaskbarHidden = true;
  276. // }
  277. // }
  278. }
  279. static void restoreTheTaskbar()
  280. {
  281. // if ( sgTaskbarHidden )
  282. // {
  283. // APPBARDATA abData;
  284. // dMemset( &abData, 0, sizeof( abData ) );
  285. // abData.cbSize = sizeof( abData );
  286. // abData.hWnd = sgTaskbar;
  287. // SHAppBarMessage( ABM_ACTIVATE, &abData );
  288. // //ShowWindow( sgTaskbar, SW_SHOW );
  289. // sgTaskbarHidden = false;
  290. // }
  291. }
  292. //--------------------------------------
  293. void Platform::enableKeyboardTranslation(void)
  294. {
  295. #ifdef UNICODE
  296. // Con::printf("translating...");
  297. ImmAssociateContext( winState.appWindow, winState.imeHandle );
  298. #endif
  299. }
  300. //--------------------------------------
  301. void Platform::disableKeyboardTranslation(void)
  302. {
  303. #ifdef UNICODE
  304. // Con::printf("not translating...");
  305. ImmAssociateContext( winState.appWindow, NULL );
  306. #endif
  307. }
  308. //--------------------------------------
  309. void Platform::setMouseLock(bool locked)
  310. {
  311. windowLocked = locked;
  312. setMouseClipping();
  313. }
  314. //--------------------------------------
  315. void Platform::minimizeWindow()
  316. {
  317. ShowWindow(winState.appWindow, SW_MINIMIZE);
  318. restoreTheTaskbar();
  319. }
  320. void Platform::restoreWindow()
  321. {
  322. ShowWindow(winState.appWindow, SW_RESTORE);
  323. }
  324. //--------------------------------------
  325. static void processKeyMessage(UINT message, WPARAM wParam, LPARAM lParam)
  326. {
  327. S32 repeatCount = lParam & 0xffff;
  328. S32 scanCode = (lParam >> 16) & 0xff;
  329. S32 nVirtkey = dIsStandardVK(wParam) ? TranslateOSKeyCode(wParam) : DIK_to_Key(scanCode);
  330. S32 keyCode;
  331. if ( wParam == VK_PROCESSKEY && sgDoubleByteEnabled )
  332. keyCode = MapVirtualKey( scanCode, 1 ); // This is the REAL virtual key...
  333. else
  334. keyCode = wParam;
  335. bool extended = (lParam >> 24) & 1;
  336. bool repeat = (lParam >> 30) & 1;
  337. bool make = (message == WM_KEYDOWN || message == WM_SYSKEYDOWN);
  338. S32 newVirtKey = nVirtkey;
  339. switch(nVirtkey)
  340. {
  341. case KEY_ALT:
  342. newVirtKey = extended ? KEY_RALT : KEY_LALT;
  343. break;
  344. case KEY_CONTROL:
  345. newVirtKey = extended ? KEY_RCONTROL : KEY_LCONTROL;
  346. break;
  347. case KEY_SHIFT:
  348. newVirtKey = ( scanCode == 54 ) ? KEY_RSHIFT : KEY_LSHIFT;
  349. break;
  350. case KEY_RETURN:
  351. if ( extended )
  352. newVirtKey = KEY_NUMPADENTER;
  353. break;
  354. }
  355. S32 modKey = modifierKeys;
  356. if(make)
  357. {
  358. switch (newVirtKey)
  359. {
  360. case KEY_LSHIFT: modifierKeys |= SI_LSHIFT; modKey = 0; break;
  361. case KEY_RSHIFT: modifierKeys |= SI_RSHIFT; modKey = 0; break;
  362. case KEY_LCONTROL: modifierKeys |= SI_LCTRL; modKey = 0; break;
  363. case KEY_RCONTROL: modifierKeys |= SI_RCTRL; modKey = 0; break;
  364. case KEY_LALT: modifierKeys |= SI_LALT; modKey = 0; break;
  365. case KEY_RALT: modifierKeys |= SI_RALT; modKey = 0; break;
  366. }
  367. if(nVirtkey == KEY_CAPSLOCK)
  368. {
  369. capsLockDown = !capsLockDown;
  370. if(capsLockDown)
  371. keyboardState[keyCode] |= 0x01;
  372. else
  373. keyboardState[keyCode] &= 0xFE;
  374. }
  375. keyboardState[keyCode] |= 0x80;
  376. }
  377. else
  378. {
  379. switch (newVirtKey)
  380. {
  381. case KEY_LSHIFT: modifierKeys &= ~SI_LSHIFT; modKey = 0; break;
  382. case KEY_RSHIFT: modifierKeys &= ~SI_RSHIFT; modKey = 0; break;
  383. case KEY_LCONTROL: modifierKeys &= ~SI_LCTRL; modKey = 0; break;
  384. case KEY_RCONTROL: modifierKeys &= ~SI_RCTRL; modKey = 0; break;
  385. case KEY_LALT: modifierKeys &= ~SI_LALT; modKey = 0; break;
  386. case KEY_RALT: modifierKeys &= ~SI_RALT; modKey = 0; break;
  387. }
  388. keyboardState[keyCode] &= 0x7f;
  389. }
  390. U16 ascii[3];
  391. WORD asciiCode = 0;
  392. dMemset( &ascii, 0, sizeof( ascii ) );
  393. S32 res = ToUnicode( keyCode, scanCode, keyboardState, ascii, 3, 0 );
  394. // This should only happen on Window 9x/ME systems
  395. if (res == 0)
  396. res = ToAscii( keyCode, scanCode, keyboardState, ascii, 0 );
  397. if (res == 2)
  398. {
  399. asciiCode = ascii[1];
  400. }
  401. else if ((res == 1) || (res < 0))
  402. {
  403. asciiCode = ascii[0];
  404. }
  405. InputEvent event;
  406. event.deviceInst = 0;
  407. event.deviceType = KeyboardDeviceType;
  408. event.objType = SI_KEY;
  409. event.objInst = newVirtKey;
  410. event.action = make ? (repeat ? SI_REPEAT : SI_MAKE ) : SI_BREAK;
  411. event.modifier = modKey;
  412. event.ascii = asciiCode;
  413. event.fValue = make ? 1.0f : 0.0f;
  414. // Store the current modifier keys
  415. Input::setModifierKeys(modifierKeys);
  416. Game->postEvent(event);
  417. }
  418. static void processCharMessage( WPARAM wParam, LPARAM lParam )
  419. {
  420. TCHAR charCode = wParam;
  421. UTF16 tmpString[2] = { charCode, 0 };
  422. UTF8 out[256];
  423. convertUTF16toUTF8(tmpString, out, 250);
  424. // Con::printf("Got IME char code %x (%s)", charCode, out);
  425. InputEvent event;
  426. event.deviceInst = 0;
  427. event.deviceType = KeyboardDeviceType;
  428. event.objType = SI_KEY;
  429. event.action = SI_MAKE;
  430. event.ascii = charCode;
  431. // Deal with some silly cases like <BS>. This might be indicative of a
  432. // missing MapVirtualKey step, not quite sure how to deal with this.
  433. if(event.ascii == 0x8)
  434. event.objInst = KEY_BACKSPACE; // Note we abuse objInst.
  435. Game->postEvent(event);
  436. // And put it back up as well, they can't hold it!
  437. event.action = SI_BREAK;
  438. Game->postEvent(event);
  439. }
  440. static S32 mouseX = 0xFFFFFFFF;
  441. static S32 mouseY = 0xFFFFFFFF;
  442. //--------------------------------------
  443. static void CheckCursorPos()
  444. {
  445. if(windowLocked && windowActive)
  446. {
  447. POINT mousePos;
  448. GetCursorPos(&mousePos);
  449. RECT r;
  450. GetWindowRect(winState.appWindow, &r);
  451. S32 centerX = (r.right + r.left) >> 1;
  452. S32 centerY = (r.bottom + r.top) >> 1;
  453. if(mousePos.x != centerX)
  454. {
  455. InputEvent event;
  456. event.deviceInst = 0;
  457. event.deviceType = MouseDeviceType;
  458. event.objType = SI_XAXIS;
  459. event.objInst = 0;
  460. event.action = SI_MOVE;
  461. event.modifier = modifierKeys;
  462. event.ascii = 0;
  463. event.fValue = (F32)(mousePos.x - centerX);
  464. Game->postEvent(event);
  465. }
  466. if(mousePos.y != centerY)
  467. {
  468. InputEvent event;
  469. event.deviceInst = 0;
  470. event.deviceType = MouseDeviceType;
  471. event.objType = SI_YAXIS;
  472. event.objInst = 0;
  473. event.action = SI_MOVE;
  474. event.modifier = modifierKeys;
  475. event.ascii = 0;
  476. event.fValue = (F32)(mousePos.y - centerY);
  477. Game->postEvent(event);
  478. }
  479. SetCursorPos(centerX, centerY);
  480. }
  481. }
  482. //--------------------------------------
  483. static void mouseButtonEvent(S32 action, S32 objInst)
  484. {
  485. CheckCursorPos();
  486. if(!windowLocked)
  487. {
  488. if(action == SI_MAKE)
  489. SetCapture(winState.appWindow);
  490. else
  491. ReleaseCapture();
  492. }
  493. U32 buttonId = objInst - KEY_BUTTON0;
  494. if ( buttonId < 3 )
  495. mouseButtonState[buttonId] = ( action == SI_MAKE ) ? true : false;
  496. InputEvent event;
  497. event.deviceInst = 0;
  498. event.deviceType = MouseDeviceType;
  499. event.objType = SI_BUTTON;
  500. event.objInst = objInst;
  501. event.action = action;
  502. event.modifier = modifierKeys;
  503. event.ascii = 0;
  504. event.fValue = action == SI_MAKE ? 1.0f : 0.0f;
  505. Game->postEvent(event);
  506. }
  507. //--------------------------------------
  508. static void mouseWheelEvent( S32 delta )
  509. {
  510. static S32 _delta = 0;
  511. _delta += delta;
  512. if ( abs( delta ) >= WHEEL_DELTA )
  513. {
  514. _delta = 0;
  515. InputEvent event;
  516. event.deviceInst = 0;
  517. event.deviceType = MouseDeviceType;
  518. event.objType = SI_ZAXIS;
  519. event.objInst = 0;
  520. event.action = SI_MOVE;
  521. event.modifier = modifierKeys;
  522. event.ascii = 0;
  523. event.fValue = (F32)delta;
  524. Game->postEvent( event );
  525. }
  526. }
  527. struct WinMessage
  528. {
  529. UINT message;
  530. WPARAM wParam;
  531. LPARAM lParam;
  532. WinMessage() {};
  533. WinMessage(UINT m, WPARAM w, LPARAM l) : message(m), wParam(w), lParam(l) {}
  534. };
  535. Vector<WinMessage> sgWinMessages;
  536. #ifndef PBT_APMQUERYSUSPEND
  537. #define PBT_APMQUERYSUSPEND 0x0000
  538. #endif
  539. bool sgKeyboardStateDirty = false;
  540. //--------------------------------------
  541. static LRESULT PASCAL WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  542. {
  543. switch ( message )
  544. {
  545. // Window DragDrop-enabled [12/14/2006 justind]
  546. case WM_CREATE:
  547. DragAcceptFiles(hWnd, TRUE);
  548. break;
  549. case WM_DESTROY:
  550. DragAcceptFiles(hWnd, FALSE);
  551. break;
  552. case WM_DROPFILES:
  553. // Retrieve Number of files
  554. // Strict policy on these things, better safe than sorry
  555. if( !Con::isFunction( "onDropBegin") || !Con::isFunction("onDropFile")
  556. || !Con::isFunction("onDropEnd") )
  557. break;
  558. int nFileCount; // Number of Files
  559. HDROP hTheDrop; // The Drop Handle
  560. hTheDrop = (HDROP)wParam;
  561. nFileCount = DragQueryFile(hTheDrop, 0xFFFFFFFF, NULL, 0);
  562. if( nFileCount == 0 )
  563. break;
  564. // Notify Drop-Begin
  565. Con::executef( 2, "onDropBegin", Con::getIntArg( nFileCount ) );
  566. int nI;
  567. for (nI = 0; nI < nFileCount; nI++)
  568. {
  569. LPTSTR pszTheBuffer[MAX_PATH];
  570. ZeroMemory( pszTheBuffer, MAX_PATH );
  571. // Query it
  572. // FIXME: Deal with Unicode
  573. DragQueryFileA(hTheDrop, nI, (LPSTR)pszTheBuffer, sizeof(pszTheBuffer));
  574. // Notify Drop
  575. if (Platform::isFile( (const char*)pszTheBuffer ))
  576. {
  577. // The timeout for waiting for files (ms).
  578. U32 timeout = 5000;
  579. // Need to make sure the file is copyable. When ganking images from Firefox, it
  580. // isn't necessarily since Firefox insists on redownloading the file instead of
  581. // using the local copy.
  582. U32 time = Platform::getRealMilliseconds() + timeout;
  583. HANDLE hfile;
  584. while((hfile = ::CreateFileA((const char*)pszTheBuffer, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
  585. {
  586. // Don't wait too long.
  587. if(Platform::getRealMilliseconds() > time)
  588. break;
  589. }
  590. if(hfile == INVALID_HANDLE_VALUE)
  591. continue;
  592. CloseHandle(hfile);
  593. Con::executef( 2, "onDropFile", StringTable->insert((const char*)pszTheBuffer) );
  594. }
  595. }
  596. DragFinish(hTheDrop);
  597. // Notify Drop-Begin
  598. Con::executef( 2, "onDropEnd", Con::getIntArg( nFileCount ) );
  599. break;
  600. // Window Min Extents - [9/20/2006 justind]
  601. case WM_GETMINMAXINFO:
  602. MINMAXINFO *winfo;
  603. winfo = (MINMAXINFO*)(lParam);
  604. //MIN_RESOLUTION defined in platformWin32/platformGL.h
  605. winfo->ptMinTrackSize.x = MIN_RESOLUTION_X;
  606. winfo->ptMinTrackSize.y = MIN_RESOLUTION_Y;
  607. break;
  608. case WM_POWERBROADCAST:
  609. {
  610. if(wParam == PBT_APMQUERYSUSPEND)
  611. return BROADCAST_QUERY_DENY;
  612. return true;
  613. }
  614. case WM_ACTIVATEAPP:
  615. if ((bool) wParam)
  616. {
  617. Video::reactivate();
  618. if ( Video::isFullScreen() )
  619. hideTheTaskbar();
  620. // HACK: Windows 98 (after switching from fullscreen to windowed mode)
  621. SetForegroundWindow( winState.appWindow );
  622. // If our keyboard state is dirty, clear it
  623. if( sgKeyboardStateDirty == true )
  624. {
  625. sgKeyboardStateDirty = false;
  626. InitInput();
  627. }
  628. }
  629. else
  630. {
  631. // Window lost focus so set a dirty flag on the keyboard state
  632. if ( lParam == 0 )
  633. sgKeyboardStateDirty = true;
  634. Video::deactivate();
  635. restoreTheTaskbar();
  636. }
  637. break;
  638. case WM_SYSCOMMAND:
  639. switch(wParam)
  640. {
  641. case SC_KEYMENU:
  642. case SC_SCREENSAVE:
  643. case SC_MONITORPOWER:
  644. if(GetForegroundWindow() == winState.appWindow)
  645. {
  646. return 0;
  647. }
  648. break;
  649. // Catch Close button press event [1/5/2007 justind]
  650. case SC_CLOSE:
  651. if (Con::isFunction("onClosePressed"))
  652. {
  653. Con::executef( 1, "onClosePressed" );
  654. return 0;
  655. }
  656. break;
  657. }
  658. break;
  659. case WM_ACTIVATE:
  660. windowActive = LOWORD(wParam) != WA_INACTIVE;
  661. if( Game->isRunning() )
  662. {
  663. if (Con::isFunction("onWindowFocusChange"))
  664. Con::executef( 2, "onWindowFocusChange", windowActive ? "1" : "0" );
  665. }
  666. if ( windowActive )
  667. {
  668. setMouseClipping();
  669. windowNotActive = false;
  670. Game->refreshWindow();
  671. Input::activate();
  672. }
  673. else
  674. {
  675. setMouseClipping();
  676. windowNotActive = true;
  677. DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
  678. if ( !mgr || !mgr->isMouseActive() )
  679. {
  680. // Deactivate all the mouse triggers:
  681. for ( U32 i = 0; i < 3; i++ )
  682. {
  683. if ( mouseButtonState[i] )
  684. mouseButtonEvent( SI_BREAK, KEY_BUTTON0 + i );
  685. }
  686. }
  687. Input::deactivate();
  688. }
  689. break;
  690. case WM_MOVE:
  691. Game->refreshWindow();
  692. break;
  693. // This override will keep windows from blacking out the background, wee!
  694. case WM_ERASEBKGND:
  695. return 0;
  696. case WM_SIZE:
  697. if(wParam != SIZE_MINIMIZED) // DAW: If window is minimized, don't do an internal resize or the universe will end
  698. {
  699. S32 nWidth, nHeight;
  700. nWidth = LOWORD(lParam);
  701. nHeight = HIWORD(lParam);
  702. Platform::setWindowSize( nWidth, nHeight );
  703. Game->gameReactivate();
  704. // MP: Commented out the if check to expose this variable no matter what the resizing is. This will help out the teams
  705. // that rely on knowing the windowRes for their tools (like Black Jack's card generator).
  706. //
  707. // If we're greater than MIN_RESOLUTION and less than our client desktop area, store as windowed mode pref resolution
  708. //if( ( nWidth > MIN_RESOLUTION_X && nWidth < winState.desktopClientWidth ) && ( nHeight > MIN_RESOLUTION_Y && nHeight < winState.desktopClientHeight ) )
  709. //{
  710. char tempBuf[32];
  711. dSprintf( tempBuf, sizeof(char) * 32, "%d %d %d", nWidth,nHeight, 16 );
  712. Con::setVariable( "$pref::Video::windowedRes", tempBuf );
  713. //}
  714. }
  715. else
  716. {
  717. // Deactivate ourselves when we're hidden
  718. Game->gameDeactivate( true );
  719. }
  720. break;
  721. case MM_MCINOTIFY:
  722. //handleRedBookCallback(wParam, lParam);
  723. break;
  724. //case WM_DESTROY:
  725. case WM_CLOSE:
  726. PostQuitMessage(0);
  727. break;
  728. case WM_SETFOCUS:
  729. winState.renderThreadBlocked = false;
  730. break;
  731. case WM_KILLFOCUS:
  732. winState.renderThreadBlocked = true;
  733. break;
  734. case WM_COMMAND:
  735. if(HIWORD(wParam) == 0)
  736. {
  737. winState.renderThreadBlocked = false;
  738. // This command came from a menu choice. Pass along the menu ID to a
  739. // script function
  740. // [tom, 8/21/2006] Pass off to the relevant PopupMenu
  741. S32 numItems = GetMenuItemCount(winState.appMenu);
  742. for(S32 i = 0;i < numItems;i++)
  743. {
  744. MENUITEMINFOA mi;
  745. mi.cbSize = sizeof(mi);
  746. mi.fMask = MIIM_DATA;
  747. if(GetMenuItemInfoA(winState.appMenu, i, TRUE, &mi))
  748. {
  749. if(mi.fMask & MIIM_DATA)
  750. {
  751. PopupMenu *mnu = (PopupMenu *)mi.dwItemData;
  752. if(mnu->canHandleID(LOWORD(wParam)))
  753. mnu->handleSelect(LOWORD(wParam));
  754. }
  755. }
  756. }
  757. }
  758. break;
  759. case WM_MENUSELECT:
  760. winState.renderThreadBlocked = true;
  761. case WM_PAINT:
  762. if(winState.renderThreadBlocked)
  763. Canvas->renderFrame(false);
  764. break;
  765. #ifdef UNICODE
  766. case WM_INPUTLANGCHANGE:
  767. // Con::printf("IME lang change");
  768. break;
  769. case WM_IME_SETCONTEXT:
  770. // Con::printf("IME set context");
  771. break;
  772. case WM_IME_COMPOSITION:
  773. // Con::printf("IME comp");
  774. break;
  775. case WM_IME_STARTCOMPOSITION:
  776. // Con::printf("IME start comp");
  777. break;
  778. #endif
  779. default:
  780. {
  781. if (sgQueueEvents)
  782. {
  783. WinMessage msg(message,wParam,lParam);
  784. sgWinMessages.push_front(msg);
  785. }
  786. }
  787. }
  788. return DefWindowProc(hWnd, message, wParam, lParam);
  789. }
  790. //--------------------------------------
  791. static void OurDispatchMessages()
  792. {
  793. WinMessage msg(0,0,0);
  794. UINT message;
  795. WPARAM wParam;
  796. LPARAM lParam;
  797. DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
  798. while (sgWinMessages.size())
  799. {
  800. msg = sgWinMessages.front();
  801. sgWinMessages.pop_front();
  802. message = msg.message;
  803. wParam = msg.wParam;
  804. lParam = msg.lParam;
  805. if ( !mgr || !mgr->isMouseActive() )
  806. {
  807. switch ( message )
  808. {
  809. #ifdef UNICODE
  810. case WM_IME_COMPOSITION:
  811. {
  812. // Con::printf("OMG IME comp");
  813. break;
  814. }
  815. case WM_IME_ENDCOMPOSITION:
  816. {
  817. // Con::printf("OMG IME end comp");
  818. break;
  819. }
  820. case WM_IME_NOTIFY:
  821. {
  822. // Con::printf("OMG IME notify");
  823. break;
  824. }
  825. case WM_IME_CHAR:
  826. processCharMessage( wParam, lParam );
  827. break;
  828. #endif
  829. // We want to show the system cursor whenever we leave
  830. // our window, and it'd be simple, except for one problem:
  831. // showcursor isn't a toggle. so, keep hammering it until
  832. // the cursor is *actually* going to be shown.
  833. case WM_NCMOUSEMOVE:
  834. {
  835. break;
  836. }
  837. case WM_MOUSEMOVE:
  838. // keep trying until we actually show it
  839. Input::refreshCursor();
  840. if ( !windowLocked )
  841. {
  842. MouseMoveEvent event;
  843. S16 xPos = LOWORD(lParam);
  844. S16 yPos = HIWORD(lParam);
  845. event.xPos = xPos; // horizontal position of cursor
  846. if( winState.appMenu != NULL )
  847. {
  848. S32 menuHeight = GetSystemMetrics(SM_CYMENU);
  849. event.yPos = ( yPos ); // vertical position of cursor
  850. }
  851. else
  852. event.yPos = yPos; // vertical position of cursor
  853. event.modifier = modifierKeys;
  854. Game->postEvent(event);
  855. }
  856. break;
  857. case WM_LBUTTONDOWN:
  858. mouseButtonEvent(SI_MAKE, KEY_BUTTON0);
  859. break;
  860. case WM_MBUTTONDOWN:
  861. mouseButtonEvent(SI_MAKE, KEY_BUTTON2);
  862. break;
  863. case WM_RBUTTONDOWN:
  864. mouseButtonEvent(SI_MAKE, KEY_BUTTON1);
  865. break;
  866. case WM_LBUTTONUP:
  867. mouseButtonEvent(SI_BREAK, KEY_BUTTON0);
  868. break;
  869. case WM_MBUTTONUP:
  870. mouseButtonEvent(SI_BREAK, KEY_BUTTON2);
  871. break;
  872. case WM_RBUTTONUP:
  873. mouseButtonEvent(SI_BREAK, KEY_BUTTON1);
  874. break;
  875. case WM_MOUSEWHEEL:
  876. mouseWheelEvent( (S16) HIWORD( wParam ) );
  877. break;
  878. }
  879. }
  880. if ( !mgr || !mgr->isKeyboardActive() )
  881. {
  882. switch ( message )
  883. {
  884. case WM_KEYUP:
  885. case WM_SYSKEYUP:
  886. case WM_KEYDOWN:
  887. case WM_SYSKEYDOWN:
  888. processKeyMessage(message, wParam, lParam);
  889. break;
  890. }
  891. }
  892. }
  893. }
  894. //--------------------------------------
  895. static bool ProcessMessages()
  896. {
  897. MSG msg;
  898. PROFILE_SCOPE(ProcessMessages_MAIN);
  899. S32 nMessagesDispatched = 0;
  900. while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
  901. {
  902. if(msg.message == WM_QUIT)
  903. return false;
  904. PROFILE_START(TranslateMessage);
  905. TranslateMessage(&msg);
  906. PROFILE_END();
  907. PROFILE_START(DispatchMessage);
  908. DispatchMessage(&msg);
  909. PROFILE_END();
  910. PROFILE_START(OurDispatchMessages);
  911. OurDispatchMessages();
  912. PROFILE_END();
  913. nMessagesDispatched++;
  914. if( nMessagesDispatched >= winState.nMessagesPerFrame )
  915. break;
  916. }
  917. return true;
  918. }
  919. //--------------------------------------
  920. void Platform::process()
  921. {
  922. PROFILE_START(GetInputManager);
  923. DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
  924. PROFILE_END();
  925. if ( !mgr || !mgr->isMouseActive() )
  926. {
  927. PROFILE_SCOPE(InputManagerCheckCursor);
  928. CheckCursorPos();
  929. }
  930. PROFILE_START(WinConsoleProcess);
  931. WindowsConsole->process();
  932. PROFILE_END();
  933. PROFILE_START(ProcessMessages);
  934. if(!ProcessMessages())
  935. {
  936. PROFILE_SCOPE(PostEvent);
  937. // generate a quit event
  938. Event quitEvent;
  939. quitEvent.type = QuitEventType;
  940. Game->postEvent(quitEvent);
  941. }
  942. PROFILE_END();
  943. PROFILE_START(GetForegroundWindow);
  944. HWND window = GetForegroundWindow();
  945. PROFILE_END();
  946. // assume that we are in the foreground,
  947. winState.backgrounded = false;
  948. // unless we see that the foreground window does not belong to this process.
  949. if (window && window != winState.appWindow && gWindowCreated)
  950. {
  951. DWORD foregroundProcessId;
  952. GetWindowThreadProcessId(window, &foregroundProcessId);
  953. if (foregroundProcessId != winState.processId)
  954. winState.backgrounded = true;
  955. }
  956. PROFILE_SCOPE(InputProcess);
  957. Input::process();
  958. }
  959. extern U32 calculateCRC(void * buffer, S32 len, U32 crcVal );
  960. #if defined(TORQUE_DEBUG) || defined(INTERNAL_RELEASE)
  961. static U32 stubCRC = 0;
  962. #else
  963. static U32 stubCRC = 0xEA63F56C;
  964. #endif
  965. //--------------------------------------
  966. static void InitWindowClass()
  967. {
  968. WNDCLASS wc;
  969. dMemset(&wc, 0, sizeof(wc));
  970. wc.style = CS_OWNDC;
  971. wc.lpfnWndProc = WindowProc;
  972. wc.cbClsExtra = 0;
  973. wc.cbWndExtra = 0;
  974. wc.hInstance = winState.appInstance;
  975. wc.hIcon = LoadIcon(winState.appInstance, MAKEINTRESOURCE(IDI_TORQUE2D));
  976. wc.hCursor = LoadCursor (NULL,IDC_ARROW);
  977. wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
  978. wc.lpszMenuName = 0;
  979. wc.lpszClassName = windowClassName;
  980. RegisterClass( &wc );
  981. // Curtain window class:
  982. wc.lpfnWndProc = DefWindowProc;
  983. wc.hCursor = NULL;
  984. wc.hbrBackground = (HBRUSH) GetStockObject(GRAY_BRUSH);
  985. wc.lpszClassName = dT("Curtain");
  986. RegisterClass( &wc );
  987. }
  988. //--------------------------------------
  989. Resolution Video::getDesktopResolution()
  990. {
  991. Resolution Result;
  992. RECT clientRect;
  993. HWND hDesktop = GetDesktopWindow();
  994. HDC hDeskDC = GetDC( hDesktop );
  995. // Retrieve Resolution Information.
  996. Result.bpp = winState.desktopBitsPixel = GetDeviceCaps( hDeskDC, BITSPIXEL );
  997. Result.w = winState.desktopWidth = GetDeviceCaps( hDeskDC, HORZRES );
  998. Result.h = winState.desktopHeight = GetDeviceCaps( hDeskDC, VERTRES );
  999. // Release Device Context.
  1000. ReleaseDC( hDesktop, hDeskDC );
  1001. SystemParametersInfo(SPI_GETWORKAREA, 0, &clientRect, 0);
  1002. winState.desktopClientWidth = clientRect.right;
  1003. winState.desktopClientHeight = clientRect.bottom;
  1004. // Return Result;
  1005. return Result;
  1006. }
  1007. //--------------------------------------
  1008. HWND CreateOpenGLWindow( U32 width, U32 height, bool fullScreen, bool allowSizing )
  1009. {
  1010. S32 windowStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
  1011. S32 exWindowStyle = 0;
  1012. if ( fullScreen )
  1013. windowStyle |= ( WS_POPUP | WS_MAXIMIZE );
  1014. else
  1015. if (!allowSizing)
  1016. windowStyle |= (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX);
  1017. else
  1018. windowStyle |= ( WS_OVERLAPPEDWINDOW );
  1019. return CreateWindowEx(
  1020. exWindowStyle,
  1021. windowClassName,
  1022. windowName,
  1023. windowStyle,
  1024. 0, 0, width, height,
  1025. NULL, NULL,
  1026. winState.appInstance,
  1027. NULL);
  1028. }
  1029. //--------------------------------------
  1030. HWND CreateCurtain( U32 width, U32 height )
  1031. {
  1032. return CreateWindow(
  1033. dT("Curtain"),
  1034. dT(""),
  1035. ( WS_POPUP | WS_MAXIMIZE ),
  1036. 0, 0,
  1037. width, height,
  1038. NULL, NULL,
  1039. winState.appInstance,
  1040. NULL );
  1041. }
  1042. //--------------------------------------
  1043. void CreatePixelFormat( PIXELFORMATDESCRIPTOR *pPFD, S32 colorBits, S32 depthBits, S32 stencilBits, bool stereo )
  1044. {
  1045. PIXELFORMATDESCRIPTOR src =
  1046. {
  1047. sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
  1048. 1, // version number
  1049. PFD_DRAW_TO_WINDOW | // support window
  1050. PFD_SUPPORT_OPENGL | // support OpenGL
  1051. PFD_DOUBLEBUFFER, // double buffered
  1052. PFD_TYPE_RGBA, // RGBA type
  1053. colorBits, // color depth
  1054. 0, 0, 0, 0, 0, 0, // color bits ignored
  1055. 0, // no alpha buffer
  1056. 0, // shift bit ignored
  1057. 0, // no accumulation buffer
  1058. 0, 0, 0, 0, // accum bits ignored
  1059. depthBits, // z-buffer
  1060. stencilBits, // stencil buffer
  1061. 0, // no auxiliary buffer
  1062. PFD_MAIN_PLANE, // main layer
  1063. 0, // reserved
  1064. 0, 0, 0 // layer masks ignored
  1065. };
  1066. if ( stereo )
  1067. {
  1068. //ri.Printf( PRINT_ALL, "...attempting to use stereo\n" );
  1069. src.dwFlags |= PFD_STEREO;
  1070. //glConfig.stereoEnabled = true;
  1071. }
  1072. else
  1073. {
  1074. //glConfig.stereoEnabled = qfalse;
  1075. }
  1076. *pPFD = src;
  1077. }
  1078. //--------------------------------------
  1079. enum WinConstants { MAX_PFDS = 256 };
  1080. #ifndef PFD_GENERIC_ACCELERATED
  1081. #define PFD_GENERIC_ACCELERATED 0x00001000
  1082. #endif
  1083. S32 ChooseBestPixelFormat(HDC hDC, PIXELFORMATDESCRIPTOR *pPFD)
  1084. {
  1085. PIXELFORMATDESCRIPTOR pfds[MAX_PFDS+1];
  1086. S32 i;
  1087. S32 bestMatch = 0;
  1088. S32 maxPFD = dwglDescribePixelFormat(hDC, 1, sizeof(PIXELFORMATDESCRIPTOR), &pfds[0]);
  1089. if(maxPFD > MAX_PFDS)
  1090. maxPFD = MAX_PFDS;
  1091. bool accelerated = false;
  1092. for(i = 1; i <= maxPFD; i++)
  1093. {
  1094. dwglDescribePixelFormat(hDC, i, sizeof(PIXELFORMATDESCRIPTOR), &pfds[i]);
  1095. // make sure this has hardware acceleration:
  1096. if ( ( pfds[i].dwFlags & PFD_GENERIC_FORMAT ) != 0 )
  1097. continue;
  1098. // verify pixel type
  1099. if ( pfds[i].iPixelType != PFD_TYPE_RGBA )
  1100. continue;
  1101. // verify proper flags
  1102. if ( ( ( pfds[i].dwFlags & pPFD->dwFlags ) & pPFD->dwFlags ) != pPFD->dwFlags )
  1103. continue;
  1104. accelerated = !(pfds[i].dwFlags & PFD_GENERIC_FORMAT);
  1105. //
  1106. // selection criteria (in order of priority):
  1107. //
  1108. // PFD_STEREO
  1109. // colorBits
  1110. // depthBits
  1111. // stencilBits
  1112. //
  1113. if ( bestMatch )
  1114. {
  1115. // check stereo
  1116. if ( ( pfds[i].dwFlags & PFD_STEREO ) && ( !( pfds[bestMatch].dwFlags & PFD_STEREO ) ) && ( pPFD->dwFlags & PFD_STEREO ) )
  1117. {
  1118. bestMatch = i;
  1119. continue;
  1120. }
  1121. if ( !( pfds[i].dwFlags & PFD_STEREO ) && ( pfds[bestMatch].dwFlags & PFD_STEREO ) && ( pPFD->dwFlags & PFD_STEREO ) )
  1122. {
  1123. bestMatch = i;
  1124. continue;
  1125. }
  1126. // check color
  1127. if ( pfds[bestMatch].cColorBits != pPFD->cColorBits )
  1128. {
  1129. // prefer perfect match
  1130. if ( pfds[i].cColorBits == pPFD->cColorBits )
  1131. {
  1132. bestMatch = i;
  1133. continue;
  1134. }
  1135. // otherwise if this PFD has more bits than our best, use it
  1136. else if ( pfds[i].cColorBits > pfds[bestMatch].cColorBits )
  1137. {
  1138. bestMatch = i;
  1139. continue;
  1140. }
  1141. }
  1142. // check depth
  1143. if ( pfds[bestMatch].cDepthBits != pPFD->cDepthBits )
  1144. {
  1145. // prefer perfect match
  1146. if ( pfds[i].cDepthBits == pPFD->cDepthBits )
  1147. {
  1148. bestMatch = i;
  1149. continue;
  1150. }
  1151. // otherwise if this PFD has more bits than our best, use it
  1152. else if ( pfds[i].cDepthBits > pfds[bestMatch].cDepthBits )
  1153. {
  1154. bestMatch = i;
  1155. continue;
  1156. }
  1157. }
  1158. // check stencil
  1159. if ( pfds[bestMatch].cStencilBits != pPFD->cStencilBits )
  1160. {
  1161. // prefer perfect match
  1162. if ( pfds[i].cStencilBits == pPFD->cStencilBits )
  1163. {
  1164. bestMatch = i;
  1165. continue;
  1166. }
  1167. // otherwise if this PFD has more bits than our best, use it
  1168. else if ( ( pfds[i].cStencilBits > pfds[bestMatch].cStencilBits ) &&
  1169. ( pPFD->cStencilBits > 0 ) )
  1170. {
  1171. bestMatch = i;
  1172. continue;
  1173. }
  1174. }
  1175. }
  1176. else
  1177. {
  1178. bestMatch = i;
  1179. }
  1180. }
  1181. if ( !bestMatch )
  1182. return 0;
  1183. else if ( pfds[bestMatch].dwFlags & PFD_GENERIC_ACCELERATED )
  1184. {
  1185. // MCD
  1186. }
  1187. else
  1188. {
  1189. // ICD
  1190. }
  1191. *pPFD = pfds[bestMatch];
  1192. return bestMatch;
  1193. }
  1194. //--------------------------------------
  1195. //
  1196. // This function exists so DirectInput can communicate with the Windows mouse
  1197. // in windowed mode.
  1198. //
  1199. //--------------------------------------
  1200. void setModifierKeys( S32 modKeys )
  1201. {
  1202. modifierKeys = modKeys;
  1203. }
  1204. //--------------------------------------
  1205. const Point2I &Platform::getWindowSize()
  1206. {
  1207. return windowSize;
  1208. }
  1209. //--------------------------------------
  1210. void Platform::setWindowSize( U32 newWidth, U32 newHeight )
  1211. {
  1212. windowSize.set( newWidth, newHeight );
  1213. }
  1214. //RectI Platform::getWindowClientRect()
  1215. //{
  1216. // RECT rClientRect;
  1217. // GetClientRect( winState.appWindow, &rClientRect );
  1218. //
  1219. // if( winState.appMenu == NULL )
  1220. // return RectI( rClientRect.left, rClientRect.top, rClientRect.right - rClientRect.left, rClientRect.bottom - rClientRect.top );
  1221. //
  1222. // S32 menuHeight = GetSystemMetrics(SM_CYMENU);
  1223. // return RectI( rClientRect.left, rClientRect.top + menuHeight, rClientRect.right - rClientRect.left, rClientRect.bottom - ( rClientRect.top + menuHeight ) );
  1224. //}
  1225. //--------------------------------------
  1226. static void InitWindow(const Point2I &initialSize)
  1227. {
  1228. windowSize = initialSize;
  1229. // The window is created when the display device is activated. BH
  1230. winState.processId = GetCurrentProcessId();
  1231. }
  1232. //--------------------------------------
  1233. static void InitOpenGL()
  1234. {
  1235. // The OpenGL initialization stuff has been mostly moved to the display
  1236. // devices' activate functions. BH
  1237. DisplayDevice::init();
  1238. bool fullScreen = Con::getBoolVariable( "$pref::Video::fullScreen" );
  1239. const char* resString = Con::getVariable( ( fullScreen ? "$pref::Video::resolution" : "$pref::Video::windowedRes" ) );
  1240. // Get the video settings from the prefs:
  1241. char* tempBuf = new char[dStrlen( resString ) + 1];
  1242. dStrcpy( tempBuf, resString );
  1243. char* temp = dStrtok( tempBuf, " x\0" );
  1244. //MIN_RESOLUTION defined in platformWin32/platformGL.h
  1245. U32 width = ( temp ? dAtoi( temp ) : MIN_RESOLUTION_X );
  1246. temp = dStrtok( NULL, " x\0" );
  1247. U32 height = ( temp ? dAtoi( temp ) : MIN_RESOLUTION_Y );
  1248. temp = dStrtok( NULL, "\0" );
  1249. U32 bpp = ( temp ? dAtoi( temp ) : MIN_RESOLUTION_BIT_DEPTH );
  1250. delete [] tempBuf;
  1251. // If no device is specified, see which ones we have...
  1252. if ( !Video::setDevice( Con::getVariable( "$pref::Video::displayDevice" ), width, height, bpp, fullScreen ) )
  1253. {
  1254. // First, try the default OpenGL device:
  1255. if ( !Video::setDevice( "OpenGL", width, height, bpp, fullScreen ) )
  1256. {
  1257. AssertFatal( false, "Could not find a compatible display device!" );
  1258. return;
  1259. }
  1260. }
  1261. }
  1262. //--------------------------------------
  1263. void Platform::init()
  1264. {
  1265. // Set the platform variable for the scripts
  1266. Con::setVariable( "$platform", "windows" );
  1267. WinConsole::create();
  1268. if ( !WinConsole::isEnabled() )
  1269. Input::init();
  1270. InitInput(); // in case DirectInput falls through
  1271. InitWindowClass();
  1272. Video::getDesktopResolution();
  1273. //installRedBookDevices();
  1274. sgDoubleByteEnabled = GetSystemMetrics( SM_DBCSENABLED );
  1275. sgQueueEvents = true;
  1276. }
  1277. //--------------------------------------
  1278. void Platform::shutdown()
  1279. {
  1280. sgQueueEvents = false;
  1281. setMouseLock( false );
  1282. Video::destroy();
  1283. Input::destroy();
  1284. WinConsole::destroy();
  1285. }
  1286. class WinTimer
  1287. {
  1288. private:
  1289. U32 mTickCountCurrent;
  1290. U32 mTickCountNext;
  1291. S64 mPerfCountCurrent;
  1292. S64 mPerfCountNext;
  1293. S64 mFrequency;
  1294. F64 mPerfCountRemainderCurrent;
  1295. F64 mPerfCountRemainderNext;
  1296. bool mUsingPerfCounter;
  1297. public:
  1298. WinTimer()
  1299. {
  1300. SetProcessAffinityMask( GetCurrentProcess(), 1 );
  1301. mPerfCountRemainderCurrent = 0.0f;
  1302. mUsingPerfCounter = QueryPerformanceFrequency((LARGE_INTEGER *) &mFrequency);
  1303. if(mUsingPerfCounter)
  1304. mUsingPerfCounter = QueryPerformanceCounter((LARGE_INTEGER *) &mPerfCountCurrent);
  1305. if(!mUsingPerfCounter)
  1306. mTickCountCurrent = GetTickCount();
  1307. }
  1308. U32 getElapsedMS()
  1309. {
  1310. if(mUsingPerfCounter)
  1311. {
  1312. QueryPerformanceCounter( (LARGE_INTEGER *) &mPerfCountNext);
  1313. F64 elapsedF64 = (1000.0 * F64(mPerfCountNext - mPerfCountCurrent) / F64(mFrequency));
  1314. elapsedF64 += mPerfCountRemainderCurrent;
  1315. U32 elapsed = (U32)mFloor((F32)elapsedF64);
  1316. mPerfCountRemainderNext = elapsedF64 - F64(elapsed);
  1317. return elapsed;
  1318. }
  1319. else
  1320. {
  1321. mTickCountNext = GetTickCount();
  1322. return mTickCountNext - mTickCountCurrent;
  1323. }
  1324. }
  1325. void advance()
  1326. {
  1327. mTickCountCurrent = mTickCountNext;
  1328. mPerfCountCurrent = mPerfCountNext;
  1329. mPerfCountRemainderCurrent = mPerfCountRemainderNext;
  1330. }
  1331. };
  1332. static WinTimer gTimer;
  1333. extern bool LinkConsoleFunctions;
  1334. //--------------------------------------
  1335. static S32 run(S32 argc, const char **argv)
  1336. {
  1337. // Initialize fonts.
  1338. createFontInit();
  1339. windowSize.set(0,0);
  1340. // Finish if the game didn't initialize.
  1341. if(!Game->mainInitialize(argc, argv) )
  1342. return 0;
  1343. // run the game main loop.
  1344. while( Game->isRunning() )
  1345. {
  1346. Game->mainLoop();
  1347. }
  1348. // Shut the game down.
  1349. Game->mainShutdown();
  1350. // Destroy fonts.
  1351. createFontShutdown();
  1352. return 0;
  1353. }
  1354. //--------------------------------------
  1355. void Platform::initWindow(const Point2I &initialSize, const char *name)
  1356. {
  1357. MSG msg;
  1358. Con::printSeparator();
  1359. Con::printf("Video Initialization:");
  1360. Video::init();
  1361. PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
  1362. if ( Video::installDevice( OpenGLDevice::create() ) )
  1363. Con::printf( " Accelerated OpenGL display device detected." );
  1364. else
  1365. Con::printf( " Accelerated OpenGL display device not detected." );
  1366. Con::printf( "" );
  1367. gWindowCreated = true;
  1368. #ifdef UNICODE
  1369. convertUTF8toUTF16((UTF8 *)name, windowName, sizeof(windowName));
  1370. #else
  1371. dStrcpy(windowName, name);
  1372. #endif
  1373. PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
  1374. InitWindow(initialSize);
  1375. PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
  1376. InitOpenGL();
  1377. PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
  1378. }
  1379. void Platform::setWindowTitle( const char* title )
  1380. {
  1381. if( !title || !title[0] )
  1382. return;
  1383. #ifdef UNICODE
  1384. convertUTF8toUTF16((UTF8 *)title, windowName, sizeof(windowName));
  1385. #else
  1386. dStrcpy(windowName, title);
  1387. #endif
  1388. if( winState.appWindow )
  1389. #ifdef UNICODE
  1390. SetWindowTextW( winState.appWindow, windowName );
  1391. #else
  1392. SetWindowTextA( winState.appWindow, windowName );
  1393. #endif
  1394. }
  1395. //--------------------------------------
  1396. S32 main(S32 argc, const char **argv)
  1397. {
  1398. winState.appInstance = GetModuleHandle(NULL);
  1399. return run(argc, argv);
  1400. }
  1401. //--------------------------------------
  1402. S32 PASCAL WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR lpszCmdLine, S32)
  1403. {
  1404. Vector<char *> argv;
  1405. char moduleName[256];
  1406. GetModuleFileNameA(NULL, moduleName, sizeof(moduleName));
  1407. argv.push_back(moduleName);
  1408. for (const char* word,*ptr = lpszCmdLine; *ptr; ) {
  1409. // Eat white space
  1410. for (; dIsspace(*ptr) && *ptr; ptr++)
  1411. ;
  1412. // Pick out the next word
  1413. bool quote = false;
  1414. for (word = ptr; !(dIsspace(*ptr) && !quote) && *ptr; ptr++)
  1415. {
  1416. if(*ptr == '\"') quote = ! quote;
  1417. }
  1418. if(*word == '\"') ++word;
  1419. // Add the word to the argument list.
  1420. if (*word) {
  1421. int len = ptr - word;
  1422. if(*(ptr-1) == '\"') --len;
  1423. char *arg = (char *) dMalloc(len + 1);
  1424. dStrncpy(arg, word, len);
  1425. arg[len] = 0;
  1426. argv.push_back(arg);
  1427. }
  1428. }
  1429. winState.appInstance = hInstance;
  1430. S32 retVal = run(argv.size(), (const char **) argv.address());
  1431. for(U32 j = 1; j < (U32)argv.size(); j++)
  1432. dFree(argv[j]);
  1433. return retVal;
  1434. }
  1435. //--------------------------------------
  1436. void TimeManager::process()
  1437. {
  1438. if( winState.backgrounded )
  1439. winState.sleepTicks = Platform::getBackgroundSleepTime();
  1440. else
  1441. winState.sleepTicks = sgTimeManagerProcessInterval;
  1442. U32 elapsedTime = gTimer.getElapsedMS();
  1443. if( elapsedTime < winState.sleepTicks)
  1444. {
  1445. MsgWaitForMultipleObjects(0, NULL, false, winState.sleepTicks - elapsedTime, QS_ALLINPUT);
  1446. }
  1447. TimeEvent event;
  1448. event.elapsedTime = gTimer.getElapsedMS();
  1449. gTimer.advance();
  1450. Game->postEvent(event);
  1451. }
  1452. F32 Platform::getRandom()
  1453. {
  1454. return sgPlatRandom.randF();
  1455. }
  1456. ////--------------------------------------
  1457. /// Spawn the default Operating System web browser with a URL
  1458. /// @param webAddress URL to pass to browser
  1459. /// @return true if browser successfully spawned
  1460. bool Platform::openWebBrowser( const char* webAddress )
  1461. {
  1462. static bool sHaveKey = false;
  1463. static wchar_t sWebKey[512];
  1464. char utf8WebKey[512];
  1465. {
  1466. HKEY regKey;
  1467. DWORD size = sizeof( sWebKey );
  1468. if ( RegOpenKeyEx( HKEY_CLASSES_ROOT, dT("\\http\\shell\\open\\command"), 0, KEY_QUERY_VALUE, &regKey ) != ERROR_SUCCESS )
  1469. {
  1470. Con::errorf( ConsoleLogEntry::General, "Platform::openWebBrowser - Failed to open the HKCR\\http registry key!!!");
  1471. return( false );
  1472. }
  1473. if ( RegQueryValueEx( regKey, dT(""), NULL, NULL, (unsigned char *)sWebKey, &size ) != ERROR_SUCCESS )
  1474. {
  1475. Con::errorf( ConsoleLogEntry::General, "Platform::openWebBrowser - Failed to query the open command registry key!!!" );
  1476. return( false );
  1477. }
  1478. RegCloseKey( regKey );
  1479. sHaveKey = true;
  1480. convertUTF16toUTF8(sWebKey,utf8WebKey,512);
  1481. #ifdef UNICODE
  1482. char *p = dStrstr((const char *)utf8WebKey, "%1");
  1483. #else
  1484. char *p = dStrstr( (const char *) sWebKey , "%1");
  1485. #endif
  1486. if (p) *p = 0;
  1487. }
  1488. STARTUPINFO si;
  1489. dMemset( &si, 0, sizeof( si ) );
  1490. si.cb = sizeof( si );
  1491. char buf[1024];
  1492. #ifdef UNICODE
  1493. dSprintf( buf, sizeof( buf ), "%s %s", utf8WebKey, webAddress );
  1494. UTF16 szCommandLine[1024];
  1495. convertUTF8toUTF16((UTF8 *)buf, szCommandLine, sizeof(szCommandLine));
  1496. #else
  1497. UTF8 szCommandLine[1024];
  1498. dSprintf( szCommandLine, sizeof( szCommandLine ), "%s %s", sWebKey, webAddress );
  1499. #endif
  1500. //Con::errorf( ConsoleLogEntry::General, "** Web browser command = %s **", buf );
  1501. PROCESS_INFORMATION pi;
  1502. dMemset( &pi, 0, sizeof( pi ) );
  1503. CreateProcess( NULL,
  1504. szCommandLine,
  1505. NULL,
  1506. NULL,
  1507. false,
  1508. CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,
  1509. NULL,
  1510. NULL,
  1511. &si,
  1512. &pi );
  1513. return( true );
  1514. }
  1515. //--------------------------------------
  1516. // Platform functions for window menu
  1517. //--------------------------------------
  1518. // Creates the menu bar for the window
  1519. void CreateWin32MenuBar( void )
  1520. {
  1521. if( HasWin32MenuBar() )
  1522. {
  1523. DrawMenuBar( winState.appWindow );
  1524. return;
  1525. }
  1526. HMENU menu = CreateMenu();
  1527. if(menu)
  1528. {
  1529. winState.appMenu = menu;
  1530. SetMenu(winState.appWindow, menu);
  1531. DrawMenuBar(winState.appWindow);
  1532. }
  1533. }
  1534. void DestroyWin32MenuBar( void )
  1535. {
  1536. if( HasWin32MenuBar() )
  1537. DestroyMenu( winState.appMenu );
  1538. // Need to update platform window child rect?
  1539. DrawMenuBar(winState.appWindow);
  1540. }
  1541. bool HasWin32MenuBar( void )
  1542. {
  1543. return (bool)IsMenu( winState.appMenu );
  1544. }