windows_system.c 53 KB

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