windows_system.c 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790
  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. static HCURSOR cursors[5];
  386. static bool bg_erased = false;
  387. void iron_mouse_set_cursor(iron_cursor_t set_cursor) {
  388. cursor = set_cursor;
  389. if (cursors_initialized) {
  390. SetCursor(cursors[cursor]);
  391. }
  392. // Set hand icon for drag even when mouse button is pressed
  393. if (set_cursor == IRON_CURSOR_HAND) {
  394. SetCursor(LoadCursor(NULL, IDC_HAND));
  395. }
  396. }
  397. LRESULT WINAPI IronWindowsMessageProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
  398. DWORD pointerId;
  399. POINTER_INFO pointerInfo = {0};
  400. POINTER_PEN_INFO penInfo = {0};
  401. static bool controlDown = false;
  402. #ifdef HANDLE_ALT_ENTER
  403. static bool altDown = false;
  404. #endif
  405. static int last_window_width = -1;
  406. static int last_window_height = -1;
  407. static int last_window_x = INT_MIN;
  408. static int last_window_y = INT_MIN;
  409. switch (msg) {
  410. case WM_NCCREATE:
  411. EnableNonClientDpiScaling(hWnd);
  412. break;
  413. case WM_DPICHANGED: {
  414. break;
  415. }
  416. case WM_MOVE:
  417. case WM_MOVING:
  418. case WM_SIZING:
  419. // Scheduler::breakTime();
  420. break;
  421. case WM_SIZE: {
  422. int width = LOWORD(lParam);
  423. int height = HIWORD(lParam);
  424. gpu_resize(width, height);
  425. iron_internal_call_resize_callback(width, height);
  426. break;
  427. }
  428. case WM_CLOSE: {
  429. if (iron_internal_call_close_callback()) {
  430. iron_window_destroy();
  431. iron_stop();
  432. return 0;
  433. }
  434. return 0;
  435. }
  436. case WM_ERASEBKGND: {
  437. if (bg_erased) {
  438. return 1;
  439. }
  440. bg_erased = true;
  441. }
  442. case WM_ACTIVATE:
  443. if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) {
  444. iron_internal_mouse_window_activated();
  445. iron_internal_foreground_callback();
  446. }
  447. else {
  448. iron_internal_mouse_window_deactivated();
  449. iron_internal_background_callback();
  450. #ifdef HANDLE_ALT_ENTER
  451. altDown = false;
  452. #endif
  453. }
  454. RegisterTouchWindow(hWnd, 0);
  455. break;
  456. case WM_MOUSELEAVE:
  457. break;
  458. case WM_MOUSEMOVE:
  459. mouseX = GET_X_LPARAM(lParam);
  460. mouseY = GET_Y_LPARAM(lParam);
  461. iron_internal_mouse_trigger_move(mouseX, mouseY);
  462. break;
  463. case WM_CREATE:
  464. cursors[0] = LoadCursor(0, IDC_ARROW);
  465. cursors[1] = LoadCursor(0, IDC_HAND);
  466. cursors[2] = LoadCursor(0, IDC_IBEAM);
  467. cursors[3] = LoadCursor(0, IDC_SIZEWE);
  468. cursors[4] = LoadCursor(0, IDC_SIZENS);
  469. cursors_initialized = true;
  470. return TRUE;
  471. case WM_SETCURSOR:
  472. if (LOWORD(lParam) == HTCLIENT) {
  473. SetCursor(cursors[cursor]);
  474. return TRUE;
  475. }
  476. break;
  477. case WM_LBUTTONDOWN:
  478. if (!iron_mouse_is_locked())
  479. SetCapture(hWnd);
  480. mouseX = GET_X_LPARAM(lParam);
  481. mouseY = GET_Y_LPARAM(lParam);
  482. iron_internal_mouse_trigger_press(0, mouseX, mouseY);
  483. break;
  484. case WM_LBUTTONUP:
  485. if (!iron_mouse_is_locked())
  486. ReleaseCapture();
  487. mouseX = GET_X_LPARAM(lParam);
  488. mouseY = GET_Y_LPARAM(lParam);
  489. iron_internal_mouse_trigger_release(0, mouseX, mouseY);
  490. break;
  491. case WM_RBUTTONDOWN:
  492. mouseX = GET_X_LPARAM(lParam);
  493. mouseY = GET_Y_LPARAM(lParam);
  494. iron_internal_mouse_trigger_press(1, mouseX, mouseY);
  495. break;
  496. case WM_RBUTTONUP:
  497. mouseX = GET_X_LPARAM(lParam);
  498. mouseY = GET_Y_LPARAM(lParam);
  499. iron_internal_mouse_trigger_release(1, mouseX, mouseY);
  500. break;
  501. case WM_MBUTTONDOWN:
  502. mouseX = GET_X_LPARAM(lParam);
  503. mouseY = GET_Y_LPARAM(lParam);
  504. iron_internal_mouse_trigger_press(2, mouseX, mouseY);
  505. break;
  506. case WM_MBUTTONUP:
  507. mouseX = GET_X_LPARAM(lParam);
  508. mouseY = GET_Y_LPARAM(lParam);
  509. iron_internal_mouse_trigger_release(2, mouseX, mouseY);
  510. break;
  511. case WM_XBUTTONDOWN:
  512. mouseX = GET_X_LPARAM(lParam);
  513. mouseY = GET_Y_LPARAM(lParam);
  514. iron_internal_mouse_trigger_press(HIWORD(wParam) + 2, mouseX, mouseY);
  515. break;
  516. case WM_XBUTTONUP:
  517. mouseX = GET_X_LPARAM(lParam);
  518. mouseY = GET_Y_LPARAM(lParam);
  519. iron_internal_mouse_trigger_release(HIWORD(wParam) + 2, mouseX, mouseY);
  520. break;
  521. case WM_MOUSEWHEEL:
  522. iron_internal_mouse_trigger_scroll(GET_WHEEL_DELTA_WPARAM(wParam) / -120);
  523. break;
  524. case WM_POINTERDOWN:
  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_press(pointerInfo.ptPixelLocation.x, pointerInfo.ptPixelLocation.y,
  531. penInfo.pressure / 1024.0f);
  532. }
  533. break;
  534. case WM_POINTERUP:
  535. pointerId = GET_POINTERID_WPARAM(wParam);
  536. GetPointerInfo(pointerId, &pointerInfo);
  537. if (pointerInfo.pointerType == PT_PEN) {
  538. GetPointerPenInfo(pointerId, &penInfo);
  539. ScreenToClient(hWnd, &pointerInfo.ptPixelLocation);
  540. iron_internal_pen_trigger_release(pointerInfo.ptPixelLocation.x, pointerInfo.ptPixelLocation.y,
  541. penInfo.pressure / 1024.0f);
  542. }
  543. break;
  544. case WM_POINTERUPDATE:
  545. pointerId = GET_POINTERID_WPARAM(wParam);
  546. GetPointerInfo(pointerId, &pointerInfo);
  547. if (pointerInfo.pointerType == PT_PEN) {
  548. GetPointerPenInfo(pointerId, &penInfo);
  549. ScreenToClient(hWnd, &pointerInfo.ptPixelLocation);
  550. iron_internal_pen_trigger_move(pointerInfo.ptPixelLocation.x, pointerInfo.ptPixelLocation.y,
  551. penInfo.pressure / 1024.0f);
  552. }
  553. break;
  554. case WM_TOUCH: {
  555. BOOL bHandled = FALSE;
  556. UINT cInputs = LOWORD(wParam);
  557. PTOUCHINPUT pInputs = _alloca(cInputs * sizeof(TOUCHINPUT));
  558. POINT ptInput;
  559. int tindex;
  560. if (pInputs) {
  561. if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))) {
  562. for (int i = 0; i < (int)cInputs; i++) {
  563. TOUCHINPUT ti = pInputs[i];
  564. if (ti.dwID != 0) {
  565. ptInput.x = TOUCH_COORD_TO_PIXEL(ti.x);
  566. ptInput.y = TOUCH_COORD_TO_PIXEL(ti.y);
  567. ScreenToClient(hWnd, &ptInput);
  568. if (ti.dwFlags & TOUCHEVENTF_UP) {
  569. tindex = GetTouchIndex(ti.dwID);
  570. ReleaseTouchIndex(ti.dwID);
  571. iron_internal_surface_trigger_touch_end(tindex, ptInput.x, ptInput.y);
  572. }
  573. else {
  574. bool touchExisits = GetTouchIndex(ti.dwID) != -1;
  575. tindex = GetAddTouchIndex(ti.dwID);
  576. if (tindex >= 0) {
  577. if (touchExisits) {
  578. if (touchPoints[tindex].x != ptInput.x || touchPoints[tindex].y != ptInput.y) {
  579. touchPoints[tindex].x = ptInput.x;
  580. touchPoints[tindex].y = ptInput.y;
  581. iron_internal_surface_trigger_move(tindex, ptInput.x, ptInput.y);
  582. }
  583. }
  584. else {
  585. touchPoints[tindex].x = ptInput.x;
  586. touchPoints[tindex].y = ptInput.y;
  587. iron_internal_surface_trigger_touch_start(tindex, ptInput.x, ptInput.y);
  588. }
  589. }
  590. }
  591. }
  592. bHandled = TRUE;
  593. if (!CloseTouchInputHandle((HTOUCHINPUT)lParam)) {
  594. }
  595. }
  596. }
  597. }
  598. if (bHandled)
  599. CloseTouchInputHandle((HTOUCHINPUT)lParam);
  600. else
  601. DefWindowProcW(hWnd, WM_TOUCH, wParam, lParam);
  602. InvalidateRect(hWnd, NULL, FALSE);
  603. } break;
  604. case WM_KEYDOWN:
  605. case WM_SYSKEYDOWN:
  606. if (!keyPressed[wParam]) {
  607. keyPressed[wParam] = true;
  608. if (keyTranslated[wParam] == IRON_KEY_CONTROL) {
  609. controlDown = true;
  610. }
  611. #ifdef HANDLE_ALT_ENTER
  612. else if (keyTranslated[wParam] == IRON_KEY_ALT) {
  613. altDown = true;
  614. }
  615. #endif
  616. else {
  617. if (controlDown && keyTranslated[wParam] == IRON_KEY_X) {
  618. char *text = iron_internal_cut_callback();
  619. if (text != NULL) {
  620. wchar_t wtext[4096];
  621. MultiByteToWideChar(CP_UTF8, 0, text, -1, wtext, 4096);
  622. OpenClipboard(hWnd);
  623. EmptyClipboard();
  624. size_t size = (wcslen(wtext) + 1) * sizeof(wchar_t);
  625. HANDLE handle = GlobalAlloc(GMEM_MOVEABLE, size);
  626. void *data = GlobalLock(handle);
  627. memcpy(data, wtext, size);
  628. GlobalUnlock(handle);
  629. SetClipboardData(CF_UNICODETEXT, handle);
  630. CloseClipboard();
  631. }
  632. }
  633. if (controlDown && keyTranslated[wParam] == IRON_KEY_C) {
  634. char *text = iron_internal_copy_callback();
  635. if (text != NULL) {
  636. iron_copy_to_clipboard(text);
  637. }
  638. }
  639. if (controlDown && keyTranslated[wParam] == IRON_KEY_V) {
  640. if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
  641. OpenClipboard(hWnd);
  642. HANDLE handle = GetClipboardData(CF_UNICODETEXT);
  643. if (handle != NULL) {
  644. wchar_t *wtext = (wchar_t *)GlobalLock(handle);
  645. if (wtext != NULL) {
  646. char text[4096];
  647. WideCharToMultiByte(CP_UTF8, 0, wtext, -1, text, 4096, NULL, NULL);
  648. iron_internal_paste_callback(text);
  649. GlobalUnlock(handle);
  650. }
  651. }
  652. CloseClipboard();
  653. }
  654. }
  655. #ifdef HANDLE_ALT_ENTER
  656. if (altDown && keyTranslated[wParam] == IRON_KEY_RETURN) {
  657. if (iron_window_get_mode() == IRON_WINDOW_MODE_WINDOW) {
  658. last_window_width = iron_window_width();
  659. last_window_height = iron_window_height();
  660. last_window_x = iron_window_x();
  661. last_window_y = iron_window_y();
  662. iron_window_change_mode(IRON_WINDOW_MODE_FULLSCREEN);
  663. }
  664. else {
  665. iron_window_change_mode(IRON_WINDOW_MODE_WINDOW);
  666. if (last_window_width > 0 && last_window_height > 0) {
  667. iron_window_resize(last_window_width, last_window_height);
  668. }
  669. if (last_window_x > INT_MIN && last_window_y > INT_MIN) {
  670. iron_window_move(last_window_x, last_window_y);
  671. }
  672. }
  673. }
  674. #endif
  675. }
  676. // No auto-repeat
  677. iron_internal_keyboard_trigger_key_down(keyTranslated[wParam]);
  678. }
  679. // Auto-repeat
  680. // iron_internal_keyboard_trigger_key_down(keyTranslated[wParam]);
  681. break;
  682. case WM_KEYUP:
  683. case WM_SYSKEYUP:
  684. keyPressed[wParam] = false;
  685. if (keyTranslated[wParam] == IRON_KEY_CONTROL) {
  686. controlDown = false;
  687. }
  688. #ifdef HANDLE_ALT_ENTER
  689. if (keyTranslated[wParam] == IRON_KEY_ALT) {
  690. altDown = false;
  691. }
  692. #endif
  693. iron_internal_keyboard_trigger_key_up(keyTranslated[wParam]);
  694. break;
  695. case WM_CHAR:
  696. switch (wParam) {
  697. case 0x1B: // escape
  698. break;
  699. default:
  700. iron_internal_keyboard_trigger_key_press((unsigned)wParam);
  701. break;
  702. }
  703. break;
  704. case WM_SYSCOMMAND:
  705. switch (wParam) {
  706. case SC_KEYMENU:
  707. return 0;
  708. case SC_SCREENSAVE:
  709. case SC_MONITORPOWER:
  710. return 0;
  711. case SC_MINIMIZE:
  712. break;
  713. case SC_RESTORE:
  714. case SC_MAXIMIZE:
  715. break;
  716. }
  717. break;
  718. case WM_DEVICECHANGE:
  719. #ifdef WITH_GAMEPAD
  720. detectGamepad = true;
  721. #endif
  722. break;
  723. case WM_DROPFILES: {
  724. HDROP hDrop = (HDROP)wParam;
  725. unsigned count = DragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0);
  726. for (unsigned i = 0; i < count; ++i) {
  727. wchar_t filePath[260];
  728. if (DragQueryFileW(hDrop, i, filePath, 260)) {
  729. iron_internal_drop_files_callback(filePath);
  730. }
  731. }
  732. DragFinish(hDrop);
  733. break;
  734. }
  735. }
  736. return DefWindowProcW(hWnd, msg, wParam, lParam);
  737. }
  738. bool iron_internal_handle_messages() {
  739. MSG message;
  740. while (PeekMessageW(&message, 0, 0, 0, PM_REMOVE)) {
  741. TranslateMessage(&message);
  742. DispatchMessageW(&message);
  743. }
  744. #ifdef WITH_GAMEPAD
  745. iron_gamepad_handle_messages();
  746. #endif
  747. return true;
  748. }
  749. static bool keyboardshown = false;
  750. static char language[3] = {0};
  751. void iron_keyboard_show() {
  752. keyboardshown = true;
  753. }
  754. void iron_keyboard_hide() {
  755. keyboardshown = false;
  756. }
  757. bool iron_keyboard_active() {
  758. return keyboardshown;
  759. }
  760. void iron_load_url(const char *url) {
  761. if (strncmp(url, "http://", sizeof("http://") - 1) == 0 || strncmp(url, "https://", sizeof("https://") - 1) == 0) {
  762. wchar_t wurl[1024];
  763. MultiByteToWideChar(CP_UTF8, 0, url, -1, wurl, 1024);
  764. INT_PTR ret = (INT_PTR)ShellExecuteW(NULL, L"open", wurl, NULL, NULL, SW_SHOWNORMAL);
  765. if (ret <= 32) {
  766. iron_log("Error opening url %s", url);
  767. }
  768. }
  769. }
  770. void iron_set_keep_screen_on(bool on) {}
  771. const char *iron_language() {
  772. wchar_t wlanguage[3] = {0};
  773. if (GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SISO639LANGNAME, wlanguage, 3)) {
  774. WideCharToMultiByte(CP_UTF8, 0, wlanguage, -1, language, 3, NULL, NULL);
  775. return language;
  776. }
  777. return "en";
  778. }
  779. const char *iron_system_id() {
  780. return "Windows";
  781. }
  782. static bool co_initialized = false;
  783. void iron_windows_co_initialize(void) {
  784. if (!co_initialized) {
  785. CoInitializeEx(0, COINIT_MULTITHREADED);
  786. co_initialized = true;
  787. }
  788. }
  789. static wchar_t savePathw[2048] = {0};
  790. static char savePath[2048] = {0};
  791. static void findSavePath() {
  792. iron_windows_co_initialize();
  793. IKnownFolderManager *folders = NULL;
  794. CoCreateInstance(&CLSID_KnownFolderManager, NULL, CLSCTX_INPROC_SERVER, &IID_IKnownFolderManager, (LPVOID *)&folders);
  795. IKnownFolder *folder = NULL;
  796. folders->lpVtbl->GetFolder(folders, &FOLDERID_SavedGames, &folder);
  797. LPWSTR path;
  798. folder->lpVtbl->GetPath(folder, 0, &path);
  799. wcscpy(savePathw, path);
  800. wcscat(savePathw, L"\\");
  801. wchar_t name[1024];
  802. MultiByteToWideChar(CP_UTF8, 0, iron_application_name(), -1, name, 1024);
  803. wcscat(savePathw, name);
  804. wcscat(savePathw, L"\\");
  805. SHCreateDirectoryExW(NULL, savePathw, NULL);
  806. WideCharToMultiByte(CP_UTF8, 0, savePathw, -1, savePath, 1024, NULL, NULL);
  807. CoTaskMemFree(path);
  808. folder->lpVtbl->Release(folder);
  809. folders->lpVtbl->Release(folders);
  810. }
  811. const char *iron_internal_save_path() {
  812. if (savePath[0] == 0)
  813. findSavePath();
  814. return savePath;
  815. }
  816. static const char *videoFormats[] = {"ogv", NULL};
  817. static LARGE_INTEGER frequency;
  818. static LARGE_INTEGER startCount;
  819. const char **iron_video_formats() {
  820. return videoFormats;
  821. }
  822. double iron_frequency() {
  823. return (double)frequency.QuadPart;
  824. }
  825. uint64_t iron_timestamp(void) {
  826. LARGE_INTEGER stamp;
  827. QueryPerformanceCounter(&stamp);
  828. return stamp.QuadPart - startCount.QuadPart;
  829. }
  830. double iron_time(void) {
  831. LARGE_INTEGER stamp;
  832. QueryPerformanceCounter(&stamp);
  833. return (double)(stamp.QuadPart - startCount.QuadPart) / (double)frequency.QuadPart;
  834. }
  835. #if !defined(IRON_NO_MAIN) && !defined(IRON_NO_CLIB)
  836. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
  837. int ret = kickstart(__argc, __argv);
  838. if (ret != 0) {
  839. #ifdef NDEBUG
  840. MessageBox(0, L"Unknown Error", L"Error", MB_OK);
  841. #else
  842. __debugbreak();
  843. #endif
  844. }
  845. return ret;
  846. }
  847. #endif
  848. void iron_init(iron_window_options_t *win) {
  849. initKeyTranslation();
  850. for (int i = 0; i < 256; ++i) {
  851. keyPressed[i] = false;
  852. }
  853. for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
  854. touchPoints[i].sysID = -1;
  855. touchPoints[i].x = -1;
  856. touchPoints[i].y = -1;
  857. }
  858. iron_display_init();
  859. QueryPerformanceCounter(&startCount);
  860. QueryPerformanceFrequency(&frequency);
  861. for (int i = 0; i < 256; ++i) {
  862. keyPressed[i] = false;
  863. }
  864. iron_set_app_name(win->title);
  865. iron_window_create(win);
  866. #ifdef WITH_GAMEPAD
  867. loadXInput();
  868. initializeDirectInput();
  869. #endif
  870. }
  871. void iron_internal_shutdown() {
  872. iron_windows_hide_windows();
  873. iron_internal_shutdown_callback();
  874. iron_windows_destroy_windows();
  875. gpu_destroy();
  876. iron_windows_restore_displays();
  877. }
  878. void iron_copy_to_clipboard(const char *text) {
  879. wchar_t wtext[4096];
  880. MultiByteToWideChar(CP_UTF8, 0, text, -1, wtext, 4096);
  881. OpenClipboard(iron_windows_window_handle(0));
  882. EmptyClipboard();
  883. size_t size = (wcslen(wtext) + 1) * sizeof(wchar_t);
  884. HANDLE handle = GlobalAlloc(GMEM_MOVEABLE, size);
  885. void *data = GlobalLock(handle);
  886. memcpy(data, wtext, size);
  887. GlobalUnlock(handle);
  888. SetClipboardData(CF_UNICODETEXT, handle);
  889. CloseClipboard();
  890. }
  891. int iron_hardware_threads(void) {
  892. SYSTEM_INFO sysinfo;
  893. GetSystemInfo(&sysinfo);
  894. return sysinfo.dwNumberOfProcessors;
  895. }
  896. struct HWND__;
  897. typedef unsigned long DWORD;
  898. // Dark mode
  899. #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
  900. #define DWMWA_USE_IMMERSIVE_DARK_MODE 20
  901. #endif
  902. struct HWND__ *iron_windows_window_handle();
  903. // Enable visual styles for ui controls
  904. #pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
  905. typedef struct {
  906. struct HWND__ *handle;
  907. int display_index;
  908. bool mouseInside;
  909. int index;
  910. int x, y, mode, bpp, frequency, features;
  911. int manualWidth, manualHeight;
  912. void (*resizeCallback)(int x, int y, void *data);
  913. void *resizeCallbackData;
  914. bool (*closeCallback)(void *data);
  915. void *closeCallbackData;
  916. } WindowData;
  917. LRESULT WINAPI IronWindowsMessageProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
  918. static WindowData windows[1] = {0};
  919. const wchar_t *windowClassName = L"IronWindow";
  920. static void RegisterWindowClass(HINSTANCE hInstance, const wchar_t *className) {
  921. WNDCLASSEXW wc = {sizeof(WNDCLASSEXA),
  922. CS_OWNDC /*CS_CLASSDC*/,
  923. IronWindowsMessageProcedure,
  924. 0L,
  925. 0L,
  926. hInstance,
  927. LoadIconW(hInstance, MAKEINTRESOURCEW(107)),
  928. LoadCursor(NULL, IDC_ARROW),
  929. CreateSolidBrush(0x00202020),
  930. 0,
  931. className,
  932. 0};
  933. RegisterClassExW(&wc);
  934. }
  935. static DWORD getStyle(int features) {
  936. DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP;
  937. if ((features & IRON_WINDOW_FEATURE_RESIZEABLE) && ((features & IRON_WINDOW_FEATURE_BORDERLESS) == 0)) {
  938. style |= WS_SIZEBOX;
  939. }
  940. if (features & IRON_WINDOW_FEATURE_MAXIMIZABLE) {
  941. style |= WS_MAXIMIZEBOX;
  942. }
  943. if (features & IRON_WINDOW_FEATURE_MINIMIZABLE) {
  944. style |= WS_MINIMIZEBOX;
  945. }
  946. if ((features & IRON_WINDOW_FEATURE_BORDERLESS) == 0) {
  947. style |= WS_CAPTION | WS_SYSMENU;
  948. }
  949. return style;
  950. }
  951. static DWORD getExStyle(int features) {
  952. DWORD exStyle = WS_EX_APPWINDOW;
  953. if ((features & IRON_WINDOW_FEATURE_BORDERLESS) == 0) {
  954. exStyle |= WS_EX_WINDOWEDGE;
  955. }
  956. if (features & IRON_WINDOW_FEATURE_ON_TOP) {
  957. exStyle |= WS_EX_TOPMOST;
  958. }
  959. return exStyle;
  960. }
  961. int iron_window_x() {
  962. RECT rect;
  963. GetWindowRect(windows[0].handle, &rect);
  964. windows[0].x = rect.left;
  965. return windows[0].x;
  966. }
  967. int iron_window_y() {
  968. RECT rect;
  969. GetWindowRect(windows[0].handle, &rect);
  970. windows[0].y = rect.top;
  971. return windows[0].y;
  972. }
  973. int iron_window_width() {
  974. RECT rect;
  975. GetClientRect(windows[0].handle, &rect);
  976. return rect.right;
  977. }
  978. int iron_window_height() {
  979. RECT rect;
  980. GetClientRect(windows[0].handle, &rect);
  981. return rect.bottom;
  982. }
  983. static DWORD getDwStyle(iron_window_mode_t mode, int features) {
  984. switch (mode) {
  985. case IRON_WINDOW_MODE_FULLSCREEN:
  986. return WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP;
  987. case IRON_WINDOW_MODE_WINDOW:
  988. default:
  989. return getStyle(features);
  990. }
  991. }
  992. static DWORD getDwExStyle(iron_window_mode_t mode, int features) {
  993. switch (mode) {
  994. case IRON_WINDOW_MODE_FULLSCREEN:
  995. return WS_EX_APPWINDOW;
  996. case IRON_WINDOW_MODE_WINDOW:
  997. default:
  998. return getExStyle(features);
  999. }
  1000. }
  1001. 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,
  1002. int target_display_index) {
  1003. HINSTANCE inst = GetModuleHandleW(NULL);
  1004. RegisterWindowClass(inst, windowClassName);
  1005. int display_index = target_display_index == -1 ? iron_primary_display() : target_display_index;
  1006. RECT WindowRect;
  1007. WindowRect.left = 0;
  1008. WindowRect.right = width;
  1009. WindowRect.top = 0;
  1010. WindowRect.bottom = height;
  1011. AdjustWindowRectEx(&WindowRect, getDwStyle(windowMode, features), FALSE, getDwExStyle(windowMode, features));
  1012. iron_display_mode_t display_mode = iron_display_current_mode(display_index);
  1013. int dstx = display_mode.x;
  1014. int dsty = display_mode.y;
  1015. int dstw = width;
  1016. int dsth = height;
  1017. switch (windowMode) {
  1018. case IRON_WINDOW_MODE_WINDOW:
  1019. dstx += x < 0 ? (display_mode.width - width) / 2 : x;
  1020. dsty += y < 0 ? (display_mode.height - height) / 2 : y;
  1021. dstw = WindowRect.right - WindowRect.left;
  1022. dsth = WindowRect.bottom - WindowRect.top;
  1023. break;
  1024. case IRON_WINDOW_MODE_FULLSCREEN:
  1025. dstw = display_mode.width;
  1026. dsth = display_mode.height;
  1027. break;
  1028. }
  1029. HWND hwnd = CreateWindowExW(getDwExStyle(windowMode, features), windowClassName, title, getDwStyle(windowMode, features), dstx, dsty, dstw, dsth, NULL, NULL,
  1030. inst, NULL);
  1031. SetCursor(LoadCursor(NULL, IDC_ARROW));
  1032. DragAcceptFiles(hwnd, true);
  1033. windows[0].handle = hwnd;
  1034. windows[0].x = dstx;
  1035. windows[0].y = dsty;
  1036. windows[0].mode = windowMode;
  1037. windows[0].display_index = display_index;
  1038. windows[0].bpp = bpp;
  1039. windows[0].frequency = frequency;
  1040. windows[0].features = features;
  1041. windows[0].manualWidth = width;
  1042. windows[0].manualHeight = height;
  1043. // Dark mode
  1044. char vdata[4];
  1045. DWORD cbdata = 4 * sizeof(char);
  1046. RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", L"AppsUseLightTheme", RRF_RT_REG_DWORD, NULL, vdata, &cbdata);
  1047. BOOL use_dark_mode = (int)(vdata[3] << 24 | vdata[2] << 16 | vdata[1] << 8 | vdata[0]) != 1;
  1048. DwmSetWindowAttribute(iron_windows_window_handle(0), DWMWA_USE_IMMERSIVE_DARK_MODE, &use_dark_mode, sizeof(use_dark_mode));
  1049. }
  1050. void iron_window_resize(int width, int height) {
  1051. WindowData *win = &windows[0];
  1052. win->manualWidth = width;
  1053. win->manualHeight = height;
  1054. switch (win->mode) {
  1055. case IRON_WINDOW_MODE_WINDOW: {
  1056. RECT rect;
  1057. rect.left = 0;
  1058. rect.top = 0;
  1059. rect.right = width;
  1060. rect.bottom = height;
  1061. AdjustWindowRectEx(&rect, getDwStyle((iron_window_mode_t)win->mode, win->features), FALSE, getDwExStyle((iron_window_mode_t)win->mode, win->features));
  1062. SetWindowPos(win->handle, NULL, iron_window_x(), iron_window_y(), rect.right - rect.left, rect.bottom - rect.top, 0);
  1063. break;
  1064. }
  1065. }
  1066. }
  1067. void iron_window_move(int x, int y) {
  1068. WindowData *win = &windows[0];
  1069. if (win->mode != 0) {
  1070. return;
  1071. }
  1072. win->x = x;
  1073. win->y = y;
  1074. RECT rect;
  1075. rect.left = 0;
  1076. rect.top = 0;
  1077. rect.right = iron_window_width();
  1078. rect.bottom = iron_window_height();
  1079. AdjustWindowRectEx(&rect, getDwStyle((iron_window_mode_t)win->mode, win->features), FALSE, getDwExStyle((iron_window_mode_t)win->mode, win->features));
  1080. SetWindowPos(win->handle, NULL, x, y, rect.right - rect.left, rect.bottom - rect.top, 0);
  1081. }
  1082. void iron_window_change_mode(iron_window_mode_t mode) {
  1083. WindowData *win = &windows[0];
  1084. int display_index = iron_window_display();
  1085. iron_display_mode_t display_mode = iron_display_current_mode(display_index);
  1086. switch (mode) {
  1087. case IRON_WINDOW_MODE_WINDOW: {
  1088. iron_windows_restore_display(display_index);
  1089. SetWindowLongW(win->handle, GWL_STYLE, getStyle(win->features));
  1090. SetWindowLongW(win->handle, GWL_EXSTYLE, getExStyle(win->features));
  1091. HWND on_top = (win->features & IRON_WINDOW_FEATURE_ON_TOP) ? HWND_TOPMOST : HWND_NOTOPMOST;
  1092. SetWindowPos(win->handle, on_top, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
  1093. iron_window_show();
  1094. break;
  1095. }
  1096. case IRON_WINDOW_MODE_FULLSCREEN: {
  1097. iron_windows_restore_display(display_index);
  1098. SetWindowLongW(win->handle, GWL_STYLE, WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP);
  1099. SetWindowLongW(win->handle, GWL_EXSTYLE, WS_EX_APPWINDOW);
  1100. SetWindowPos(win->handle, NULL, display_mode.x, display_mode.y, display_mode.width, display_mode.height, 0);
  1101. iron_window_show();
  1102. break;
  1103. }
  1104. }
  1105. win->mode = mode;
  1106. DragAcceptFiles(win->handle, true);
  1107. }
  1108. iron_window_mode_t iron_window_get_mode() {
  1109. return (iron_window_mode_t)windows[0].mode;
  1110. }
  1111. void iron_window_destroy() {
  1112. WindowData *win = &windows[0];
  1113. if (win->handle != NULL) {
  1114. DestroyWindow(win->handle);
  1115. win->handle = NULL;
  1116. }
  1117. }
  1118. void iron_windows_hide_windows(void) {
  1119. for (int i = 0; i < 1; ++i) {
  1120. if (windows[i].handle != NULL) {
  1121. ShowWindow(windows[i].handle, SW_HIDE);
  1122. UpdateWindow(windows[i].handle);
  1123. }
  1124. }
  1125. }
  1126. void iron_windows_destroy_windows(void) {
  1127. for (int i = 0; i < 1; ++i) {
  1128. iron_window_destroy(i);
  1129. }
  1130. UnregisterClassW(windowClassName, GetModuleHandleW(NULL));
  1131. }
  1132. void iron_window_show() {
  1133. ShowWindow(windows[0].handle, SW_SHOWDEFAULT);
  1134. UpdateWindow(windows[0].handle);
  1135. }
  1136. void iron_window_hide() {
  1137. ShowWindow(windows[0].handle, SW_HIDE);
  1138. UpdateWindow(windows[0].handle);
  1139. }
  1140. void iron_window_set_title(const char *title) {
  1141. wchar_t buffer[1024];
  1142. MultiByteToWideChar(CP_UTF8, 0, title, -1, buffer, 1024);
  1143. SetWindowTextW(windows[0].handle, buffer);
  1144. }
  1145. void iron_window_create(iron_window_options_t *win) {
  1146. wchar_t wbuffer[1024];
  1147. MultiByteToWideChar(CP_UTF8, 0, win->title, -1, wbuffer, 1024);
  1148. createWindow(wbuffer, win->x, win->y, win->width, win->height, win->color_bits, win->frequency, win->features, win->mode, win->display_index);
  1149. gpu_init(win->depth_bits, win->vsync);
  1150. if (win->visible) {
  1151. iron_window_show();
  1152. }
  1153. }
  1154. void iron_window_set_resize_callback(void (*callback)(int x, int y, void *data), void *data) {
  1155. windows[0].resizeCallback = callback;
  1156. windows[0].resizeCallbackData = data;
  1157. }
  1158. void iron_window_set_close_callback(bool (*callback)(void *data), void *data) {
  1159. windows[0].closeCallback = callback;
  1160. windows[0].closeCallbackData = data;
  1161. }
  1162. int iron_window_display() {
  1163. return iron_windows_get_display_for_monitor(MonitorFromWindow(windows[0].handle, MONITOR_DEFAULTTOPRIMARY));
  1164. }
  1165. struct HWND__ *iron_windows_window_handle() {
  1166. return windows[0].handle;
  1167. }
  1168. void iron_internal_call_resize_callback(int width, int height) {
  1169. if (windows[0].resizeCallback != NULL) {
  1170. windows[0].resizeCallback(width, height, windows[0].resizeCallbackData);
  1171. }
  1172. }
  1173. bool iron_internal_call_close_callback() {
  1174. if (windows[0].closeCallback != NULL) {
  1175. return windows[0].closeCallback(windows[0].closeCallbackData);
  1176. }
  1177. return true;
  1178. }
  1179. #ifdef WITH_GAMEPAD
  1180. bool iron_gamepad_connected(int num) {
  1181. return isXInputGamepad(num) || isDirectInputGamepad(num);
  1182. }
  1183. void iron_gamepad_rumble(int gamepad, float left, float right) {
  1184. if (isXInputGamepad(gamepad)) {
  1185. XINPUT_VIBRATION vibration;
  1186. memset(&vibration, 0, sizeof(XINPUT_VIBRATION));
  1187. vibration.wLeftMotorSpeed = (WORD)(65535.f * left);
  1188. vibration.wRightMotorSpeed = (WORD)(65535.f * right);
  1189. InputSetState(gamepad, &vibration);
  1190. }
  1191. }
  1192. #include <dinput.h>
  1193. #include <XInput.h>
  1194. static float axes[12 * 6];
  1195. static float buttons[12 * 16];
  1196. typedef DWORD(WINAPI *XInputGetStateType)(DWORD dwUserIndex, XINPUT_STATE *pState);
  1197. typedef DWORD(WINAPI *XInputSetStateType)(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration);
  1198. static XInputGetStateType InputGetState = NULL;
  1199. static XInputSetStateType InputSetState = NULL;
  1200. void loadXInput() {
  1201. HMODULE lib = LoadLibraryA("xinput1_4.dll");
  1202. if (lib == NULL) {
  1203. lib = LoadLibraryA("xinput1_3.dll");
  1204. }
  1205. if (lib == NULL) {
  1206. lib = LoadLibraryA("xinput9_1_0.dll");
  1207. }
  1208. if (lib != NULL) {
  1209. InputGetState = (XInputGetStateType)GetProcAddress(lib, "XInputGetState");
  1210. InputSetState = (XInputSetStateType)GetProcAddress(lib, "XInputSetState");
  1211. }
  1212. }
  1213. static IDirectInput8W *di_instance = NULL;
  1214. static IDirectInputDevice8W *di_pads[IRON_DINPUT_MAX_COUNT];
  1215. static DIJOYSTATE2 di_padState[IRON_DINPUT_MAX_COUNT];
  1216. static DIJOYSTATE2 di_lastPadState[IRON_DINPUT_MAX_COUNT];
  1217. static DIDEVCAPS di_deviceCaps[IRON_DINPUT_MAX_COUNT];
  1218. static int padCount = 0;
  1219. static void cleanupPad(int padIndex) {
  1220. if (di_pads[padIndex] != NULL) {
  1221. di_pads[padIndex]->lpVtbl->Unacquire(di_pads[padIndex]);
  1222. di_pads[padIndex]->lpVtbl->Release(di_pads[padIndex]);
  1223. di_pads[padIndex] = 0;
  1224. }
  1225. }
  1226. #ifndef SAFE_RELEASE
  1227. #define SAFE_RELEASE(x) \
  1228. if (x != NULL) { \
  1229. x->lpVtbl->Release(x); \
  1230. x = NULL; \
  1231. }
  1232. #endif
  1233. // From
  1234. //-----------------------------------------------------------------------------
  1235. // Enum each PNP device using WMI and check each device ID to see if it contains
  1236. // "IG_" (ex. "VID_045E&PID_028E&IG_00"). If it does, then it's an XInput device
  1237. // Unfortunately this information can not be found by just using DirectInput
  1238. //-----------------------------------------------------------------------------
  1239. static BOOL IsXInputDevice(const GUID *pGuidProductFromDirectInput) {
  1240. IWbemLocator *pIWbemLocator = NULL;
  1241. IEnumWbemClassObject *pEnumDevices = NULL;
  1242. IWbemClassObject *pDevices[20] = {0};
  1243. IWbemServices *pIWbemServices = NULL;
  1244. BSTR bstrNamespace = NULL;
  1245. BSTR bstrDeviceID = NULL;
  1246. BSTR bstrClassName = NULL;
  1247. DWORD uReturned = 0;
  1248. bool bIsXinputDevice = false;
  1249. UINT iDevice = 0;
  1250. VARIANT var;
  1251. HRESULT hr;
  1252. // CoInit if needed
  1253. hr = CoInitialize(NULL);
  1254. bool bCleanupCOM = SUCCEEDED(hr);
  1255. // Create WMI
  1256. hr = CoCreateInstance(&CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, &IID_IWbemLocator, (LPVOID *)&pIWbemLocator);
  1257. if (FAILED(hr) || pIWbemLocator == NULL)
  1258. goto LCleanup;
  1259. bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2");
  1260. if (bstrNamespace == NULL)
  1261. goto LCleanup;
  1262. bstrClassName = SysAllocString(L"Win32_PNPEntity");
  1263. if (bstrClassName == NULL)
  1264. goto LCleanup;
  1265. bstrDeviceID = SysAllocString(L"DeviceID");
  1266. if (bstrDeviceID == NULL)
  1267. goto LCleanup;
  1268. // Connect to WMI
  1269. hr = pIWbemLocator->lpVtbl->ConnectServer(pIWbemLocator, bstrNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pIWbemServices);
  1270. if (FAILED(hr) || pIWbemServices == NULL)
  1271. goto LCleanup;
  1272. // Switch security level to IMPERSONATE.
  1273. CoSetProxyBlanket((IUnknown *)pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL,
  1274. EOAC_NONE);
  1275. hr = pIWbemServices->lpVtbl->CreateInstanceEnum(pIWbemServices, bstrClassName, 0, NULL, &pEnumDevices);
  1276. if (FAILED(hr) || pEnumDevices == NULL)
  1277. goto LCleanup;
  1278. // Loop over all devices
  1279. for (;;) {
  1280. // Get 20 at a time
  1281. hr = pEnumDevices->lpVtbl->Next(pEnumDevices, 10000, 20, pDevices, &uReturned);
  1282. if (FAILED(hr))
  1283. goto LCleanup;
  1284. if (uReturned == 0)
  1285. break;
  1286. for (iDevice = 0; iDevice < uReturned; iDevice++) {
  1287. // For each device, get its device ID
  1288. hr = pDevices[iDevice]->lpVtbl->Get(pDevices[iDevice], bstrDeviceID, 0L, &var, NULL, NULL);
  1289. if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL) {
  1290. // Check if the device ID contains "IG_". If it does, then it's an XInput device
  1291. // This information can not be found from DirectInput
  1292. // TODO: Doesn't work with an Xbox Series X|S controller
  1293. if (wcsstr(var.bstrVal, L"IG_")) {
  1294. // If it does, then get the VID/PID from var.bstrVal
  1295. DWORD dwPid = 0, dwVid = 0;
  1296. WCHAR *strVid = wcsstr(var.bstrVal, L"VID_");
  1297. #ifndef IRON_NO_CLIB
  1298. if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1) {
  1299. dwVid = 0;
  1300. }
  1301. WCHAR *strPid = wcsstr(var.bstrVal, L"PID_");
  1302. if (strPid && swscanf(strPid, L"PID_%4X", &dwPid) != 1) {
  1303. dwPid = 0;
  1304. }
  1305. #endif
  1306. // Compare the VID/PID to the DInput device
  1307. DWORD dwVidPid = MAKELONG(dwVid, dwPid);
  1308. if (dwVidPid == pGuidProductFromDirectInput->Data1) {
  1309. bIsXinputDevice = true;
  1310. goto LCleanup;
  1311. }
  1312. }
  1313. }
  1314. SAFE_RELEASE(pDevices[iDevice]);
  1315. }
  1316. }
  1317. LCleanup:
  1318. if (bstrNamespace)
  1319. SysFreeString(bstrNamespace);
  1320. if (bstrDeviceID)
  1321. SysFreeString(bstrDeviceID);
  1322. if (bstrClassName)
  1323. SysFreeString(bstrClassName);
  1324. for (iDevice = 0; iDevice < 20; iDevice++)
  1325. SAFE_RELEASE(pDevices[iDevice]);
  1326. SAFE_RELEASE(pEnumDevices);
  1327. SAFE_RELEASE(pIWbemLocator);
  1328. SAFE_RELEASE(pIWbemServices);
  1329. if (bCleanupCOM)
  1330. CoUninitialize();
  1331. return bIsXinputDevice;
  1332. }
  1333. static void cleanupDirectInput() {
  1334. for (int padIndex = 0; padIndex < IRON_DINPUT_MAX_COUNT; ++padIndex) {
  1335. cleanupPad(padIndex);
  1336. }
  1337. if (di_instance != NULL) {
  1338. di_instance->lpVtbl->Release(di_instance);
  1339. di_instance = NULL;
  1340. }
  1341. }
  1342. static BOOL CALLBACK enumerateJoystickAxesCallback(LPCDIDEVICEOBJECTINSTANCEW ddoi, LPVOID context) {
  1343. HWND hwnd = (HWND)context;
  1344. DIPROPRANGE propertyRange;
  1345. propertyRange.diph.dwSize = sizeof(DIPROPRANGE);
  1346. propertyRange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1347. propertyRange.diph.dwHow = DIPH_BYID;
  1348. propertyRange.diph.dwObj = ddoi->dwType;
  1349. propertyRange.lMin = -32768;
  1350. propertyRange.lMax = 32768;
  1351. HRESULT hr = di_pads[padCount]->lpVtbl->SetProperty(di_pads[padCount], DIPROP_RANGE, &propertyRange.diph);
  1352. if (FAILED(hr)) {
  1353. iron_log("DirectInput8 / Pad%i / SetProperty() failed (HRESULT=0x%x)", padCount, hr);
  1354. return DIENUM_STOP;
  1355. }
  1356. return DIENUM_CONTINUE;
  1357. }
  1358. static BOOL CALLBACK enumerateJoysticksCallback(LPCDIDEVICEINSTANCEW ddi, LPVOID context) {
  1359. if (padCount < XUSER_MAX_COUNT && IsXInputDevice(&ddi->guidProduct)) {
  1360. ++padCount;
  1361. return DIENUM_CONTINUE;
  1362. }
  1363. HRESULT hr = di_instance->lpVtbl->CreateDevice(di_instance, &ddi->guidInstance, &di_pads[padCount], NULL);
  1364. if (SUCCEEDED(hr)) {
  1365. hr = di_pads[padCount]->lpVtbl->SetDataFormat(di_pads[padCount], &c_dfDIJoystick2);
  1366. if (SUCCEEDED(hr)) {
  1367. di_deviceCaps[padCount].dwSize = sizeof(DIDEVCAPS);
  1368. hr = di_pads[padCount]->lpVtbl->GetCapabilities(di_pads[padCount], &di_deviceCaps[padCount]);
  1369. if (SUCCEEDED(hr)) {
  1370. hr = di_pads[padCount]->lpVtbl->EnumObjects(di_pads[padCount], enumerateJoystickAxesCallback, NULL, DIDFT_AXIS);
  1371. if (SUCCEEDED(hr)) {
  1372. hr = di_pads[padCount]->lpVtbl->Acquire(di_pads[padCount]);
  1373. if (SUCCEEDED(hr)) {
  1374. memset(&di_padState[padCount], 0, sizeof(DIJOYSTATE2));
  1375. hr = di_pads[padCount]->lpVtbl->GetDeviceState(di_pads[padCount], sizeof(DIJOYSTATE2), &di_padState[padCount]);
  1376. if (SUCCEEDED(hr)) {
  1377. iron_log("DirectInput8 / Pad%i / initialized", padCount);
  1378. }
  1379. else {
  1380. iron_log("DirectInput8 / Pad%i / GetDeviceState() failed (HRESULT=0x%x)", padCount, hr);
  1381. }
  1382. }
  1383. else {
  1384. iron_log("DirectInput8 / Pad%i / Acquire() failed (HRESULT=0x%x)", padCount, hr);
  1385. cleanupPad(padCount);
  1386. }
  1387. }
  1388. else {
  1389. iron_log("DirectInput8 / Pad%i / EnumObjects(DIDFT_AXIS) failed (HRESULT=0x%x)", padCount, hr);
  1390. cleanupPad(padCount);
  1391. }
  1392. }
  1393. else {
  1394. iron_log("DirectInput8 / Pad%i / GetCapabilities() failed (HRESULT=0x%x)", padCount, hr);
  1395. cleanupPad(padCount);
  1396. }
  1397. }
  1398. else {
  1399. iron_log("DirectInput8 / Pad%i / SetDataFormat() failed (HRESULT=0x%x)", padCount, hr);
  1400. cleanupPad(padCount);
  1401. }
  1402. ++padCount;
  1403. if (padCount >= IRON_DINPUT_MAX_COUNT) {
  1404. return DIENUM_STOP;
  1405. }
  1406. }
  1407. return DIENUM_CONTINUE;
  1408. }
  1409. static void initializeDirectInput() {
  1410. HINSTANCE hinstance = GetModuleHandleW(NULL);
  1411. memset(&di_pads, 0, sizeof(IDirectInputDevice8) * IRON_DINPUT_MAX_COUNT);
  1412. memset(&di_padState, 0, sizeof(DIJOYSTATE2) * IRON_DINPUT_MAX_COUNT);
  1413. memset(&di_lastPadState, 0, sizeof(DIJOYSTATE2) * IRON_DINPUT_MAX_COUNT);
  1414. memset(&di_deviceCaps, 0, sizeof(DIDEVCAPS) * IRON_DINPUT_MAX_COUNT);
  1415. // #define DIRECTINPUT_VERSION 0x0800
  1416. HRESULT hr = DirectInput8Create(hinstance, 0x0800, &IID_IDirectInput8W, (void **)&di_instance, NULL);
  1417. if (SUCCEEDED(hr)) {
  1418. hr = di_instance->lpVtbl->EnumDevices(di_instance, DI8DEVCLASS_GAMECTRL, enumerateJoysticksCallback, NULL, DIEDFL_ATTACHEDONLY);
  1419. if (SUCCEEDED(hr)) {
  1420. }
  1421. else {
  1422. cleanupDirectInput();
  1423. }
  1424. }
  1425. else {
  1426. iron_log("DirectInput8Create failed (HRESULT=0x%x)", hr);
  1427. }
  1428. }
  1429. bool handleDirectInputPad(int padIndex) {
  1430. if (di_pads[padIndex] == NULL) {
  1431. return false;
  1432. }
  1433. HRESULT hr = di_pads[padIndex]->lpVtbl->GetDeviceState(di_pads[padIndex], sizeof(DIJOYSTATE2), &di_padState[padIndex]);
  1434. switch (hr) {
  1435. case S_OK: {
  1436. for (int axisIndex = 0; axisIndex < 2; ++axisIndex) {
  1437. LONG *now = NULL;
  1438. LONG *last = NULL;
  1439. switch (axisIndex) {
  1440. case 0: {
  1441. now = &di_padState[padIndex].lX;
  1442. last = &di_lastPadState[padIndex].lX;
  1443. } break;
  1444. case 1: {
  1445. now = &di_padState[padIndex].lY;
  1446. last = &di_lastPadState[padIndex].lY;
  1447. } break;
  1448. case 2: {
  1449. now = &di_padState[padIndex].lZ;
  1450. last = &di_lastPadState[padIndex].lZ;
  1451. } break;
  1452. }
  1453. if (*now != *last) {
  1454. iron_internal_gamepad_trigger_axis(padIndex, axisIndex, *now / 32768.0f);
  1455. }
  1456. }
  1457. for (int buttonIndex = 0; buttonIndex < 128; ++buttonIndex) {
  1458. BYTE *now = &di_padState[padIndex].rgbButtons[buttonIndex];
  1459. BYTE *last = &di_lastPadState[padIndex].rgbButtons[buttonIndex];
  1460. if (*now != *last) {
  1461. iron_internal_gamepad_trigger_button(padIndex, buttonIndex, *now / 255.0f);
  1462. }
  1463. }
  1464. for (int povIndex = 0; povIndex < 4; ++povIndex) {
  1465. DWORD *now = &di_padState[padIndex].rgdwPOV[povIndex];
  1466. DWORD *last = &di_lastPadState[padIndex].rgdwPOV[povIndex];
  1467. bool up = (*now == 0 || *now == 31500 || *now == 4500);
  1468. bool down = (*now == 18000 || *now == 13500 || *now == 22500);
  1469. bool left = (*now == 27000 || *now == 22500 || *now == 31500);
  1470. bool right = (*now == 9000 || *now == 4500 || *now == 13500);
  1471. bool lastUp = (*last == 0 || *last == 31500 || *last == 4500);
  1472. bool lastDown = (*last == 18000 || *last == 13500 || *last == 22500);
  1473. bool lastLeft = (*last == 27000 || *last == 22500 || *last == 31500);
  1474. bool lastRight = (*last == 9000 || *last == 4500 || *last == 13500);
  1475. if (up != lastUp) {
  1476. iron_internal_gamepad_trigger_button(padIndex, 12, up ? 1.0f : 0.0f);
  1477. }
  1478. if (down != lastDown) {
  1479. iron_internal_gamepad_trigger_button(padIndex, 13, down ? 1.0f : 0.0f);
  1480. }
  1481. if (left != lastLeft) {
  1482. iron_internal_gamepad_trigger_button(padIndex, 14, left ? 1.0f : 0.0f);
  1483. }
  1484. if (right != lastRight) {
  1485. iron_internal_gamepad_trigger_button(padIndex, 15, right ? 1.0f : 0.0f);
  1486. }
  1487. }
  1488. memcpy(&di_lastPadState[padIndex], &di_padState[padIndex], sizeof(DIJOYSTATE2));
  1489. break;
  1490. }
  1491. case DIERR_INPUTLOST: // fall through
  1492. case DIERR_NOTACQUIRED: {
  1493. hr = di_pads[padIndex]->lpVtbl->Acquire(di_pads[padIndex]);
  1494. break;
  1495. }
  1496. }
  1497. return hr == S_OK;
  1498. }
  1499. static bool isXInputGamepad(int gamepad) {
  1500. //if gamepad is greater than XInput max, treat it as DINPUT.
  1501. if (gamepad >= XUSER_MAX_COUNT)
  1502. {
  1503. return false;
  1504. }
  1505. XINPUT_STATE state;
  1506. memset(&state, 0, sizeof(XINPUT_STATE));
  1507. DWORD dwResult = InputGetState(gamepad, &state);
  1508. return dwResult == ERROR_SUCCESS;
  1509. }
  1510. static bool isDirectInputGamepad(int gamepad) {
  1511. if (di_pads[gamepad] == NULL) {
  1512. return false;
  1513. }
  1514. HRESULT hr = di_pads[gamepad]->lpVtbl->GetDeviceState(di_pads[gamepad], sizeof(DIJOYSTATE2), &di_padState[gamepad]);
  1515. return hr == S_OK;
  1516. }
  1517. const char *iron_gamepad_vendor(int gamepad) {
  1518. if (isXInputGamepad(gamepad)) {
  1519. return "Microsoft";
  1520. }
  1521. else {
  1522. return "DirectInput8";
  1523. }
  1524. }
  1525. const char *iron_gamepad_product_name(int gamepad) {
  1526. if (isXInputGamepad(gamepad)) {
  1527. return "Xbox 360 Controller";
  1528. }
  1529. else {
  1530. return "Generic Gamepad";
  1531. }
  1532. }
  1533. void iron_gamepad_handle_messages() {
  1534. if (InputGetState != NULL && (detectGamepad || gamepadFound)) {
  1535. detectGamepad = false;
  1536. for (DWORD i = 0; i < IRON_DINPUT_MAX_COUNT; ++i) {
  1537. XINPUT_STATE state;
  1538. memset(&state, 0, sizeof(XINPUT_STATE));
  1539. DWORD dwResult = InputGetState(i, &state);
  1540. if (dwResult == ERROR_SUCCESS) {
  1541. gamepadFound = true;
  1542. float newaxes[6];
  1543. newaxes[0] = state.Gamepad.sThumbLX / 32768.0f;
  1544. newaxes[1] = state.Gamepad.sThumbLY / 32768.0f;
  1545. newaxes[2] = state.Gamepad.sThumbRX / 32768.0f;
  1546. newaxes[3] = state.Gamepad.sThumbRY / 32768.0f;
  1547. newaxes[4] = state.Gamepad.bLeftTrigger / 255.0f;
  1548. newaxes[5] = state.Gamepad.bRightTrigger / 255.0f;
  1549. for (int i2 = 0; i2 < 6; ++i2) {
  1550. if (axes[i * 6 + i2] != newaxes[i2]) {
  1551. iron_internal_gamepad_trigger_axis(i, i2, newaxes[i2]);
  1552. axes[i * 6 + i2] = newaxes[i2];
  1553. }
  1554. }
  1555. float newbuttons[16];
  1556. newbuttons[0] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_A) ? 1.0f : 0.0f;
  1557. newbuttons[1] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_B) ? 1.0f : 0.0f;
  1558. newbuttons[2] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_X) ? 1.0f : 0.0f;
  1559. newbuttons[3] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_Y) ? 1.0f : 0.0f;
  1560. newbuttons[4] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) ? 1.0f : 0.0f;
  1561. newbuttons[5] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) ? 1.0f : 0.0f;
  1562. newbuttons[6] = state.Gamepad.bLeftTrigger / 255.0f;
  1563. newbuttons[7] = state.Gamepad.bRightTrigger / 255.0f;
  1564. newbuttons[8] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) ? 1.0f : 0.0f;
  1565. newbuttons[9] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_START) ? 1.0f : 0.0f;
  1566. newbuttons[10] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) ? 1.0f : 0.0f;
  1567. newbuttons[11] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) ? 1.0f : 0.0f;
  1568. newbuttons[12] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) ? 1.0f : 0.0f;
  1569. newbuttons[13] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) ? 1.0f : 0.0f;
  1570. newbuttons[14] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) ? 1.0f : 0.0f;
  1571. newbuttons[15] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) ? 1.0f : 0.0f;
  1572. for (int i2 = 0; i2 < 16; ++i2) {
  1573. if (buttons[i * 16 + i2] != newbuttons[i2]) {
  1574. iron_internal_gamepad_trigger_button(i, i2, newbuttons[i2]);
  1575. buttons[i * 16 + i2] = newbuttons[i2];
  1576. }
  1577. }
  1578. }
  1579. else {
  1580. if (handleDirectInputPad(i)) {
  1581. gamepadFound = true;
  1582. }
  1583. }
  1584. }
  1585. }
  1586. }
  1587. #endif