windows_system.c 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844
  1. #define WIN32_LEAN_AND_MEAN
  2. #include "windows_system.h"
  3. #include <Windows.h>
  4. #include <Windowsx.h>
  5. #include <dwmapi.h>
  6. #include <iron_gpu.h>
  7. #include <iron_system.h>
  8. #include <iron_thread.h>
  9. #include <iron_video.h>
  10. #include <shellapi.h>
  11. #include <shlobj.h>
  12. #include <stdio.h>
  13. void iron_microsoft_format(const char *format, va_list args, wchar_t *buffer) {
  14. char cbuffer[4096];
  15. vsprintf(cbuffer, format, args);
  16. MultiByteToWideChar(CP_UTF8, 0, cbuffer, -1, buffer, 4096);
  17. }
  18. #ifdef IRON_NO_CLIB
  19. #ifndef NDEBUG
  20. void _wassert(wchar_t const *message, wchar_t const *filename, unsigned line) {
  21. __debugbreak();
  22. }
  23. void _RTC_CheckStackVars(void) {}
  24. void _RTC_InitBase(void) {}
  25. void _RTC_Shutdown(void) {}
  26. void _RTC_AllocaHelper(void) {}
  27. void _RTC_CheckStackVars2(void) {}
  28. void __GSHandlerCheck(void) {}
  29. void __fastcall __security_check_cookie(_In_ uintptr_t _StackCookie) {}
  30. uintptr_t __security_cookie;
  31. int _fltused = 1;
  32. void __report_rangecheckfailure(void) {}
  33. void __chkstk(void) {}
  34. #endif
  35. #endif
  36. #define MAXIMUM_DISPLAYS 8
  37. typedef struct {
  38. struct HMONITOR__ *monitor;
  39. char name[32];
  40. bool primary, available, mode_changed;
  41. int index, x, y, width, height, ppi, frequency, bpp;
  42. } DisplayData;
  43. static DisplayData displays[MAXIMUM_DISPLAYS];
  44. static DEVMODEA original_modes[MAXIMUM_DISPLAYS];
  45. static int screen_counter = 0;
  46. static bool display_initialized = false;
  47. typedef enum {
  48. MDT_EFFECTIVE_DPI = 0,
  49. MDT_ANGULAR_DPI = 1,
  50. MDT_RAW_DPI = 2,
  51. MDT_DEFAULT = MDT_EFFECTIVE_DPI
  52. } MONITOR_DPI_TYPE;
  53. typedef HRESULT(WINAPI *GetDpiForMonitorType)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT *dpiX, UINT *dpiY);
  54. static GetDpiForMonitorType MyGetDpiForMonitor = NULL;
  55. static BOOL CALLBACK EnumerationCallback(HMONITOR monitor, HDC hdc_unused, LPRECT rect_unused, LPARAM lparam) {
  56. MONITORINFOEXA info;
  57. memset(&info, 0, sizeof(MONITORINFOEXA));
  58. info.cbSize = sizeof(MONITORINFOEXA);
  59. if (GetMonitorInfoA(monitor, (MONITORINFO *)&info) == FALSE) {
  60. return FALSE;
  61. }
  62. int free_slot = 0;
  63. for (; free_slot < MAXIMUM_DISPLAYS; ++free_slot) {
  64. if (displays[free_slot].monitor == monitor) {
  65. return FALSE;
  66. }
  67. if (displays[free_slot].monitor == NULL) {
  68. break;
  69. }
  70. }
  71. DisplayData *display = &displays[free_slot];
  72. strncpy(display->name, info.szDevice, 31);
  73. display->name[31] = 0;
  74. display->index = free_slot;
  75. display->monitor = monitor;
  76. display->primary = (info.dwFlags & MONITORINFOF_PRIMARY) != 0;
  77. display->available = true;
  78. display->x = info.rcMonitor.left;
  79. display->y = info.rcMonitor.top;
  80. display->width = info.rcMonitor.right - info.rcMonitor.left;
  81. display->height = info.rcMonitor.bottom - info.rcMonitor.top;
  82. HDC hdc = CreateDCA(NULL, display->name, NULL, NULL);
  83. display->ppi = GetDeviceCaps(hdc, LOGPIXELSX);
  84. /* int scale =*/ GetDeviceCaps(hdc, SCALINGFACTORX);
  85. DeleteDC(hdc);
  86. if (MyGetDpiForMonitor != NULL) {
  87. unsigned dpiX, dpiY;
  88. MyGetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
  89. display->ppi = (int)dpiX;
  90. }
  91. memset(&original_modes[free_slot], 0, sizeof(DEVMODEA));
  92. original_modes[free_slot].dmSize = sizeof(DEVMODEA);
  93. EnumDisplaySettingsA(display->name, ENUM_CURRENT_SETTINGS, &original_modes[free_slot]);
  94. display->frequency = original_modes[free_slot].dmDisplayFrequency;
  95. display->bpp = original_modes[free_slot].dmBitsPerPel;
  96. ++screen_counter;
  97. return TRUE;
  98. }
  99. void iron_display_init() {
  100. if (display_initialized) {
  101. return;
  102. }
  103. HMODULE shcore = LoadLibraryA("Shcore.dll");
  104. if (shcore != NULL) {
  105. MyGetDpiForMonitor = (GetDpiForMonitorType)GetProcAddress(shcore, "GetDpiForMonitor");
  106. }
  107. memset(displays, 0, sizeof(DisplayData) * MAXIMUM_DISPLAYS);
  108. EnumDisplayMonitors(NULL, NULL, EnumerationCallback, 0);
  109. display_initialized = true;
  110. }
  111. int iron_windows_get_display_for_monitor(struct HMONITOR__ *monitor) {
  112. for (int i = 0; i < MAXIMUM_DISPLAYS; ++i) {
  113. if (displays[i].monitor == monitor) {
  114. return i;
  115. }
  116. }
  117. return -1;
  118. }
  119. int iron_count_displays() {
  120. return screen_counter;
  121. }
  122. int iron_primary_display() {
  123. for (int i = 0; i < MAXIMUM_DISPLAYS; ++i) {
  124. DisplayData *display = &displays[i];
  125. if (display->available && display->primary) {
  126. return i;
  127. }
  128. }
  129. return -1;
  130. }
  131. bool iron_windows_set_display_mode(int display_index, int width, int height, int bpp, int frequency) {
  132. DisplayData *display = &displays[display_index];
  133. display->mode_changed = true;
  134. DEVMODEA mode = {0};
  135. mode.dmSize = sizeof(mode);
  136. strcpy((char *)mode.dmDeviceName, display->name);
  137. mode.dmPelsWidth = width;
  138. mode.dmPelsHeight = height;
  139. mode.dmBitsPerPel = bpp;
  140. mode.dmDisplayFrequency = frequency;
  141. mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
  142. bool success = ChangeDisplaySettingsA(&mode, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL;
  143. if (!success) {
  144. mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
  145. success = ChangeDisplaySettingsA(&mode, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL;
  146. }
  147. return success;
  148. }
  149. void iron_windows_restore_display(int display) {
  150. if (displays[display].mode_changed) {
  151. ChangeDisplaySettingsA(&original_modes[display], 0);
  152. }
  153. }
  154. void iron_windows_restore_displays() {
  155. for (int i = 0; i < MAXIMUM_DISPLAYS; ++i) {
  156. iron_windows_restore_display(i);
  157. }
  158. }
  159. iron_display_mode_t iron_display_current_mode(int display_index) {
  160. DisplayData *display = &displays[display_index];
  161. iron_display_mode_t mode;
  162. mode.x = display->x;
  163. mode.y = display->y;
  164. mode.width = display->width;
  165. mode.height = display->height;
  166. mode.pixels_per_inch = display->ppi;
  167. mode.frequency = display->frequency;
  168. mode.bits_per_pixel = display->bpp;
  169. return mode;
  170. }
  171. bool iron_mouse_can_lock(void) {
  172. return true;
  173. }
  174. void iron_mouse_show() {
  175. // Work around the internal counter of ShowCursor
  176. while (ShowCursor(true) < 0) {
  177. }
  178. }
  179. void iron_mouse_hide() {
  180. // Work around the internal counter of ShowCursor
  181. while (ShowCursor(false) >= 0) {
  182. }
  183. }
  184. void iron_mouse_set_position(int x, int y) {
  185. POINT point;
  186. point.x = x;
  187. point.y = y;
  188. ClientToScreen(iron_windows_window_handle(), &point);
  189. SetCursorPos(point.x, point.y);
  190. }
  191. void iron_mouse_get_position(int *x, int *y) {
  192. POINT point;
  193. GetCursorPos(&point);
  194. ScreenToClient(iron_windows_window_handle(), &point);
  195. *x = point.x;
  196. *y = point.y;
  197. }
  198. #define MAX_TOUCH_POINTS 10
  199. #define IRON_DINPUT_MAX_COUNT 8
  200. struct touchpoint {
  201. int sysID;
  202. int x;
  203. int y;
  204. };
  205. static struct touchpoint touchPoints[MAX_TOUCH_POINTS];
  206. static int mouseX, mouseY;
  207. static bool keyPressed[256];
  208. static int keyTranslated[256]; // http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
  209. static int GetTouchIndex(int dwID) {
  210. for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
  211. if (touchPoints[i].sysID == dwID) {
  212. return i;
  213. }
  214. }
  215. return -1;
  216. }
  217. static int GetAddTouchIndex(int dwID) {
  218. for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
  219. if (touchPoints[i].sysID == dwID) {
  220. return i;
  221. }
  222. }
  223. for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
  224. if (touchPoints[i].sysID == -1) {
  225. touchPoints[i].sysID = dwID;
  226. return i;
  227. }
  228. }
  229. return -1;
  230. }
  231. static void ReleaseTouchIndex(int dwID) {
  232. for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
  233. if (touchPoints[i].sysID == dwID) {
  234. touchPoints[i].sysID = -1;
  235. touchPoints[i].x = -1;
  236. touchPoints[i].y = -1;
  237. }
  238. }
  239. }
  240. static void initKeyTranslation() {
  241. for (int i = 0; i < 256; ++i) {
  242. keyTranslated[i] = IRON_KEY_UNKNOWN;
  243. }
  244. keyTranslated[VK_BACK] = IRON_KEY_BACKSPACE;
  245. keyTranslated[VK_TAB] = IRON_KEY_TAB;
  246. keyTranslated[VK_CLEAR] = IRON_KEY_CLEAR;
  247. keyTranslated[VK_RETURN] = IRON_KEY_RETURN;
  248. keyTranslated[VK_SHIFT] = IRON_KEY_SHIFT;
  249. keyTranslated[VK_CONTROL] = IRON_KEY_CONTROL;
  250. keyTranslated[VK_MENU] = IRON_KEY_ALT;
  251. keyTranslated[VK_PAUSE] = IRON_KEY_PAUSE;
  252. keyTranslated[VK_CAPITAL] = IRON_KEY_CAPS_LOCK;
  253. keyTranslated[VK_KANA] = IRON_KEY_KANA;
  254. keyTranslated[VK_HANGUL] = IRON_KEY_HANGUL;
  255. keyTranslated[VK_JUNJA] = IRON_KEY_JUNJA;
  256. keyTranslated[VK_FINAL] = IRON_KEY_FINAL;
  257. keyTranslated[VK_HANJA] = IRON_KEY_HANJA;
  258. keyTranslated[VK_KANJI] = IRON_KEY_KANJI;
  259. keyTranslated[VK_ESCAPE] = IRON_KEY_ESCAPE;
  260. keyTranslated[VK_SPACE] = IRON_KEY_SPACE;
  261. keyTranslated[VK_PRIOR] = IRON_KEY_PAGE_UP;
  262. keyTranslated[VK_NEXT] = IRON_KEY_PAGE_DOWN;
  263. keyTranslated[VK_END] = IRON_KEY_END;
  264. keyTranslated[VK_HOME] = IRON_KEY_HOME;
  265. keyTranslated[VK_LEFT] = IRON_KEY_LEFT;
  266. keyTranslated[VK_UP] = IRON_KEY_UP;
  267. keyTranslated[VK_RIGHT] = IRON_KEY_RIGHT;
  268. keyTranslated[VK_DOWN] = IRON_KEY_DOWN;
  269. keyTranslated[VK_PRINT] = IRON_KEY_PRINT;
  270. keyTranslated[VK_INSERT] = IRON_KEY_INSERT;
  271. keyTranslated[VK_DELETE] = IRON_KEY_DELETE;
  272. keyTranslated[VK_HELP] = IRON_KEY_HELP;
  273. keyTranslated[0x30] = IRON_KEY_0;
  274. keyTranslated[0x31] = IRON_KEY_1;
  275. keyTranslated[0x32] = IRON_KEY_2;
  276. keyTranslated[0x33] = IRON_KEY_3;
  277. keyTranslated[0x34] = IRON_KEY_4;
  278. keyTranslated[0x35] = IRON_KEY_5;
  279. keyTranslated[0x36] = IRON_KEY_6;
  280. keyTranslated[0x37] = IRON_KEY_7;
  281. keyTranslated[0x38] = IRON_KEY_8;
  282. keyTranslated[0x39] = IRON_KEY_9;
  283. keyTranslated[0x41] = IRON_KEY_A;
  284. keyTranslated[0x42] = IRON_KEY_B;
  285. keyTranslated[0x43] = IRON_KEY_C;
  286. keyTranslated[0x44] = IRON_KEY_D;
  287. keyTranslated[0x45] = IRON_KEY_E;
  288. keyTranslated[0x46] = IRON_KEY_F;
  289. keyTranslated[0x47] = IRON_KEY_G;
  290. keyTranslated[0x48] = IRON_KEY_H;
  291. keyTranslated[0x49] = IRON_KEY_I;
  292. keyTranslated[0x4A] = IRON_KEY_J;
  293. keyTranslated[0x4B] = IRON_KEY_K;
  294. keyTranslated[0x4C] = IRON_KEY_L;
  295. keyTranslated[0x4D] = IRON_KEY_M;
  296. keyTranslated[0x4E] = IRON_KEY_N;
  297. keyTranslated[0x4F] = IRON_KEY_O;
  298. keyTranslated[0x50] = IRON_KEY_P;
  299. keyTranslated[0x51] = IRON_KEY_Q;
  300. keyTranslated[0x52] = IRON_KEY_R;
  301. keyTranslated[0x53] = IRON_KEY_S;
  302. keyTranslated[0x54] = IRON_KEY_T;
  303. keyTranslated[0x55] = IRON_KEY_U;
  304. keyTranslated[0x56] = IRON_KEY_V;
  305. keyTranslated[0x57] = IRON_KEY_W;
  306. keyTranslated[0x58] = IRON_KEY_X;
  307. keyTranslated[0x59] = IRON_KEY_Y;
  308. keyTranslated[0x5A] = IRON_KEY_Z;
  309. keyTranslated[VK_LWIN] = IRON_KEY_WIN;
  310. keyTranslated[VK_RWIN] = IRON_KEY_WIN;
  311. keyTranslated[VK_APPS] = IRON_KEY_CONTEXT_MENU;
  312. keyTranslated[VK_NUMPAD0] = IRON_KEY_NUMPAD_0;
  313. keyTranslated[VK_NUMPAD1] = IRON_KEY_NUMPAD_1;
  314. keyTranslated[VK_NUMPAD2] = IRON_KEY_NUMPAD_2;
  315. keyTranslated[VK_NUMPAD3] = IRON_KEY_NUMPAD_3;
  316. keyTranslated[VK_NUMPAD4] = IRON_KEY_NUMPAD_4;
  317. keyTranslated[VK_NUMPAD5] = IRON_KEY_NUMPAD_5;
  318. keyTranslated[VK_NUMPAD6] = IRON_KEY_NUMPAD_6;
  319. keyTranslated[VK_NUMPAD7] = IRON_KEY_NUMPAD_7;
  320. keyTranslated[VK_NUMPAD8] = IRON_KEY_NUMPAD_8;
  321. keyTranslated[VK_NUMPAD9] = IRON_KEY_NUMPAD_9;
  322. keyTranslated[VK_MULTIPLY] = IRON_KEY_MULTIPLY;
  323. keyTranslated[VK_ADD] = IRON_KEY_ADD;
  324. keyTranslated[VK_SUBTRACT] = IRON_KEY_SUBTRACT;
  325. keyTranslated[VK_DECIMAL] = IRON_KEY_DECIMAL;
  326. keyTranslated[VK_DIVIDE] = IRON_KEY_DIVIDE;
  327. keyTranslated[VK_F1] = IRON_KEY_F1;
  328. keyTranslated[VK_F2] = IRON_KEY_F2;
  329. keyTranslated[VK_F3] = IRON_KEY_F3;
  330. keyTranslated[VK_F4] = IRON_KEY_F4;
  331. keyTranslated[VK_F5] = IRON_KEY_F5;
  332. keyTranslated[VK_F6] = IRON_KEY_F6;
  333. keyTranslated[VK_F7] = IRON_KEY_F7;
  334. keyTranslated[VK_F8] = IRON_KEY_F8;
  335. keyTranslated[VK_F9] = IRON_KEY_F9;
  336. keyTranslated[VK_F10] = IRON_KEY_F10;
  337. keyTranslated[VK_F11] = IRON_KEY_F11;
  338. keyTranslated[VK_F12] = IRON_KEY_F12;
  339. keyTranslated[VK_F13] = IRON_KEY_F13;
  340. keyTranslated[VK_F14] = IRON_KEY_F14;
  341. keyTranslated[VK_F15] = IRON_KEY_F15;
  342. keyTranslated[VK_F16] = IRON_KEY_F16;
  343. keyTranslated[VK_F17] = IRON_KEY_F17;
  344. keyTranslated[VK_F18] = IRON_KEY_F18;
  345. keyTranslated[VK_F19] = IRON_KEY_F19;
  346. keyTranslated[VK_F20] = IRON_KEY_F20;
  347. keyTranslated[VK_F21] = IRON_KEY_F21;
  348. keyTranslated[VK_F22] = IRON_KEY_F22;
  349. keyTranslated[VK_F23] = IRON_KEY_F23;
  350. keyTranslated[VK_F24] = IRON_KEY_F24;
  351. keyTranslated[VK_NUMLOCK] = IRON_KEY_NUM_LOCK;
  352. keyTranslated[VK_SCROLL] = IRON_KEY_SCROLL_LOCK;
  353. keyTranslated[VK_LSHIFT] = IRON_KEY_SHIFT;
  354. keyTranslated[VK_RSHIFT] = IRON_KEY_SHIFT;
  355. keyTranslated[VK_LCONTROL] = IRON_KEY_CONTROL;
  356. keyTranslated[VK_RCONTROL] = IRON_KEY_CONTROL;
  357. keyTranslated[VK_OEM_1] = IRON_KEY_SEMICOLON;
  358. keyTranslated[VK_OEM_PLUS] = IRON_KEY_PLUS;
  359. keyTranslated[VK_OEM_COMMA] = IRON_KEY_COMMA;
  360. keyTranslated[VK_OEM_MINUS] = IRON_KEY_HYPHEN_MINUS;
  361. keyTranslated[VK_OEM_PERIOD] = IRON_KEY_PERIOD;
  362. keyTranslated[VK_OEM_2] = IRON_KEY_SLASH;
  363. keyTranslated[VK_OEM_3] = IRON_KEY_BACK_QUOTE;
  364. keyTranslated[VK_OEM_4] = IRON_KEY_OPEN_BRACKET;
  365. keyTranslated[VK_OEM_5] = IRON_KEY_BACK_SLASH;
  366. keyTranslated[VK_OEM_6] = IRON_KEY_CLOSE_BRACKET;
  367. keyTranslated[VK_OEM_7] = IRON_KEY_QUOTE;
  368. }
  369. #ifdef WITH_GAMEPAD
  370. static bool detectGamepad = true;
  371. static bool gamepadFound = false;
  372. #endif
  373. #define HANDLE_ALT_ENTER
  374. static bool cursors_initialized = false;
  375. static int cursor = 0;
  376. static HCURSOR cursors[5];
  377. static bool bg_erased = false;
  378. void iron_mouse_set_cursor(iron_cursor_t set_cursor) {
  379. cursor = set_cursor;
  380. if (cursors_initialized) {
  381. SetCursor(cursors[cursor]);
  382. }
  383. // Set hand icon for drag even when mouse button is pressed
  384. if (set_cursor == IRON_CURSOR_HAND) {
  385. SetCursor(LoadCursor(NULL, IDC_HAND));
  386. }
  387. }
  388. LRESULT WINAPI IronWindowsMessageProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
  389. DWORD pointerId;
  390. POINTER_INFO pointerInfo = {0};
  391. POINTER_PEN_INFO penInfo = {0};
  392. static bool controlDown = false;
  393. #ifdef HANDLE_ALT_ENTER
  394. static bool altDown = false;
  395. #endif
  396. static int last_window_width = -1;
  397. static int last_window_height = -1;
  398. static int last_window_x = INT_MIN;
  399. static int last_window_y = INT_MIN;
  400. switch (msg) {
  401. case WM_NCCREATE:
  402. EnableNonClientDpiScaling(hWnd);
  403. break;
  404. case WM_DPICHANGED: {
  405. break;
  406. }
  407. case WM_MOVE:
  408. case WM_MOVING:
  409. case WM_SIZING:
  410. // Scheduler::breakTime();
  411. break;
  412. case WM_SIZE: {
  413. int width = LOWORD(lParam);
  414. int height = HIWORD(lParam);
  415. gpu_resize(width, height);
  416. iron_internal_call_resize_callback(width, height);
  417. break;
  418. }
  419. case WM_CLOSE: {
  420. if (iron_internal_call_close_callback()) {
  421. iron_window_destroy();
  422. iron_stop();
  423. return 0;
  424. }
  425. return 0;
  426. }
  427. case WM_ERASEBKGND: {
  428. if (bg_erased) {
  429. return 1;
  430. }
  431. bg_erased = true;
  432. }
  433. case WM_ACTIVATE:
  434. if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) {
  435. iron_internal_mouse_window_activated();
  436. iron_internal_foreground_callback();
  437. }
  438. else {
  439. iron_internal_mouse_window_deactivated();
  440. iron_internal_background_callback();
  441. #ifdef HANDLE_ALT_ENTER
  442. altDown = false;
  443. #endif
  444. }
  445. RegisterTouchWindow(hWnd, 0);
  446. break;
  447. case WM_MOUSELEAVE:
  448. break;
  449. case WM_MOUSEMOVE:
  450. mouseX = GET_X_LPARAM(lParam);
  451. mouseY = GET_Y_LPARAM(lParam);
  452. iron_internal_mouse_trigger_move(mouseX, mouseY);
  453. break;
  454. case WM_CREATE:
  455. cursors[0] = LoadCursor(0, IDC_ARROW);
  456. cursors[1] = LoadCursor(0, IDC_HAND);
  457. cursors[2] = LoadCursor(0, IDC_IBEAM);
  458. cursors[3] = LoadCursor(0, IDC_SIZEWE);
  459. cursors[4] = LoadCursor(0, IDC_SIZENS);
  460. cursors_initialized = true;
  461. return TRUE;
  462. case WM_SETCURSOR:
  463. if (LOWORD(lParam) == HTCLIENT) {
  464. SetCursor(cursors[cursor]);
  465. return TRUE;
  466. }
  467. break;
  468. case WM_LBUTTONDOWN:
  469. if (!iron_mouse_is_locked())
  470. SetCapture(hWnd);
  471. mouseX = GET_X_LPARAM(lParam);
  472. mouseY = GET_Y_LPARAM(lParam);
  473. iron_internal_mouse_trigger_press(0, mouseX, mouseY);
  474. break;
  475. case WM_LBUTTONUP:
  476. if (!iron_mouse_is_locked())
  477. ReleaseCapture();
  478. mouseX = GET_X_LPARAM(lParam);
  479. mouseY = GET_Y_LPARAM(lParam);
  480. iron_internal_mouse_trigger_release(0, mouseX, mouseY);
  481. break;
  482. case WM_RBUTTONDOWN:
  483. mouseX = GET_X_LPARAM(lParam);
  484. mouseY = GET_Y_LPARAM(lParam);
  485. iron_internal_mouse_trigger_press(1, mouseX, mouseY);
  486. break;
  487. case WM_RBUTTONUP:
  488. mouseX = GET_X_LPARAM(lParam);
  489. mouseY = GET_Y_LPARAM(lParam);
  490. iron_internal_mouse_trigger_release(1, mouseX, mouseY);
  491. break;
  492. case WM_MBUTTONDOWN:
  493. mouseX = GET_X_LPARAM(lParam);
  494. mouseY = GET_Y_LPARAM(lParam);
  495. iron_internal_mouse_trigger_press(2, mouseX, mouseY);
  496. break;
  497. case WM_MBUTTONUP:
  498. mouseX = GET_X_LPARAM(lParam);
  499. mouseY = GET_Y_LPARAM(lParam);
  500. iron_internal_mouse_trigger_release(2, mouseX, mouseY);
  501. break;
  502. case WM_XBUTTONDOWN:
  503. mouseX = GET_X_LPARAM(lParam);
  504. mouseY = GET_Y_LPARAM(lParam);
  505. iron_internal_mouse_trigger_press(HIWORD(wParam) + 2, mouseX, mouseY);
  506. break;
  507. case WM_XBUTTONUP:
  508. mouseX = GET_X_LPARAM(lParam);
  509. mouseY = GET_Y_LPARAM(lParam);
  510. iron_internal_mouse_trigger_release(HIWORD(wParam) + 2, mouseX, mouseY);
  511. break;
  512. case WM_MOUSEWHEEL:
  513. iron_internal_mouse_trigger_scroll(GET_WHEEL_DELTA_WPARAM(wParam) / -120.0);
  514. break;
  515. case WM_POINTERDOWN:
  516. pointerId = GET_POINTERID_WPARAM(wParam);
  517. GetPointerInfo(pointerId, &pointerInfo);
  518. if (pointerInfo.pointerType == PT_PEN) {
  519. GetPointerPenInfo(pointerId, &penInfo);
  520. ScreenToClient(hWnd, &pointerInfo.ptPixelLocation);
  521. iron_internal_pen_trigger_press(pointerInfo.ptPixelLocation.x, pointerInfo.ptPixelLocation.y, penInfo.pressure / 1024.0f);
  522. }
  523. break;
  524. case WM_POINTERUP:
  525. pointerId = GET_POINTERID_WPARAM(wParam);
  526. GetPointerInfo(pointerId, &pointerInfo);
  527. if (pointerInfo.pointerType == PT_PEN) {
  528. GetPointerPenInfo(pointerId, &penInfo);
  529. ScreenToClient(hWnd, &pointerInfo.ptPixelLocation);
  530. iron_internal_pen_trigger_release(pointerInfo.ptPixelLocation.x, pointerInfo.ptPixelLocation.y, penInfo.pressure / 1024.0f);
  531. }
  532. break;
  533. case WM_POINTERUPDATE:
  534. pointerId = GET_POINTERID_WPARAM(wParam);
  535. GetPointerInfo(pointerId, &pointerInfo);
  536. if (pointerInfo.pointerType == PT_PEN) {
  537. GetPointerPenInfo(pointerId, &penInfo);
  538. ScreenToClient(hWnd, &pointerInfo.ptPixelLocation);
  539. iron_internal_pen_trigger_move(pointerInfo.ptPixelLocation.x, pointerInfo.ptPixelLocation.y, penInfo.pressure / 1024.0f);
  540. }
  541. break;
  542. case WM_TOUCH: {
  543. BOOL bHandled = FALSE;
  544. UINT cInputs = LOWORD(wParam);
  545. PTOUCHINPUT pInputs = _alloca(cInputs * sizeof(TOUCHINPUT));
  546. POINT ptInput;
  547. int tindex;
  548. if (pInputs) {
  549. if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))) {
  550. for (int i = 0; i < (int)cInputs; i++) {
  551. TOUCHINPUT ti = pInputs[i];
  552. if (ti.dwID != 0) {
  553. ptInput.x = TOUCH_COORD_TO_PIXEL(ti.x);
  554. ptInput.y = TOUCH_COORD_TO_PIXEL(ti.y);
  555. ScreenToClient(hWnd, &ptInput);
  556. if (ti.dwFlags & TOUCHEVENTF_UP) {
  557. tindex = GetTouchIndex(ti.dwID);
  558. ReleaseTouchIndex(ti.dwID);
  559. iron_internal_surface_trigger_touch_end(tindex, ptInput.x, ptInput.y);
  560. }
  561. else {
  562. bool touchExisits = GetTouchIndex(ti.dwID) != -1;
  563. tindex = GetAddTouchIndex(ti.dwID);
  564. if (tindex >= 0) {
  565. if (touchExisits) {
  566. if (touchPoints[tindex].x != ptInput.x || touchPoints[tindex].y != ptInput.y) {
  567. touchPoints[tindex].x = ptInput.x;
  568. touchPoints[tindex].y = ptInput.y;
  569. iron_internal_surface_trigger_move(tindex, ptInput.x, ptInput.y);
  570. }
  571. }
  572. else {
  573. touchPoints[tindex].x = ptInput.x;
  574. touchPoints[tindex].y = ptInput.y;
  575. iron_internal_surface_trigger_touch_start(tindex, ptInput.x, ptInput.y);
  576. }
  577. }
  578. }
  579. }
  580. bHandled = TRUE;
  581. if (!CloseTouchInputHandle((HTOUCHINPUT)lParam)) {
  582. }
  583. }
  584. }
  585. }
  586. if (bHandled)
  587. CloseTouchInputHandle((HTOUCHINPUT)lParam);
  588. else
  589. DefWindowProcW(hWnd, WM_TOUCH, wParam, lParam);
  590. InvalidateRect(hWnd, NULL, FALSE);
  591. } break;
  592. case WM_KEYDOWN:
  593. case WM_SYSKEYDOWN:
  594. if (!keyPressed[wParam]) {
  595. keyPressed[wParam] = true;
  596. if (keyTranslated[wParam] == IRON_KEY_CONTROL) {
  597. controlDown = true;
  598. }
  599. #ifdef HANDLE_ALT_ENTER
  600. else if (keyTranslated[wParam] == IRON_KEY_ALT) {
  601. altDown = true;
  602. }
  603. #endif
  604. else {
  605. if (controlDown && keyTranslated[wParam] == IRON_KEY_X) {
  606. char *text = iron_internal_cut_callback();
  607. if (text != NULL) {
  608. wchar_t wtext[4096];
  609. MultiByteToWideChar(CP_UTF8, 0, text, -1, wtext, 4096);
  610. OpenClipboard(hWnd);
  611. EmptyClipboard();
  612. size_t size = (wcslen(wtext) + 1) * sizeof(wchar_t);
  613. HANDLE handle = GlobalAlloc(GMEM_MOVEABLE, size);
  614. void *data = GlobalLock(handle);
  615. memcpy(data, wtext, size);
  616. GlobalUnlock(handle);
  617. SetClipboardData(CF_UNICODETEXT, handle);
  618. CloseClipboard();
  619. }
  620. }
  621. if (controlDown && keyTranslated[wParam] == IRON_KEY_C) {
  622. char *text = iron_internal_copy_callback();
  623. if (text != NULL) {
  624. iron_copy_to_clipboard(text);
  625. }
  626. }
  627. if (controlDown && keyTranslated[wParam] == IRON_KEY_V) {
  628. if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
  629. OpenClipboard(hWnd);
  630. HANDLE handle = GetClipboardData(CF_UNICODETEXT);
  631. if (handle != NULL) {
  632. wchar_t *wtext = (wchar_t *)GlobalLock(handle);
  633. if (wtext != NULL) {
  634. char text[4096];
  635. WideCharToMultiByte(CP_UTF8, 0, wtext, -1, text, 4096, NULL, NULL);
  636. iron_internal_paste_callback(text);
  637. GlobalUnlock(handle);
  638. }
  639. }
  640. CloseClipboard();
  641. }
  642. }
  643. #ifdef HANDLE_ALT_ENTER
  644. if (altDown && keyTranslated[wParam] == IRON_KEY_RETURN) {
  645. if (iron_window_get_mode() == IRON_WINDOW_MODE_WINDOW) {
  646. last_window_width = iron_window_width();
  647. last_window_height = iron_window_height();
  648. last_window_x = iron_window_x();
  649. last_window_y = iron_window_y();
  650. iron_window_change_mode(IRON_WINDOW_MODE_FULLSCREEN);
  651. }
  652. else {
  653. iron_window_change_mode(IRON_WINDOW_MODE_WINDOW);
  654. if (last_window_width > 0 && last_window_height > 0) {
  655. iron_window_resize(last_window_width, last_window_height);
  656. }
  657. if (last_window_x > INT_MIN && last_window_y > INT_MIN) {
  658. iron_window_move(last_window_x, last_window_y);
  659. }
  660. }
  661. }
  662. #endif
  663. }
  664. // No auto-repeat
  665. iron_internal_keyboard_trigger_key_down(keyTranslated[wParam]);
  666. }
  667. // Auto-repeat
  668. // iron_internal_keyboard_trigger_key_down(keyTranslated[wParam]);
  669. break;
  670. case WM_KEYUP:
  671. case WM_SYSKEYUP:
  672. keyPressed[wParam] = false;
  673. if (keyTranslated[wParam] == IRON_KEY_CONTROL) {
  674. controlDown = false;
  675. }
  676. #ifdef HANDLE_ALT_ENTER
  677. if (keyTranslated[wParam] == IRON_KEY_ALT) {
  678. altDown = false;
  679. }
  680. #endif
  681. iron_internal_keyboard_trigger_key_up(keyTranslated[wParam]);
  682. break;
  683. case WM_CHAR:
  684. switch (wParam) {
  685. case 0x1B: // escape
  686. break;
  687. default:
  688. iron_internal_keyboard_trigger_key_press((unsigned)wParam);
  689. break;
  690. }
  691. break;
  692. case WM_SYSCOMMAND:
  693. switch (wParam) {
  694. case SC_KEYMENU:
  695. return 0;
  696. case SC_SCREENSAVE:
  697. case SC_MONITORPOWER:
  698. return 0;
  699. case SC_MINIMIZE:
  700. break;
  701. case SC_RESTORE:
  702. case SC_MAXIMIZE:
  703. break;
  704. }
  705. break;
  706. case WM_DEVICECHANGE:
  707. #ifdef WITH_GAMEPAD
  708. detectGamepad = true;
  709. #endif
  710. break;
  711. case WM_DROPFILES: {
  712. HDROP hDrop = (HDROP)wParam;
  713. unsigned count = DragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0);
  714. for (unsigned i = 0; i < count; ++i) {
  715. wchar_t filePath[260];
  716. if (DragQueryFileW(hDrop, i, filePath, 260)) {
  717. char buffer[1024];
  718. WideCharToMultiByte(CP_UTF8, 0, filePath, wcslen(filePath) + 1, buffer, sizeof(buffer), NULL, NULL);
  719. iron_internal_drop_files_callback(buffer);
  720. }
  721. }
  722. DragFinish(hDrop);
  723. break;
  724. }
  725. }
  726. return DefWindowProcW(hWnd, msg, wParam, lParam);
  727. }
  728. bool iron_internal_handle_messages() {
  729. MSG message;
  730. while (PeekMessageW(&message, 0, 0, 0, PM_REMOVE)) {
  731. TranslateMessage(&message);
  732. DispatchMessageW(&message);
  733. }
  734. #ifdef WITH_GAMEPAD
  735. iron_gamepad_handle_messages();
  736. #endif
  737. return true;
  738. }
  739. static bool keyboardshown = false;
  740. static char language[3] = {0};
  741. void iron_keyboard_show() {
  742. keyboardshown = true;
  743. }
  744. void iron_keyboard_hide() {
  745. keyboardshown = false;
  746. }
  747. bool iron_keyboard_active() {
  748. return keyboardshown;
  749. }
  750. void iron_load_url(const char *url) {
  751. if (strncmp(url, "http://", sizeof("http://") - 1) == 0 || strncmp(url, "https://", sizeof("https://") - 1) == 0) {
  752. wchar_t wurl[1024];
  753. MultiByteToWideChar(CP_UTF8, 0, url, -1, wurl, 1024);
  754. INT_PTR ret = (INT_PTR)ShellExecuteW(NULL, L"open", wurl, NULL, NULL, SW_SHOWNORMAL);
  755. if (ret <= 32) {
  756. iron_log("Error opening url %s", url);
  757. }
  758. }
  759. }
  760. void iron_set_keep_screen_on(bool on) {}
  761. const char *iron_language() {
  762. wchar_t wlanguage[3] = {0};
  763. if (GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SISO639LANGNAME, wlanguage, 3)) {
  764. WideCharToMultiByte(CP_UTF8, 0, wlanguage, -1, language, 3, NULL, NULL);
  765. return language;
  766. }
  767. return "en";
  768. }
  769. const char *iron_system_id() {
  770. return "Windows";
  771. }
  772. static bool co_initialized = false;
  773. void iron_windows_co_initialize(void) {
  774. if (!co_initialized) {
  775. CoInitializeEx(0, COINIT_MULTITHREADED);
  776. co_initialized = true;
  777. }
  778. }
  779. static wchar_t savePathw[2048] = {0};
  780. static char savePath[2048] = {0};
  781. static void findSavePath() {
  782. iron_windows_co_initialize();
  783. IKnownFolderManager *folders = NULL;
  784. CoCreateInstance(&CLSID_KnownFolderManager, NULL, CLSCTX_INPROC_SERVER, &IID_IKnownFolderManager, (LPVOID *)&folders);
  785. IKnownFolder *folder = NULL;
  786. folders->lpVtbl->GetFolder(folders, &FOLDERID_SavedGames, &folder);
  787. LPWSTR path;
  788. folder->lpVtbl->GetPath(folder, 0, &path);
  789. wcscpy(savePathw, path);
  790. wcscat(savePathw, L"\\");
  791. wchar_t name[1024];
  792. MultiByteToWideChar(CP_UTF8, 0, iron_application_name(), -1, name, 1024);
  793. wcscat(savePathw, name);
  794. wcscat(savePathw, L"\\");
  795. SHCreateDirectoryExW(NULL, savePathw, NULL);
  796. WideCharToMultiByte(CP_UTF8, 0, savePathw, -1, savePath, 1024, NULL, NULL);
  797. CoTaskMemFree(path);
  798. folder->lpVtbl->Release(folder);
  799. folders->lpVtbl->Release(folders);
  800. }
  801. const char *iron_internal_save_path() {
  802. if (savePath[0] == 0)
  803. findSavePath();
  804. return savePath;
  805. }
  806. static const char *videoFormats[] = {"ogv", NULL};
  807. static LARGE_INTEGER frequency;
  808. static LARGE_INTEGER startCount;
  809. const char **iron_video_formats() {
  810. return videoFormats;
  811. }
  812. double iron_frequency() {
  813. return (double)frequency.QuadPart;
  814. }
  815. uint64_t iron_timestamp(void) {
  816. LARGE_INTEGER stamp;
  817. QueryPerformanceCounter(&stamp);
  818. return stamp.QuadPart - startCount.QuadPart;
  819. }
  820. double iron_time(void) {
  821. LARGE_INTEGER stamp;
  822. QueryPerformanceCounter(&stamp);
  823. return (double)(stamp.QuadPart - startCount.QuadPart) / (double)frequency.QuadPart;
  824. }
  825. #if !defined(IRON_NO_MAIN) && !defined(IRON_NO_CLIB)
  826. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
  827. int ret = kickstart(__argc, __argv);
  828. if (ret != 0) {
  829. #ifdef NDEBUG
  830. MessageBox(0, L"Unknown Error", L"Error", MB_OK);
  831. #else
  832. __debugbreak();
  833. #endif
  834. }
  835. return ret;
  836. }
  837. #endif
  838. void iron_init(iron_window_options_t *ops) {
  839. initKeyTranslation();
  840. for (int i = 0; i < 256; ++i) {
  841. keyPressed[i] = false;
  842. }
  843. for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
  844. touchPoints[i].sysID = -1;
  845. touchPoints[i].x = -1;
  846. touchPoints[i].y = -1;
  847. }
  848. iron_display_init();
  849. QueryPerformanceCounter(&startCount);
  850. QueryPerformanceFrequency(&frequency);
  851. for (int i = 0; i < 256; ++i) {
  852. keyPressed[i] = false;
  853. }
  854. iron_set_app_name(ops->title);
  855. iron_window_create(ops);
  856. // Maximized window has x < -1, prevent window centering
  857. if (ops->x < -1 && ops->y < -1) {
  858. iron_window_move(ops->x, ops->y);
  859. }
  860. #ifdef WITH_GAMEPAD
  861. loadXInput();
  862. initializeDirectInput();
  863. #endif
  864. }
  865. void iron_internal_shutdown() {
  866. iron_windows_hide_windows();
  867. iron_internal_shutdown_callback();
  868. iron_windows_destroy_windows();
  869. gpu_destroy();
  870. iron_windows_restore_displays();
  871. }
  872. void iron_copy_to_clipboard(const char *text) {
  873. wchar_t wtext[4096];
  874. MultiByteToWideChar(CP_UTF8, 0, text, -1, wtext, 4096);
  875. OpenClipboard(iron_windows_window_handle());
  876. EmptyClipboard();
  877. size_t size = (wcslen(wtext) + 1) * sizeof(wchar_t);
  878. HANDLE handle = GlobalAlloc(GMEM_MOVEABLE, size);
  879. void *data = GlobalLock(handle);
  880. memcpy(data, wtext, size);
  881. GlobalUnlock(handle);
  882. SetClipboardData(CF_UNICODETEXT, handle);
  883. CloseClipboard();
  884. }
  885. int iron_hardware_threads(void) {
  886. SYSTEM_INFO sysinfo;
  887. GetSystemInfo(&sysinfo);
  888. return sysinfo.dwNumberOfProcessors;
  889. }
  890. struct HWND__;
  891. typedef unsigned long DWORD;
  892. // Dark mode
  893. #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
  894. #define DWMWA_USE_IMMERSIVE_DARK_MODE 20
  895. #endif
  896. struct HWND__ *iron_windows_window_handle();
  897. // Enable visual styles for ui controls
  898. #pragma comment( \
  899. linker, \
  900. "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
  901. typedef struct {
  902. struct HWND__ *handle;
  903. int display_index;
  904. bool mouseInside;
  905. int index;
  906. int x, y, mode, bpp, frequency, features;
  907. int manualWidth, manualHeight;
  908. void (*resizeCallback)(int x, int y, void *data);
  909. void *resizeCallbackData;
  910. bool (*closeCallback)(void *data);
  911. void *closeCallbackData;
  912. } WindowData;
  913. LRESULT WINAPI IronWindowsMessageProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
  914. static WindowData windows[1] = {0};
  915. const wchar_t *windowClassName = L"IronWindow";
  916. static void RegisterWindowClass(HINSTANCE hInstance, const wchar_t *className) {
  917. WNDCLASSEXW wc = {sizeof(WNDCLASSEXA),
  918. CS_OWNDC /*CS_CLASSDC*/,
  919. IronWindowsMessageProcedure,
  920. 0L,
  921. 0L,
  922. hInstance,
  923. LoadIconW(hInstance, MAKEINTRESOURCEW(107)),
  924. LoadCursor(NULL, IDC_ARROW),
  925. CreateSolidBrush(0x00202020),
  926. 0,
  927. className,
  928. 0};
  929. RegisterClassExW(&wc);
  930. }
  931. static DWORD getStyle(int features) {
  932. DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP;
  933. if ((features & IRON_WINDOW_FEATURE_RESIZEABLE) && ((features & IRON_WINDOW_FEATURE_BORDERLESS) == 0)) {
  934. style |= WS_SIZEBOX;
  935. }
  936. if (features & IRON_WINDOW_FEATURE_MAXIMIZABLE) {
  937. style |= WS_MAXIMIZEBOX;
  938. }
  939. if (features & IRON_WINDOW_FEATURE_MINIMIZABLE) {
  940. style |= WS_MINIMIZEBOX;
  941. }
  942. if ((features & IRON_WINDOW_FEATURE_BORDERLESS) == 0) {
  943. style |= WS_CAPTION | WS_SYSMENU;
  944. }
  945. return style;
  946. }
  947. static DWORD getExStyle(int features) {
  948. DWORD exStyle = WS_EX_APPWINDOW;
  949. if ((features & IRON_WINDOW_FEATURE_BORDERLESS) == 0) {
  950. exStyle |= WS_EX_WINDOWEDGE;
  951. }
  952. if (features & IRON_WINDOW_FEATURE_ON_TOP) {
  953. exStyle |= WS_EX_TOPMOST;
  954. }
  955. return exStyle;
  956. }
  957. int iron_window_x() {
  958. RECT rect;
  959. GetWindowRect(windows[0].handle, &rect);
  960. windows[0].x = rect.left;
  961. return windows[0].x;
  962. }
  963. int iron_window_y() {
  964. RECT rect;
  965. GetWindowRect(windows[0].handle, &rect);
  966. windows[0].y = rect.top;
  967. return windows[0].y;
  968. }
  969. int iron_window_width() {
  970. RECT rect;
  971. GetClientRect(windows[0].handle, &rect);
  972. return rect.right;
  973. }
  974. int iron_window_height() {
  975. RECT rect;
  976. GetClientRect(windows[0].handle, &rect);
  977. return rect.bottom;
  978. }
  979. static DWORD getDwStyle(iron_window_mode_t mode, int features) {
  980. switch (mode) {
  981. case IRON_WINDOW_MODE_FULLSCREEN:
  982. return WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP;
  983. case IRON_WINDOW_MODE_WINDOW:
  984. default:
  985. return getStyle(features);
  986. }
  987. }
  988. static DWORD getDwExStyle(iron_window_mode_t mode, int features) {
  989. switch (mode) {
  990. case IRON_WINDOW_MODE_FULLSCREEN:
  991. return WS_EX_APPWINDOW;
  992. case IRON_WINDOW_MODE_WINDOW:
  993. default:
  994. return getExStyle(features);
  995. }
  996. }
  997. static void createWindow(const wchar_t *title, int x, int y, int width, int height, int bpp, int frequency, int features, iron_window_mode_t windowMode,
  998. int target_display_index) {
  999. HINSTANCE inst = GetModuleHandleW(NULL);
  1000. RegisterWindowClass(inst, windowClassName);
  1001. int display_index = target_display_index == -1 ? iron_primary_display() : target_display_index;
  1002. RECT WindowRect;
  1003. WindowRect.left = 0;
  1004. WindowRect.right = width;
  1005. WindowRect.top = 0;
  1006. WindowRect.bottom = height;
  1007. AdjustWindowRectEx(&WindowRect, getDwStyle(windowMode, features), FALSE, getDwExStyle(windowMode, features));
  1008. iron_display_mode_t display_mode = iron_display_current_mode(display_index);
  1009. int dstx = display_mode.x;
  1010. int dsty = display_mode.y;
  1011. int dstw = width;
  1012. int dsth = height;
  1013. switch (windowMode) {
  1014. case IRON_WINDOW_MODE_WINDOW:
  1015. dstx += x < 0 ? (display_mode.width - width) / 2 : x;
  1016. dsty += y < 0 ? (display_mode.height - height) / 2 : y;
  1017. dstw = WindowRect.right - WindowRect.left;
  1018. dsth = WindowRect.bottom - WindowRect.top;
  1019. break;
  1020. case IRON_WINDOW_MODE_FULLSCREEN:
  1021. dstw = display_mode.width;
  1022. dsth = display_mode.height;
  1023. break;
  1024. }
  1025. HWND hwnd = CreateWindowExW(getDwExStyle(windowMode, features), windowClassName, title, getDwStyle(windowMode, features), dstx, dsty, dstw, dsth, NULL,
  1026. NULL, inst, NULL);
  1027. SetCursor(LoadCursor(NULL, IDC_ARROW));
  1028. DragAcceptFiles(hwnd, true);
  1029. windows[0].handle = hwnd;
  1030. windows[0].x = dstx;
  1031. windows[0].y = dsty;
  1032. windows[0].mode = windowMode;
  1033. windows[0].display_index = display_index;
  1034. windows[0].bpp = bpp;
  1035. windows[0].frequency = frequency;
  1036. windows[0].features = features;
  1037. windows[0].manualWidth = width;
  1038. windows[0].manualHeight = height;
  1039. // Dark mode
  1040. char vdata[4];
  1041. DWORD cbdata = 4 * sizeof(char);
  1042. RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", L"AppsUseLightTheme", RRF_RT_REG_DWORD, NULL, vdata,
  1043. &cbdata);
  1044. BOOL use_dark_mode = (int)(vdata[3] << 24 | vdata[2] << 16 | vdata[1] << 8 | vdata[0]) != 1;
  1045. DwmSetWindowAttribute(iron_windows_window_handle(), DWMWA_USE_IMMERSIVE_DARK_MODE, &use_dark_mode, sizeof(use_dark_mode));
  1046. }
  1047. void iron_window_resize(int width, int height) {
  1048. WindowData *win = &windows[0];
  1049. win->manualWidth = width;
  1050. win->manualHeight = height;
  1051. switch (win->mode) {
  1052. case IRON_WINDOW_MODE_WINDOW: {
  1053. RECT rect;
  1054. rect.left = 0;
  1055. rect.top = 0;
  1056. rect.right = width;
  1057. rect.bottom = height;
  1058. AdjustWindowRectEx(&rect, getDwStyle((iron_window_mode_t)win->mode, win->features), FALSE, getDwExStyle((iron_window_mode_t)win->mode, win->features));
  1059. SetWindowPos(win->handle, NULL, iron_window_x(), iron_window_y(), rect.right - rect.left, rect.bottom - rect.top, 0);
  1060. break;
  1061. }
  1062. }
  1063. }
  1064. void iron_window_move(int x, int y) {
  1065. WindowData *win = &windows[0];
  1066. if (win->mode != 0) {
  1067. return;
  1068. }
  1069. win->x = x;
  1070. win->y = y;
  1071. RECT rect;
  1072. rect.left = 0;
  1073. rect.top = 0;
  1074. rect.right = iron_window_width();
  1075. rect.bottom = iron_window_height();
  1076. AdjustWindowRectEx(&rect, getDwStyle((iron_window_mode_t)win->mode, win->features), FALSE, getDwExStyle((iron_window_mode_t)win->mode, win->features));
  1077. SetWindowPos(win->handle, NULL, x, y, rect.right - rect.left, rect.bottom - rect.top, 0);
  1078. }
  1079. void iron_window_change_mode(iron_window_mode_t mode) {
  1080. WindowData *win = &windows[0];
  1081. int display_index = iron_window_display();
  1082. iron_display_mode_t display_mode = iron_display_current_mode(display_index);
  1083. switch (mode) {
  1084. case IRON_WINDOW_MODE_WINDOW: {
  1085. iron_windows_restore_display(display_index);
  1086. SetWindowLongW(win->handle, GWL_STYLE, getStyle(win->features));
  1087. SetWindowLongW(win->handle, GWL_EXSTYLE, getExStyle(win->features));
  1088. HWND on_top = (win->features & IRON_WINDOW_FEATURE_ON_TOP) ? HWND_TOPMOST : HWND_NOTOPMOST;
  1089. SetWindowPos(win->handle, on_top, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
  1090. iron_window_show();
  1091. break;
  1092. }
  1093. case IRON_WINDOW_MODE_FULLSCREEN: {
  1094. iron_windows_restore_display(display_index);
  1095. SetWindowLongW(win->handle, GWL_STYLE, WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP);
  1096. SetWindowLongW(win->handle, GWL_EXSTYLE, WS_EX_APPWINDOW);
  1097. SetWindowPos(win->handle, NULL, display_mode.x, display_mode.y, display_mode.width, display_mode.height, 0);
  1098. iron_window_show();
  1099. break;
  1100. }
  1101. }
  1102. win->mode = mode;
  1103. DragAcceptFiles(win->handle, true);
  1104. }
  1105. iron_window_mode_t iron_window_get_mode() {
  1106. return (iron_window_mode_t)windows[0].mode;
  1107. }
  1108. void iron_window_destroy() {
  1109. WindowData *win = &windows[0];
  1110. if (win->handle != NULL) {
  1111. DestroyWindow(win->handle);
  1112. win->handle = NULL;
  1113. }
  1114. }
  1115. void iron_windows_hide_windows(void) {
  1116. for (int i = 0; i < 1; ++i) {
  1117. if (windows[i].handle != NULL) {
  1118. ShowWindow(windows[i].handle, SW_HIDE);
  1119. UpdateWindow(windows[i].handle);
  1120. }
  1121. }
  1122. }
  1123. void iron_windows_destroy_windows(void) {
  1124. iron_window_destroy();
  1125. UnregisterClassW(windowClassName, GetModuleHandleW(NULL));
  1126. }
  1127. void iron_window_show() {
  1128. ShowWindow(windows[0].handle, SW_SHOWDEFAULT);
  1129. UpdateWindow(windows[0].handle);
  1130. }
  1131. void iron_window_hide() {
  1132. ShowWindow(windows[0].handle, SW_HIDE);
  1133. UpdateWindow(windows[0].handle);
  1134. }
  1135. void iron_window_set_title(const char *title) {
  1136. wchar_t buffer[1024];
  1137. MultiByteToWideChar(CP_UTF8, 0, title, -1, buffer, 1024);
  1138. SetWindowTextW(windows[0].handle, buffer);
  1139. }
  1140. void iron_window_create(iron_window_options_t *win) {
  1141. wchar_t wbuffer[1024];
  1142. MultiByteToWideChar(CP_UTF8, 0, win->title, -1, wbuffer, 1024);
  1143. createWindow(wbuffer, win->x, win->y, win->width, win->height, win->color_bits, win->frequency, win->features, win->mode, win->display_index);
  1144. gpu_init(win->depth_bits, win->vsync);
  1145. if (win->visible) {
  1146. iron_window_show();
  1147. }
  1148. }
  1149. void iron_window_set_resize_callback(void (*callback)(int x, int y, void *data), void *data) {
  1150. windows[0].resizeCallback = callback;
  1151. windows[0].resizeCallbackData = data;
  1152. }
  1153. void iron_window_set_close_callback(bool (*callback)(void *data), void *data) {
  1154. windows[0].closeCallback = callback;
  1155. windows[0].closeCallbackData = data;
  1156. }
  1157. int iron_window_display() {
  1158. return iron_windows_get_display_for_monitor(MonitorFromWindow(windows[0].handle, MONITOR_DEFAULTTOPRIMARY));
  1159. }
  1160. struct HWND__ *iron_windows_window_handle() {
  1161. return windows[0].handle;
  1162. }
  1163. void iron_internal_call_resize_callback(int width, int height) {
  1164. if (windows[0].resizeCallback != NULL) {
  1165. windows[0].resizeCallback(width, height, windows[0].resizeCallbackData);
  1166. }
  1167. }
  1168. bool iron_internal_call_close_callback() {
  1169. if (windows[0].closeCallback != NULL) {
  1170. return windows[0].closeCallback(windows[0].closeCallbackData);
  1171. }
  1172. return true;
  1173. }
  1174. extern void (*iron_save_and_quit)(bool);
  1175. bool _save_and_quit_callback_internal() {
  1176. bool save = false;
  1177. wchar_t title[1024];
  1178. GetWindowTextW(iron_windows_window_handle(), title, sizeof(title));
  1179. bool dirty = wcsstr(title, L"* - ArmorPaint") != NULL;
  1180. if (dirty) {
  1181. int res = MessageBox(iron_windows_window_handle(), L"Project has been modified, save changes?", L"Save Changes?", MB_YESNOCANCEL | MB_ICONEXCLAMATION);
  1182. if (res == IDYES) {
  1183. save = true;
  1184. }
  1185. else if (res == IDNO) {
  1186. save = false;
  1187. }
  1188. else { // Cancel
  1189. return false;
  1190. }
  1191. }
  1192. iron_save_and_quit(save);
  1193. return false;
  1194. }
  1195. #ifdef WITH_GAMEPAD
  1196. bool iron_gamepad_connected(int num) {
  1197. return isXInputGamepad(num) || isDirectInputGamepad(num);
  1198. }
  1199. void iron_gamepad_rumble(int gamepad, float left, float right) {
  1200. if (isXInputGamepad(gamepad)) {
  1201. XINPUT_VIBRATION vibration;
  1202. memset(&vibration, 0, sizeof(XINPUT_VIBRATION));
  1203. vibration.wLeftMotorSpeed = (WORD)(65535.f * left);
  1204. vibration.wRightMotorSpeed = (WORD)(65535.f * right);
  1205. InputSetState(gamepad, &vibration);
  1206. }
  1207. }
  1208. #include <XInput.h>
  1209. #include <dinput.h>
  1210. static float axes[12 * 6];
  1211. static float buttons[12 * 16];
  1212. typedef DWORD(WINAPI *XInputGetStateType)(DWORD dwUserIndex, XINPUT_STATE *pState);
  1213. typedef DWORD(WINAPI *XInputSetStateType)(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration);
  1214. static XInputGetStateType InputGetState = NULL;
  1215. static XInputSetStateType InputSetState = NULL;
  1216. void loadXInput() {
  1217. HMODULE lib = LoadLibraryA("xinput1_4.dll");
  1218. if (lib == NULL) {
  1219. lib = LoadLibraryA("xinput1_3.dll");
  1220. }
  1221. if (lib == NULL) {
  1222. lib = LoadLibraryA("xinput9_1_0.dll");
  1223. }
  1224. if (lib != NULL) {
  1225. InputGetState = (XInputGetStateType)GetProcAddress(lib, "XInputGetState");
  1226. InputSetState = (XInputSetStateType)GetProcAddress(lib, "XInputSetState");
  1227. }
  1228. }
  1229. static IDirectInput8W *di_instance = NULL;
  1230. static IDirectInputDevice8W *di_pads[IRON_DINPUT_MAX_COUNT];
  1231. static DIJOYSTATE2 di_padState[IRON_DINPUT_MAX_COUNT];
  1232. static DIJOYSTATE2 di_lastPadState[IRON_DINPUT_MAX_COUNT];
  1233. static DIDEVCAPS di_deviceCaps[IRON_DINPUT_MAX_COUNT];
  1234. static int padCount = 0;
  1235. static void cleanupPad(int padIndex) {
  1236. if (di_pads[padIndex] != NULL) {
  1237. di_pads[padIndex]->lpVtbl->Unacquire(di_pads[padIndex]);
  1238. di_pads[padIndex]->lpVtbl->Release(di_pads[padIndex]);
  1239. di_pads[padIndex] = 0;
  1240. }
  1241. }
  1242. #ifndef SAFE_RELEASE
  1243. #define SAFE_RELEASE(x) \
  1244. if (x != NULL) { \
  1245. x->lpVtbl->Release(x); \
  1246. x = NULL; \
  1247. }
  1248. #endif
  1249. // From
  1250. //-----------------------------------------------------------------------------
  1251. // Enum each PNP device using WMI and check each device ID to see if it contains
  1252. // "IG_" (ex. "VID_045E&PID_028E&IG_00"). If it does, then it's an XInput device
  1253. // Unfortunately this information can not be found by just using DirectInput
  1254. //-----------------------------------------------------------------------------
  1255. static BOOL IsXInputDevice(const GUID *pGuidProductFromDirectInput) {
  1256. IWbemLocator *pIWbemLocator = NULL;
  1257. IEnumWbemClassObject *pEnumDevices = NULL;
  1258. IWbemClassObject *pDevices[20] = {0};
  1259. IWbemServices *pIWbemServices = NULL;
  1260. BSTR bstrNamespace = NULL;
  1261. BSTR bstrDeviceID = NULL;
  1262. BSTR bstrClassName = NULL;
  1263. DWORD uReturned = 0;
  1264. bool bIsXinputDevice = false;
  1265. UINT iDevice = 0;
  1266. VARIANT var;
  1267. HRESULT hr;
  1268. // CoInit if needed
  1269. hr = CoInitialize(NULL);
  1270. bool bCleanupCOM = SUCCEEDED(hr);
  1271. // Create WMI
  1272. hr = CoCreateInstance(&CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, &IID_IWbemLocator, (LPVOID *)&pIWbemLocator);
  1273. if (FAILED(hr) || pIWbemLocator == NULL)
  1274. goto LCleanup;
  1275. bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2");
  1276. if (bstrNamespace == NULL)
  1277. goto LCleanup;
  1278. bstrClassName = SysAllocString(L"Win32_PNPEntity");
  1279. if (bstrClassName == NULL)
  1280. goto LCleanup;
  1281. bstrDeviceID = SysAllocString(L"DeviceID");
  1282. if (bstrDeviceID == NULL)
  1283. goto LCleanup;
  1284. // Connect to WMI
  1285. hr = pIWbemLocator->lpVtbl->ConnectServer(pIWbemLocator, bstrNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pIWbemServices);
  1286. if (FAILED(hr) || pIWbemServices == NULL)
  1287. goto LCleanup;
  1288. // Switch security level to IMPERSONATE.
  1289. CoSetProxyBlanket((IUnknown *)pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL,
  1290. EOAC_NONE);
  1291. hr = pIWbemServices->lpVtbl->CreateInstanceEnum(pIWbemServices, bstrClassName, 0, NULL, &pEnumDevices);
  1292. if (FAILED(hr) || pEnumDevices == NULL)
  1293. goto LCleanup;
  1294. // Loop over all devices
  1295. for (;;) {
  1296. // Get 20 at a time
  1297. hr = pEnumDevices->lpVtbl->Next(pEnumDevices, 10000, 20, pDevices, &uReturned);
  1298. if (FAILED(hr))
  1299. goto LCleanup;
  1300. if (uReturned == 0)
  1301. break;
  1302. for (iDevice = 0; iDevice < uReturned; iDevice++) {
  1303. // For each device, get its device ID
  1304. hr = pDevices[iDevice]->lpVtbl->Get(pDevices[iDevice], bstrDeviceID, 0L, &var, NULL, NULL);
  1305. if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL) {
  1306. // Check if the device ID contains "IG_". If it does, then it's an XInput device
  1307. // This information can not be found from DirectInput
  1308. // TODO: Doesn't work with an Xbox Series X|S controller
  1309. if (wcsstr(var.bstrVal, L"IG_")) {
  1310. // If it does, then get the VID/PID from var.bstrVal
  1311. DWORD dwPid = 0, dwVid = 0;
  1312. WCHAR *strVid = wcsstr(var.bstrVal, L"VID_");
  1313. #ifndef IRON_NO_CLIB
  1314. if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1) {
  1315. dwVid = 0;
  1316. }
  1317. WCHAR *strPid = wcsstr(var.bstrVal, L"PID_");
  1318. if (strPid && swscanf(strPid, L"PID_%4X", &dwPid) != 1) {
  1319. dwPid = 0;
  1320. }
  1321. #endif
  1322. // Compare the VID/PID to the DInput device
  1323. DWORD dwVidPid = MAKELONG(dwVid, dwPid);
  1324. if (dwVidPid == pGuidProductFromDirectInput->Data1) {
  1325. bIsXinputDevice = true;
  1326. goto LCleanup;
  1327. }
  1328. }
  1329. }
  1330. SAFE_RELEASE(pDevices[iDevice]);
  1331. }
  1332. }
  1333. LCleanup:
  1334. if (bstrNamespace)
  1335. SysFreeString(bstrNamespace);
  1336. if (bstrDeviceID)
  1337. SysFreeString(bstrDeviceID);
  1338. if (bstrClassName)
  1339. SysFreeString(bstrClassName);
  1340. for (iDevice = 0; iDevice < 20; iDevice++)
  1341. SAFE_RELEASE(pDevices[iDevice]);
  1342. SAFE_RELEASE(pEnumDevices);
  1343. SAFE_RELEASE(pIWbemLocator);
  1344. SAFE_RELEASE(pIWbemServices);
  1345. if (bCleanupCOM)
  1346. CoUninitialize();
  1347. return bIsXinputDevice;
  1348. }
  1349. static void cleanupDirectInput() {
  1350. for (int padIndex = 0; padIndex < IRON_DINPUT_MAX_COUNT; ++padIndex) {
  1351. cleanupPad(padIndex);
  1352. }
  1353. if (di_instance != NULL) {
  1354. di_instance->lpVtbl->Release(di_instance);
  1355. di_instance = NULL;
  1356. }
  1357. }
  1358. static BOOL CALLBACK enumerateJoystickAxesCallback(LPCDIDEVICEOBJECTINSTANCEW ddoi, LPVOID context) {
  1359. HWND hwnd = (HWND)context;
  1360. DIPROPRANGE propertyRange;
  1361. propertyRange.diph.dwSize = sizeof(DIPROPRANGE);
  1362. propertyRange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1363. propertyRange.diph.dwHow = DIPH_BYID;
  1364. propertyRange.diph.dwObj = ddoi->dwType;
  1365. propertyRange.lMin = -32768;
  1366. propertyRange.lMax = 32768;
  1367. HRESULT hr = di_pads[padCount]->lpVtbl->SetProperty(di_pads[padCount], DIPROP_RANGE, &propertyRange.diph);
  1368. if (FAILED(hr)) {
  1369. iron_log("DirectInput8 / Pad%i / SetProperty() failed (HRESULT=0x%x)", padCount, hr);
  1370. return DIENUM_STOP;
  1371. }
  1372. return DIENUM_CONTINUE;
  1373. }
  1374. static BOOL CALLBACK enumerateJoysticksCallback(LPCDIDEVICEINSTANCEW ddi, LPVOID context) {
  1375. if (padCount < XUSER_MAX_COUNT && IsXInputDevice(&ddi->guidProduct)) {
  1376. ++padCount;
  1377. return DIENUM_CONTINUE;
  1378. }
  1379. HRESULT hr = di_instance->lpVtbl->CreateDevice(di_instance, &ddi->guidInstance, &di_pads[padCount], NULL);
  1380. if (SUCCEEDED(hr)) {
  1381. hr = di_pads[padCount]->lpVtbl->SetDataFormat(di_pads[padCount], &c_dfDIJoystick2);
  1382. if (SUCCEEDED(hr)) {
  1383. di_deviceCaps[padCount].dwSize = sizeof(DIDEVCAPS);
  1384. hr = di_pads[padCount]->lpVtbl->GetCapabilities(di_pads[padCount], &di_deviceCaps[padCount]);
  1385. if (SUCCEEDED(hr)) {
  1386. hr = di_pads[padCount]->lpVtbl->EnumObjects(di_pads[padCount], enumerateJoystickAxesCallback, NULL, DIDFT_AXIS);
  1387. if (SUCCEEDED(hr)) {
  1388. hr = di_pads[padCount]->lpVtbl->Acquire(di_pads[padCount]);
  1389. if (SUCCEEDED(hr)) {
  1390. memset(&di_padState[padCount], 0, sizeof(DIJOYSTATE2));
  1391. hr = di_pads[padCount]->lpVtbl->GetDeviceState(di_pads[padCount], sizeof(DIJOYSTATE2), &di_padState[padCount]);
  1392. if (SUCCEEDED(hr)) {
  1393. iron_log("DirectInput8 / Pad%i / initialized", padCount);
  1394. }
  1395. else {
  1396. iron_log("DirectInput8 / Pad%i / GetDeviceState() failed (HRESULT=0x%x)", padCount, hr);
  1397. }
  1398. }
  1399. else {
  1400. iron_log("DirectInput8 / Pad%i / Acquire() failed (HRESULT=0x%x)", padCount, hr);
  1401. cleanupPad(padCount);
  1402. }
  1403. }
  1404. else {
  1405. iron_log("DirectInput8 / Pad%i / EnumObjects(DIDFT_AXIS) failed (HRESULT=0x%x)", padCount, hr);
  1406. cleanupPad(padCount);
  1407. }
  1408. }
  1409. else {
  1410. iron_log("DirectInput8 / Pad%i / GetCapabilities() failed (HRESULT=0x%x)", padCount, hr);
  1411. cleanupPad(padCount);
  1412. }
  1413. }
  1414. else {
  1415. iron_log("DirectInput8 / Pad%i / SetDataFormat() failed (HRESULT=0x%x)", padCount, hr);
  1416. cleanupPad(padCount);
  1417. }
  1418. ++padCount;
  1419. if (padCount >= IRON_DINPUT_MAX_COUNT) {
  1420. return DIENUM_STOP;
  1421. }
  1422. }
  1423. return DIENUM_CONTINUE;
  1424. }
  1425. static void initializeDirectInput() {
  1426. HINSTANCE hinstance = GetModuleHandleW(NULL);
  1427. memset(&di_pads, 0, sizeof(IDirectInputDevice8) * IRON_DINPUT_MAX_COUNT);
  1428. memset(&di_padState, 0, sizeof(DIJOYSTATE2) * IRON_DINPUT_MAX_COUNT);
  1429. memset(&di_lastPadState, 0, sizeof(DIJOYSTATE2) * IRON_DINPUT_MAX_COUNT);
  1430. memset(&di_deviceCaps, 0, sizeof(DIDEVCAPS) * IRON_DINPUT_MAX_COUNT);
  1431. // #define DIRECTINPUT_VERSION 0x0800
  1432. HRESULT hr = DirectInput8Create(hinstance, 0x0800, &IID_IDirectInput8W, (void **)&di_instance, NULL);
  1433. if (SUCCEEDED(hr)) {
  1434. hr = di_instance->lpVtbl->EnumDevices(di_instance, DI8DEVCLASS_GAMECTRL, enumerateJoysticksCallback, NULL, DIEDFL_ATTACHEDONLY);
  1435. if (SUCCEEDED(hr)) {
  1436. }
  1437. else {
  1438. cleanupDirectInput();
  1439. }
  1440. }
  1441. else {
  1442. iron_log("DirectInput8Create failed (HRESULT=0x%x)", hr);
  1443. }
  1444. }
  1445. bool handleDirectInputPad(int padIndex) {
  1446. if (di_pads[padIndex] == NULL) {
  1447. return false;
  1448. }
  1449. HRESULT hr = di_pads[padIndex]->lpVtbl->GetDeviceState(di_pads[padIndex], sizeof(DIJOYSTATE2), &di_padState[padIndex]);
  1450. switch (hr) {
  1451. case S_OK: {
  1452. for (int axisIndex = 0; axisIndex < 2; ++axisIndex) {
  1453. LONG *now = NULL;
  1454. LONG *last = NULL;
  1455. switch (axisIndex) {
  1456. case 0: {
  1457. now = &di_padState[padIndex].lX;
  1458. last = &di_lastPadState[padIndex].lX;
  1459. } break;
  1460. case 1: {
  1461. now = &di_padState[padIndex].lY;
  1462. last = &di_lastPadState[padIndex].lY;
  1463. } break;
  1464. case 2: {
  1465. now = &di_padState[padIndex].lZ;
  1466. last = &di_lastPadState[padIndex].lZ;
  1467. } break;
  1468. }
  1469. if (*now != *last) {
  1470. iron_internal_gamepad_trigger_axis(padIndex, axisIndex, *now / 32768.0f);
  1471. }
  1472. }
  1473. for (int buttonIndex = 0; buttonIndex < 128; ++buttonIndex) {
  1474. BYTE *now = &di_padState[padIndex].rgbButtons[buttonIndex];
  1475. BYTE *last = &di_lastPadState[padIndex].rgbButtons[buttonIndex];
  1476. if (*now != *last) {
  1477. iron_internal_gamepad_trigger_button(padIndex, buttonIndex, *now / 255.0f);
  1478. }
  1479. }
  1480. for (int povIndex = 0; povIndex < 4; ++povIndex) {
  1481. DWORD *now = &di_padState[padIndex].rgdwPOV[povIndex];
  1482. DWORD *last = &di_lastPadState[padIndex].rgdwPOV[povIndex];
  1483. bool up = (*now == 0 || *now == 31500 || *now == 4500);
  1484. bool down = (*now == 18000 || *now == 13500 || *now == 22500);
  1485. bool left = (*now == 27000 || *now == 22500 || *now == 31500);
  1486. bool right = (*now == 9000 || *now == 4500 || *now == 13500);
  1487. bool lastUp = (*last == 0 || *last == 31500 || *last == 4500);
  1488. bool lastDown = (*last == 18000 || *last == 13500 || *last == 22500);
  1489. bool lastLeft = (*last == 27000 || *last == 22500 || *last == 31500);
  1490. bool lastRight = (*last == 9000 || *last == 4500 || *last == 13500);
  1491. if (up != lastUp) {
  1492. iron_internal_gamepad_trigger_button(padIndex, 12, up ? 1.0f : 0.0f);
  1493. }
  1494. if (down != lastDown) {
  1495. iron_internal_gamepad_trigger_button(padIndex, 13, down ? 1.0f : 0.0f);
  1496. }
  1497. if (left != lastLeft) {
  1498. iron_internal_gamepad_trigger_button(padIndex, 14, left ? 1.0f : 0.0f);
  1499. }
  1500. if (right != lastRight) {
  1501. iron_internal_gamepad_trigger_button(padIndex, 15, right ? 1.0f : 0.0f);
  1502. }
  1503. }
  1504. memcpy(&di_lastPadState[padIndex], &di_padState[padIndex], sizeof(DIJOYSTATE2));
  1505. break;
  1506. }
  1507. case DIERR_INPUTLOST: // fall through
  1508. case DIERR_NOTACQUIRED: {
  1509. hr = di_pads[padIndex]->lpVtbl->Acquire(di_pads[padIndex]);
  1510. break;
  1511. }
  1512. }
  1513. return hr == S_OK;
  1514. }
  1515. static bool isXInputGamepad(int gamepad) {
  1516. // if gamepad is greater than XInput max, treat it as DINPUT.
  1517. if (gamepad >= XUSER_MAX_COUNT) {
  1518. return false;
  1519. }
  1520. XINPUT_STATE state;
  1521. memset(&state, 0, sizeof(XINPUT_STATE));
  1522. DWORD dwResult = InputGetState(gamepad, &state);
  1523. return dwResult == ERROR_SUCCESS;
  1524. }
  1525. static bool isDirectInputGamepad(int gamepad) {
  1526. if (di_pads[gamepad] == NULL) {
  1527. return false;
  1528. }
  1529. HRESULT hr = di_pads[gamepad]->lpVtbl->GetDeviceState(di_pads[gamepad], sizeof(DIJOYSTATE2), &di_padState[gamepad]);
  1530. return hr == S_OK;
  1531. }
  1532. const char *iron_gamepad_vendor(int gamepad) {
  1533. if (isXInputGamepad(gamepad)) {
  1534. return "Microsoft";
  1535. }
  1536. else {
  1537. return "DirectInput8";
  1538. }
  1539. }
  1540. const char *iron_gamepad_product_name(int gamepad) {
  1541. if (isXInputGamepad(gamepad)) {
  1542. return "Xbox 360 Controller";
  1543. }
  1544. else {
  1545. return "Generic Gamepad";
  1546. }
  1547. }
  1548. void iron_gamepad_handle_messages() {
  1549. if (InputGetState != NULL && (detectGamepad || gamepadFound)) {
  1550. detectGamepad = false;
  1551. for (DWORD i = 0; i < IRON_DINPUT_MAX_COUNT; ++i) {
  1552. XINPUT_STATE state;
  1553. memset(&state, 0, sizeof(XINPUT_STATE));
  1554. DWORD dwResult = InputGetState(i, &state);
  1555. if (dwResult == ERROR_SUCCESS) {
  1556. gamepadFound = true;
  1557. float newaxes[6];
  1558. newaxes[0] = state.Gamepad.sThumbLX / 32768.0f;
  1559. newaxes[1] = state.Gamepad.sThumbLY / 32768.0f;
  1560. newaxes[2] = state.Gamepad.sThumbRX / 32768.0f;
  1561. newaxes[3] = state.Gamepad.sThumbRY / 32768.0f;
  1562. newaxes[4] = state.Gamepad.bLeftTrigger / 255.0f;
  1563. newaxes[5] = state.Gamepad.bRightTrigger / 255.0f;
  1564. for (int i2 = 0; i2 < 6; ++i2) {
  1565. if (axes[i * 6 + i2] != newaxes[i2]) {
  1566. iron_internal_gamepad_trigger_axis(i, i2, newaxes[i2]);
  1567. axes[i * 6 + i2] = newaxes[i2];
  1568. }
  1569. }
  1570. float newbuttons[16];
  1571. newbuttons[0] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_A) ? 1.0f : 0.0f;
  1572. newbuttons[1] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_B) ? 1.0f : 0.0f;
  1573. newbuttons[2] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_X) ? 1.0f : 0.0f;
  1574. newbuttons[3] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_Y) ? 1.0f : 0.0f;
  1575. newbuttons[4] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) ? 1.0f : 0.0f;
  1576. newbuttons[5] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) ? 1.0f : 0.0f;
  1577. newbuttons[6] = state.Gamepad.bLeftTrigger / 255.0f;
  1578. newbuttons[7] = state.Gamepad.bRightTrigger / 255.0f;
  1579. newbuttons[8] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) ? 1.0f : 0.0f;
  1580. newbuttons[9] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_START) ? 1.0f : 0.0f;
  1581. newbuttons[10] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) ? 1.0f : 0.0f;
  1582. newbuttons[11] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) ? 1.0f : 0.0f;
  1583. newbuttons[12] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) ? 1.0f : 0.0f;
  1584. newbuttons[13] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) ? 1.0f : 0.0f;
  1585. newbuttons[14] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) ? 1.0f : 0.0f;
  1586. newbuttons[15] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) ? 1.0f : 0.0f;
  1587. for (int i2 = 0; i2 < 16; ++i2) {
  1588. if (buttons[i * 16 + i2] != newbuttons[i2]) {
  1589. iron_internal_gamepad_trigger_button(i, i2, newbuttons[i2]);
  1590. buttons[i * 16 + i2] = newbuttons[i2];
  1591. }
  1592. }
  1593. }
  1594. else {
  1595. if (handleDirectInputPad(i)) {
  1596. gamepadFound = true;
  1597. }
  1598. }
  1599. }
  1600. }
  1601. }
  1602. #endif
  1603. #include <process.h>
  1604. volatile int iron_exec_async_done = 1;
  1605. static HANDLE child_handle = NULL;
  1606. static HANDLE wait_handle = NULL;
  1607. static void CALLBACK exec_wait_callback(PVOID lparam, BOOLEAN b) {
  1608. iron_exec_async_done = 1;
  1609. }
  1610. void iron_exec_async(const char *path, char *argv[]) {
  1611. iron_exec_async_done = 0;
  1612. char cmd[4096] = {0};
  1613. snprintf(cmd, sizeof(cmd), "\"%s\"", path);
  1614. for (int i = 1; argv[i] != NULL; ++i) {
  1615. strncat(cmd, " \"", sizeof(cmd) - strlen(cmd) - 1);
  1616. strncat(cmd, argv[i], sizeof(cmd) - strlen(cmd) - 1);
  1617. strncat(cmd, "\"", sizeof(cmd) - strlen(cmd) - 1);
  1618. }
  1619. wchar_t wcmd[4096];
  1620. MultiByteToWideChar(CP_UTF8, 0, cmd, -1, wcmd, 4096);
  1621. STARTUPINFOW si = {0};
  1622. PROCESS_INFORMATION pi = {0};
  1623. si.cb = sizeof(si);
  1624. si.dwFlags = STARTF_USESHOWWINDOW;
  1625. si.wShowWindow = SW_HIDE;
  1626. BOOL success = CreateProcessW(NULL, wcmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
  1627. if (!success) {
  1628. iron_exec_async_done = 1;
  1629. return;
  1630. }
  1631. child_handle = pi.hProcess;
  1632. CloseHandle(pi.hThread);
  1633. RegisterWaitForSingleObject(&wait_handle, child_handle, exec_wait_callback, NULL, INFINITE, WT_EXECUTEONLYONCE);
  1634. }