linux_system.c 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448
  1. #include "linux_system.h"
  2. #include <iron_gpu.h>
  3. #include <iron_video.h>
  4. #include <iron_system.h>
  5. #include <assert.h>
  6. #include <limits.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <errno.h>
  11. #include <pwd.h>
  12. #include <unistd.h>
  13. #include <ctype.h>
  14. #include <time.h>
  15. #include <dlfcn.h>
  16. #include <X11/Xlib.h>
  17. #include <sys/stat.h>
  18. #include <sys/time.h>
  19. #include <vulkan/vulkan.h>
  20. #include <vulkan/vulkan_xlib.h>
  21. #define Button6 6
  22. #define Button7 7
  23. struct iron_x11_procs xlib = {0};
  24. struct x11_context x11_ctx = {0};
  25. static size_t clipboardStringSize = 1024;
  26. static char *clipboardString = NULL;
  27. static char buffer[1024];
  28. static char save[2000];
  29. static bool saveInitialized = false;
  30. static const char *videoFormats[] = {"ogv", NULL};
  31. static struct timeval start;
  32. static bool mouse_hidden = false;
  33. static void init_pen_device(XDeviceInfo *info, struct x11_pen_device *pen, bool eraser);
  34. bool iron_x11_init();
  35. void iron_display_init() {
  36. static bool display_initialized = false;
  37. if (display_initialized) {
  38. return;
  39. }
  40. iron_x11_init();
  41. Window root_window = RootWindow(x11_ctx.display, DefaultScreen(x11_ctx.display));
  42. XRRScreenResources *screen_resources = xlib.XRRGetScreenResourcesCurrent(x11_ctx.display, root_window);
  43. RROutput primary_output = xlib.XRRGetOutputPrimary(x11_ctx.display, root_window);
  44. for (int i = 0; i < screen_resources->noutput; i++) {
  45. if (i >= MAXIMUM_DISPLAYS) {
  46. iron_error("Too many screens (maximum %i)", MAXIMUM_DISPLAYS);
  47. break;
  48. }
  49. XRROutputInfo *output_info = xlib.XRRGetOutputInfo(x11_ctx.display, screen_resources, screen_resources->outputs[i]);
  50. if (output_info->connection != RR_Connected || output_info->crtc == None) {
  51. xlib.XRRFreeOutputInfo(output_info);
  52. continue;
  53. }
  54. XRRCrtcInfo *crtc_info = xlib.XRRGetCrtcInfo(x11_ctx.display, screen_resources, output_info->crtc);
  55. struct iron_x11_display *display = &x11_ctx.displays[x11_ctx.num_displays++];
  56. display->index = i;
  57. display->x = crtc_info->x;
  58. display->y = crtc_info->y;
  59. display->width = crtc_info->width;
  60. display->height = crtc_info->height;
  61. display->primary = screen_resources->outputs[i] == primary_output;
  62. display->crtc = output_info->crtc;
  63. display->output = screen_resources->outputs[i];
  64. xlib.XRRFreeOutputInfo(output_info);
  65. xlib.XRRFreeCrtcInfo(crtc_info);
  66. }
  67. xlib.XRRFreeScreenResources(screen_resources);
  68. display_initialized = true;
  69. }
  70. iron_display_mode_t iron_display_current_mode(int display_index) {
  71. struct iron_x11_display *display = &x11_ctx.displays[display_index];
  72. iron_display_mode_t mode;
  73. mode.x = 0;
  74. mode.y = 0;
  75. mode.width = display->width;
  76. mode.height = display->height;
  77. mode.frequency = 60;
  78. mode.bits_per_pixel = 32;
  79. mode.pixels_per_inch = 96;
  80. Window root_window = DefaultRootWindow(x11_ctx.display);
  81. XRRScreenResources *screen_resources = xlib.XRRGetScreenResourcesCurrent(x11_ctx.display, root_window);
  82. XRROutputInfo *output_info = xlib.XRRGetOutputInfo(x11_ctx.display, screen_resources, screen_resources->outputs[display->index]);
  83. if (output_info->connection != RR_Connected || output_info->crtc == None) {
  84. xlib.XRRFreeOutputInfo(output_info);
  85. xlib.XRRFreeScreenResources(screen_resources);
  86. return mode;
  87. }
  88. XRRCrtcInfo *crtc_info = xlib.XRRGetCrtcInfo(x11_ctx.display, screen_resources, output_info->crtc);
  89. for (int j = 0; j < output_info->nmode; j++) {
  90. RRMode rr_mode = crtc_info->mode;
  91. XRRModeInfo *mode_info = NULL;
  92. for (int k = 0; k < screen_resources->nmode; k++) {
  93. if (screen_resources->modes[k].id == rr_mode) {
  94. mode_info = &screen_resources->modes[k];
  95. break;
  96. }
  97. }
  98. if (mode_info == NULL) {
  99. continue;
  100. }
  101. mode.x = display->x;
  102. mode.y = display->y;
  103. mode.width = mode_info->width;
  104. mode.height = mode_info->height;
  105. mode.pixels_per_inch = 96;
  106. mode.bits_per_pixel = 32;
  107. if (mode_info->hTotal && mode_info->vTotal) {
  108. mode.frequency = (mode_info->dotClock / (mode_info->hTotal * mode_info->vTotal));
  109. }
  110. }
  111. xlib.XRRFreeOutputInfo(output_info);
  112. xlib.XRRFreeCrtcInfo(crtc_info);
  113. xlib.XRRFreeScreenResources(screen_resources);
  114. return mode;
  115. }
  116. int iron_primary_display(void) {
  117. for (int i = 0; i < x11_ctx.num_displays; i++) {
  118. if (x11_ctx.displays[i].primary) {
  119. return i;
  120. }
  121. }
  122. return 0;
  123. }
  124. int iron_count_displays(void) {
  125. return x11_ctx.num_displays;
  126. }
  127. int iron_window_x() {
  128. return 0;
  129. }
  130. int iron_window_y() {
  131. return 0;
  132. }
  133. int iron_window_width() {
  134. return x11_ctx.windows[0].width;
  135. }
  136. int iron_window_height() {
  137. return x11_ctx.windows[0].height;
  138. }
  139. void iron_window_resize(int width, int height) {
  140. struct iron_x11_window *window = &x11_ctx.windows[0];
  141. xlib.XResizeWindow(x11_ctx.display, window->window, width, height);
  142. }
  143. void iron_window_move(int x, int y) {
  144. struct iron_x11_window *window = &x11_ctx.windows[0];
  145. xlib.XMoveWindow(x11_ctx.display, window->window, x, y);
  146. }
  147. void iron_window_change_mode(iron_window_mode_t mode) {
  148. struct iron_x11_window *window = &x11_ctx.windows[0];
  149. if (mode == window->mode) {
  150. return;
  151. }
  152. window->mode = mode;
  153. XEvent xev;
  154. memset(&xev, 0, sizeof(xev));
  155. xev.type = ClientMessage;
  156. xev.xclient.window = window->window;
  157. xev.xclient.message_type = x11_ctx.atoms.NET_WM_STATE;
  158. xev.xclient.format = 32;
  159. xev.xclient.data.l[0] = mode == IRON_WINDOW_MODE_FULLSCREEN ? 1 : 0;
  160. xev.xclient.data.l[1] = x11_ctx.atoms.NET_WM_STATE_FULLSCREEN;
  161. xev.xclient.data.l[2] = 0;
  162. xlib.XMapWindow(x11_ctx.display, window->window);
  163. xlib.XSendEvent(x11_ctx.display, DefaultRootWindow(x11_ctx.display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
  164. xlib.XFlush(x11_ctx.display);
  165. }
  166. int iron_window_display() {
  167. struct iron_x11_window *window = &x11_ctx.windows[0];
  168. return window->display_index;
  169. }
  170. void iron_window_destroy() {
  171. xlib.XFlush(x11_ctx.display);
  172. struct iron_x11_window *window = &x11_ctx.windows[0];
  173. xlib.XDestroyIC(window->xInputContext);
  174. xlib.XCloseIM(window->xInputMethod);
  175. xlib.XDestroyWindow(x11_ctx.display, window->window);
  176. xlib.XFlush(x11_ctx.display);
  177. *window = (struct iron_x11_window){0};
  178. }
  179. void iron_window_show() {
  180. struct iron_x11_window *window = &x11_ctx.windows[0];
  181. xlib.XMapWindow(x11_ctx.display, window->window);
  182. }
  183. void iron_window_hide() {
  184. struct iron_x11_window *window = &x11_ctx.windows[0];
  185. xlib.XUnmapWindow(x11_ctx.display, window->window);
  186. }
  187. void iron_window_set_title(const char *title) {
  188. struct iron_x11_window *window = &x11_ctx.windows[0];
  189. xlib.XChangeProperty(x11_ctx.display, window->window, x11_ctx.atoms.NET_WM_NAME, x11_ctx.atoms.UTF8_STRING, 8, PropModeReplace, (unsigned char *)title, strlen(title));
  190. xlib.XChangeProperty(x11_ctx.display, window->window, x11_ctx.atoms.NET_WM_ICON_NAME, x11_ctx.atoms.UTF8_STRING, 8, PropModeReplace, (unsigned char *)title, strlen(title));
  191. xlib.XFlush(x11_ctx.display);
  192. }
  193. void iron_window_create(iron_window_options_t *win) {
  194. struct iron_x11_window *window = &x11_ctx.windows[0];
  195. window->width = win->width;
  196. window->height = win->height;
  197. Visual *visual = NULL;
  198. XSetWindowAttributes set_window_attribs = {0};
  199. XColor color;
  200. Colormap colormap = DefaultColormap(x11_ctx.display, DefaultScreen(x11_ctx.display));
  201. color.red = (32 * 65535) / 255;
  202. color.green = (32 * 65535) / 255;
  203. color.blue = (32 * 65535) / 255;
  204. xlib.XAllocColor(x11_ctx.display, colormap, &color);
  205. set_window_attribs.background_pixel = color.pixel;
  206. set_window_attribs.border_pixel = 0;
  207. set_window_attribs.event_mask =
  208. KeyPressMask | KeyReleaseMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask;
  209. int screen = DefaultScreen(x11_ctx.display);
  210. visual = DefaultVisual(x11_ctx.display, screen);
  211. int depth = DefaultDepth(x11_ctx.display, screen);
  212. set_window_attribs.colormap = xlib.XCreateColormap(x11_ctx.display, RootWindow(x11_ctx.display, screen), visual, AllocNone);
  213. window->window = xlib.XCreateWindow(x11_ctx.display, RootWindow(x11_ctx.display, DefaultScreen(x11_ctx.display)), 0, 0, win->width, win->height, 0, depth,
  214. InputOutput, visual, CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &set_window_attribs);
  215. static char nameClass[256];
  216. static const char *nameClassAddendum = "_IronApplication";
  217. strncpy(nameClass, iron_application_name(), sizeof(nameClass) - strlen(nameClassAddendum) - 1);
  218. strcat(nameClass, nameClassAddendum);
  219. char resNameBuffer[256];
  220. strncpy(resNameBuffer, iron_application_name(), 256);
  221. XClassHint classHint = {.res_name = resNameBuffer, .res_class = nameClass};
  222. xlib.XSetClassHint(x11_ctx.display, window->window, &classHint);
  223. xlib.XSetLocaleModifiers("@im=none");
  224. window->xInputMethod = xlib.XOpenIM(x11_ctx.display, NULL, NULL, NULL);
  225. window->xInputContext = xlib.XCreateIC(window->xInputMethod, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, window->window, NULL);
  226. xlib.XSetICFocus(window->xInputContext);
  227. window->mode = IRON_WINDOW_MODE_WINDOW;
  228. iron_window_change_mode(win->mode);
  229. xlib.XMapWindow(x11_ctx.display, window->window);
  230. Atom XdndVersion = 5;
  231. xlib.XChangeProperty(x11_ctx.display, window->window, x11_ctx.atoms.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&XdndVersion, 1);
  232. xlib.XSetWMProtocols(x11_ctx.display, window->window, &x11_ctx.atoms.WM_DELETE_WINDOW, 1);
  233. iron_window_set_title(win->title);
  234. if (x11_ctx.pen.id != -1) {
  235. xlib.XSelectExtensionEvent(x11_ctx.display, window->window, &x11_ctx.pen.motionClass, 1);
  236. }
  237. if (x11_ctx.eraser.id != -1) {
  238. xlib.XSelectExtensionEvent(x11_ctx.display, window->window, &x11_ctx.eraser.motionClass, 1);
  239. }
  240. gpu_init(win->depth_bits, win->vsync);
  241. }
  242. static struct {
  243. void (*resize_callback)(int width, int height, void *data);
  244. void *resize_data;
  245. void *ppi_data;
  246. bool (*close_callback)(void *data);
  247. void *close_data;
  248. } iron_internal_window_callbacks[1];
  249. void iron_window_set_resize_callback(void (*callback)(int width, int height, void *data), void *data) {
  250. iron_internal_window_callbacks[0].resize_callback = callback;
  251. iron_internal_window_callbacks[0].resize_data = data;
  252. }
  253. void iron_internal_call_resize_callback(int width, int height) {
  254. if (iron_internal_window_callbacks[0].resize_callback != NULL) {
  255. iron_internal_window_callbacks[0].resize_callback(width, height, iron_internal_window_callbacks[0].resize_data);
  256. }
  257. }
  258. void iron_window_set_close_callback(bool (*callback)(void *data), void *data) {
  259. iron_internal_window_callbacks[0].close_callback = callback;
  260. iron_internal_window_callbacks[0].close_data = data;
  261. }
  262. bool iron_internal_call_close_callback() {
  263. if (iron_internal_window_callbacks[0].close_callback != NULL) {
  264. return iron_internal_window_callbacks[0].close_callback(iron_internal_window_callbacks[0].close_data);
  265. }
  266. else {
  267. return true;
  268. }
  269. }
  270. iron_window_mode_t iron_window_get_mode() {
  271. return x11_ctx.windows[0].mode;
  272. }
  273. static void load_lib(void **lib, const char *name) {
  274. char libname[64];
  275. sprintf(libname, "lib%s.so", name);
  276. *lib = dlopen(libname, RTLD_LAZY);
  277. if (*lib != NULL) {
  278. return;
  279. }
  280. // Ubuntu and Fedora only ship libFoo.so.major by default, so look for those.
  281. for (int i = 0; i < 10; i++) {
  282. sprintf(libname, "lib%s.so.%i", name, i);
  283. *lib = dlopen(libname, RTLD_LAZY);
  284. if (*lib != NULL) {
  285. return;
  286. }
  287. }
  288. iron_error("Failed to load lib%s.so", name);
  289. }
  290. int iron_x11_error_handler(Display *display, XErrorEvent *error_event) {
  291. xlib.XGetErrorText(display, error_event->error_code, buffer, 1024);
  292. iron_error("X Error: %s", buffer);
  293. return 0;
  294. }
  295. bool iron_x11_init() {
  296. load_lib(&x11_ctx.libs.X11, "X11");
  297. load_lib(&x11_ctx.libs.Xi, "Xi");
  298. load_lib(&x11_ctx.libs.Xcursor, "Xcursor");
  299. load_lib(&x11_ctx.libs.Xrandr, "Xrandr");
  300. xlib.XOpenDisplay = dlsym(x11_ctx.libs.X11, "XOpenDisplay");
  301. xlib.XCloseDisplay = dlsym(x11_ctx.libs.X11, "XCloseDisplay");
  302. xlib.XInitThreads = dlsym(x11_ctx.libs.X11, "XInitThreads");
  303. xlib.XSetErrorHandler = dlsym(x11_ctx.libs.X11, "XSetErrorHandler");
  304. xlib.XGetErrorText = dlsym(x11_ctx.libs.X11, "XGetErrorText");
  305. xlib.XInternAtoms = dlsym(x11_ctx.libs.X11, "XInternAtoms");
  306. xlib.XPending = dlsym(x11_ctx.libs.X11, "XPending");
  307. xlib.XFlush = dlsym(x11_ctx.libs.X11, "XFlush");
  308. xlib.XNextEvent = dlsym(x11_ctx.libs.X11, "XNextEvent");
  309. xlib.XRefreshKeyboardMapping = dlsym(x11_ctx.libs.X11, "XRefreshKeyboardMapping");
  310. xlib.XwcLookupString = dlsym(x11_ctx.libs.X11, "XwcLookupString");
  311. xlib.XFilterEvent = dlsym(x11_ctx.libs.X11, "XFilterEvent");
  312. xlib.XConvertSelection = dlsym(x11_ctx.libs.X11, "XConvertSelection");
  313. xlib.XSetSelectionOwner = dlsym(x11_ctx.libs.X11, "XSetSelectionOwner");
  314. xlib.XLookupString = dlsym(x11_ctx.libs.X11, "XLookupString");
  315. xlib.XkbKeycodeToKeysym = dlsym(x11_ctx.libs.X11, "XkbKeycodeToKeysym");
  316. xlib.XSendEvent = dlsym(x11_ctx.libs.X11, "XSendEvent");
  317. xlib.XGetWindowProperty = dlsym(x11_ctx.libs.X11, "XGetWindowProperty");
  318. xlib.XFree = dlsym(x11_ctx.libs.X11, "XFree");
  319. xlib.XChangeProperty = dlsym(x11_ctx.libs.X11, "XChangeProperty");
  320. xlib.XDefineCursor = dlsym(x11_ctx.libs.X11, "XDefineCursor");
  321. xlib.XUndefineCursor = dlsym(x11_ctx.libs.X11, "XUndefineCursor");
  322. xlib.XCreateBitmapFromData = dlsym(x11_ctx.libs.X11, "XCreateBitmapFromData");
  323. xlib.XCreatePixmapCursor = dlsym(x11_ctx.libs.X11, "XCreatePixmapCursor");
  324. xlib.XFreePixmap = dlsym(x11_ctx.libs.X11, "XFreePixmap");
  325. xlib.XWarpPointer = dlsym(x11_ctx.libs.X11, "XWarpPointer");
  326. xlib.XQueryPointer = dlsym(x11_ctx.libs.X11, "XQueryPointer");
  327. xlib.XCreateColormap = dlsym(x11_ctx.libs.X11, "XCreateColormap");
  328. xlib.XCreateWindow = dlsym(x11_ctx.libs.X11, "XCreateWindow");
  329. xlib.XMoveWindow = dlsym(x11_ctx.libs.X11, "XMoveWindow");
  330. xlib.XResizeWindow = dlsym(x11_ctx.libs.X11, "XResizeWindow");
  331. xlib.XDestroyWindow = dlsym(x11_ctx.libs.X11, "XDestroyWindow");
  332. xlib.XSetClassHint = dlsym(x11_ctx.libs.X11, "XSetClassHint");
  333. xlib.XSetLocaleModifiers = dlsym(x11_ctx.libs.X11, "XSetLocaleModifiers");
  334. xlib.XOpenIM = dlsym(x11_ctx.libs.X11, "XOpenIM");
  335. xlib.XCloseIM = dlsym(x11_ctx.libs.X11, "XCloseIM");
  336. xlib.XCreateIC = dlsym(x11_ctx.libs.X11, "XCreateIC");
  337. xlib.XDestroyIC = dlsym(x11_ctx.libs.X11, "XDestroyIC");
  338. xlib.XSetICFocus = dlsym(x11_ctx.libs.X11, "XSetICFocus");
  339. xlib.XMapWindow = dlsym(x11_ctx.libs.X11, "XMapWindow");
  340. xlib.XUnmapWindow = dlsym(x11_ctx.libs.X11, "XUnmapWindow");
  341. xlib.XSetWMProtocols = dlsym(x11_ctx.libs.X11, "XSetWMProtocols");
  342. xlib.XPeekEvent = dlsym(x11_ctx.libs.X11, "XPeekEvent");
  343. xlib.XAllocColor = dlsym(x11_ctx.libs.X11, "XAllocColor");
  344. xlib.XListInputDevices = dlsym(x11_ctx.libs.Xi, "XListInputDevices");
  345. xlib.XFreeDeviceList = dlsym(x11_ctx.libs.Xi, "XFreeDeviceList");
  346. xlib.XOpenDevice = dlsym(x11_ctx.libs.Xi, "XOpenDevice");
  347. xlib.XCloseDevice = dlsym(x11_ctx.libs.Xi, "XCloseDevice");
  348. xlib.XSelectExtensionEvent = dlsym(x11_ctx.libs.Xi, "XSelectExtensionEvent");
  349. xlib.XcursorLibraryLoadCursor = dlsym(x11_ctx.libs.Xcursor, "XcursorLibraryLoadCursor");
  350. xlib.XRRGetScreenResourcesCurrent = dlsym(x11_ctx.libs.Xrandr, "XRRGetScreenResourcesCurrent");
  351. xlib.XRRGetOutputPrimary = dlsym(x11_ctx.libs.Xrandr, "XRRGetOutputPrimary");
  352. xlib.XRRGetOutputInfo = dlsym(x11_ctx.libs.Xrandr, "XRRGetOutputInfo");
  353. xlib.XRRFreeOutputInfo = dlsym(x11_ctx.libs.Xrandr, "XRRFreeOutputInfo");
  354. xlib.XRRGetCrtcInfo = dlsym(x11_ctx.libs.Xrandr, "XRRGetCrtcInfo");
  355. xlib.XRRFreeCrtcInfo = dlsym(x11_ctx.libs.Xrandr, "XRRFreeCrtcInfo");
  356. xlib.XRRFreeScreenResources = dlsym(x11_ctx.libs.Xrandr, "XRRFreeScreenResources");
  357. xlib.XInitThreads(); // Fixes random ubuntu22 crash
  358. x11_ctx.display = xlib.XOpenDisplay(NULL);
  359. if (!x11_ctx.display) {
  360. return false;
  361. }
  362. xlib.XSetErrorHandler(iron_x11_error_handler);
  363. // this should be kept in sync with the x11_atoms struct
  364. static char *atom_names[] = {
  365. "XdndAware",
  366. "XdndDrop",
  367. "XdndEnter",
  368. "text/uri-list",
  369. "XdndStatus",
  370. "XdndActionCopy",
  371. "XdndSelection",
  372. "CLIPBOARD",
  373. "UTF8_STRING",
  374. "XSEL_DATA",
  375. "TARGETS",
  376. "MULTIPLE",
  377. "text/plain;charset=utf-8",
  378. "WM_DELETE_WINDOW",
  379. "_MOTIF_WM_HINTS",
  380. "_NET_WM_NAME",
  381. "_NET_WM_ICON_NAME",
  382. "_NET_WM_STATE",
  383. "_NET_WM_STATE_FULLSCREEN",
  384. XI_MOUSE,
  385. XI_TABLET,
  386. XI_KEYBOARD,
  387. XI_TOUCHSCREEN,
  388. XI_TOUCHPAD,
  389. XI_BUTTONBOX,
  390. XI_BARCODE,
  391. XI_TRACKBALL,
  392. XI_QUADRATURE,
  393. XI_ID_MODULE,
  394. XI_ONE_KNOB,
  395. XI_NINE_KNOB,
  396. XI_KNOB_BOX,
  397. XI_SPACEBALL,
  398. XI_DATAGLOVE,
  399. XI_EYETRACKER,
  400. XI_CURSORKEYS,
  401. XI_FOOTMOUSE,
  402. XI_JOYSTICK,
  403. };
  404. assert((sizeof atom_names / sizeof atom_names[0]) == (sizeof(struct iron_x11_atoms) / sizeof(Atom)));
  405. xlib.XInternAtoms(x11_ctx.display, atom_names, sizeof atom_names / sizeof atom_names[0], False, (Atom *)&x11_ctx.atoms);
  406. clipboardString = (char *)malloc(clipboardStringSize);
  407. x11_ctx.pen.id = -1;
  408. x11_ctx.eraser.id = -1;
  409. int count;
  410. XDeviceInfoPtr devices = (XDeviceInfoPtr)xlib.XListInputDevices(x11_ctx.display, &count);
  411. for (int i = 0; i < count; i++) {
  412. strncpy(buffer, devices[i].name, 1023);
  413. buffer[1023] = 0;
  414. for (int j = 0; buffer[j]; j++) {
  415. buffer[j] = tolower(buffer[j]);
  416. }
  417. if (strstr(buffer, "stylus") || strstr(buffer, "pen") || strstr(buffer, "wacom")) {
  418. init_pen_device(&devices[i], &x11_ctx.pen, false);
  419. }
  420. if (strstr(buffer, "eraser")) {
  421. init_pen_device(&devices[i], &x11_ctx.eraser, true);
  422. }
  423. }
  424. if (devices != NULL) {
  425. xlib.XFreeDeviceList(devices);
  426. }
  427. return true;
  428. }
  429. static void init_pen_device(XDeviceInfo *info, struct x11_pen_device *pen, bool eraser) {
  430. XDevice *device = xlib.XOpenDevice(x11_ctx.display, info->id);
  431. XAnyClassPtr c = info->inputclassinfo;
  432. for (int j = 0; j < device->num_classes; j++) {
  433. if (c->class == ValuatorClass) {
  434. XValuatorInfo *valuator_info = (XValuatorInfo *)c;
  435. if (valuator_info->num_axes > 2) {
  436. pen->maxPressure = valuator_info->axes[2].max_value;
  437. }
  438. pen->id = info->id;
  439. DeviceMotionNotify(device, pen->motionEvent, pen->motionClass);
  440. if (eraser) {
  441. pen->press = iron_internal_eraser_trigger_press;
  442. pen->move = iron_internal_eraser_trigger_move;
  443. pen->release = iron_internal_eraser_trigger_release;
  444. }
  445. else {
  446. pen->press = iron_internal_pen_trigger_press;
  447. pen->move = iron_internal_pen_trigger_move;
  448. pen->release = iron_internal_pen_trigger_release;
  449. }
  450. return;
  451. }
  452. c = (XAnyClassPtr)((uint8_t *)c + c->length);
  453. }
  454. xlib.XCloseDevice(x11_ctx.display, device);
  455. }
  456. static void check_pen_device(struct iron_x11_window *window, XEvent *event, struct x11_pen_device *pen) {
  457. if (event->type == pen->motionEvent) {
  458. XDeviceMotionEvent *motion = (XDeviceMotionEvent *)(event);
  459. if (motion->deviceid == pen->id) {
  460. float p = (float)motion->axis_data[2] / (float)pen->maxPressure;
  461. if (p > 0 && x11_ctx.pen.current_pressure == 0) {
  462. pen->press(motion->x, motion->y, p);
  463. }
  464. else if (p == 0 && pen->current_pressure > 0) {
  465. pen->release(motion->x, motion->y, p);
  466. }
  467. else if (p > 0) {
  468. pen->move(motion->x, motion->y, p);
  469. }
  470. pen->current_pressure = p;
  471. }
  472. }
  473. }
  474. struct iron_x11_window *window_from_window(Window window) {
  475. for (int i = 0; i < 1; i++) {
  476. if (x11_ctx.windows[i].window == window) {
  477. return &x11_ctx.windows[i];
  478. }
  479. }
  480. return NULL;
  481. }
  482. void iron_vulkan_get_instance_extensions(const char **names, int *index) {
  483. names[(*index)++] = VK_KHR_XLIB_SURFACE_EXTENSION_NAME;
  484. }
  485. VkBool32 iron_vulkan_get_physical_device_presentation_support(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex) {
  486. return vkGetPhysicalDeviceXlibPresentationSupportKHR(physicalDevice, queueFamilyIndex, x11_ctx.display,
  487. DefaultVisual(x11_ctx.display, DefaultScreen(x11_ctx.display))->visualid);
  488. }
  489. VkResult iron_vulkan_create_surface(VkInstance instance, VkSurfaceKHR *surface) {
  490. VkXlibSurfaceCreateInfoKHR info = {0};
  491. info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
  492. info.pNext = NULL;
  493. info.flags = 0;
  494. info.dpy = x11_ctx.display;
  495. info.window = x11_ctx.windows[0].window;
  496. return vkCreateXlibSurfaceKHR(instance, &info, NULL, surface);
  497. }
  498. static int xk_to_iron(KeySym symbol) {
  499. if (symbol == XK_Right) return IRON_KEY_RIGHT;
  500. if (symbol == XK_Left) return IRON_KEY_LEFT;
  501. if (symbol == XK_Up) return IRON_KEY_UP;
  502. if (symbol == XK_Down) return IRON_KEY_DOWN;
  503. if (symbol == XK_space) return IRON_KEY_SPACE;
  504. if (symbol == XK_BackSpace) return IRON_KEY_BACKSPACE;
  505. if (symbol == XK_Tab) return IRON_KEY_TAB;
  506. if (symbol == XK_Return) return IRON_KEY_RETURN;
  507. if (symbol == XK_Shift_L) return IRON_KEY_SHIFT;
  508. if (symbol == XK_Shift_R) return IRON_KEY_SHIFT;
  509. if (symbol == XK_Control_L) return IRON_KEY_CONTROL;
  510. if (symbol == XK_Control_R) return IRON_KEY_CONTROL;
  511. if (symbol == XK_Alt_L) return IRON_KEY_ALT;
  512. if (symbol == XK_Alt_R) return IRON_KEY_ALT;
  513. if (symbol == XK_Delete) return IRON_KEY_DELETE;
  514. if (symbol == XK_comma) return IRON_KEY_COMMA;
  515. if (symbol == XK_period) return IRON_KEY_PERIOD;
  516. if (symbol == XK_bracketleft) return IRON_KEY_OPEN_BRACKET;
  517. if (symbol == XK_bracketright) return IRON_KEY_CLOSE_BRACKET;
  518. if (symbol == XK_braceleft) return IRON_KEY_OPEN_CURLY_BRACKET;
  519. if (symbol == XK_braceright) return IRON_KEY_CLOSE_CURLY_BRACKET;
  520. if (symbol == XK_parenleft) return IRON_KEY_OPEN_PAREN;
  521. if (symbol == XK_parenright) return IRON_KEY_CLOSE_PAREN;
  522. if (symbol == XK_backslash) return IRON_KEY_BACK_SLASH;
  523. if (symbol == XK_apostrophe) return IRON_KEY_QUOTE;
  524. if (symbol == XK_colon) return IRON_KEY_COLON;
  525. if (symbol == XK_semicolon) return IRON_KEY_SEMICOLON;
  526. if (symbol == XK_minus) return IRON_KEY_HYPHEN_MINUS;
  527. if (symbol == XK_underscore) return IRON_KEY_UNDERSCORE;
  528. if (symbol == XK_slash) return IRON_KEY_SLASH;
  529. if (symbol == XK_bar) return IRON_KEY_PIPE;
  530. if (symbol == XK_question) return IRON_KEY_QUESTIONMARK;
  531. if (symbol == XK_less) return IRON_KEY_LESS_THAN;
  532. if (symbol == XK_greater) return IRON_KEY_GREATER_THAN;
  533. if (symbol == XK_asterisk) return IRON_KEY_ASTERISK;
  534. if (symbol == XK_ampersand) return IRON_KEY_AMPERSAND;
  535. if (symbol == XK_asciicircum) return IRON_KEY_CIRCUMFLEX;
  536. if (symbol == XK_percent) return IRON_KEY_PERCENT;
  537. if (symbol == XK_dollar) return IRON_KEY_DOLLAR;
  538. if (symbol == XK_numbersign) return IRON_KEY_HASH;
  539. if (symbol == XK_at) return IRON_KEY_AT;
  540. if (symbol == XK_exclam) return IRON_KEY_EXCLAMATION;
  541. if (symbol == XK_equal) return IRON_KEY_EQUALS;
  542. if (symbol == XK_plus) return IRON_KEY_ADD;
  543. if (symbol == XK_quoteleft) return IRON_KEY_BACK_QUOTE;
  544. if (symbol == XK_quotedbl) return IRON_KEY_DOUBLE_QUOTE;
  545. if (symbol == XK_asciitilde) return IRON_KEY_TILDE;
  546. if (symbol == XK_Pause) return IRON_KEY_PAUSE;
  547. if (symbol == XK_Scroll_Lock) return IRON_KEY_SCROLL_LOCK;
  548. if (symbol == XK_Home) return IRON_KEY_HOME;
  549. if (symbol == XK_Page_Up) return IRON_KEY_PAGE_UP;
  550. if (symbol == XK_Page_Down) return IRON_KEY_PAGE_DOWN;
  551. if (symbol == XK_End) return IRON_KEY_END;
  552. if (symbol == XK_Insert) return IRON_KEY_INSERT;
  553. if (symbol == XK_KP_Enter) return IRON_KEY_RETURN;
  554. if (symbol == XK_KP_Multiply) return IRON_KEY_MULTIPLY;
  555. if (symbol == XK_KP_Add) return IRON_KEY_ADD;
  556. if (symbol == XK_KP_Subtract) return IRON_KEY_SUBTRACT;
  557. if (symbol == XK_KP_Decimal) return IRON_KEY_DECIMAL;
  558. if (symbol == XK_KP_Divide) return IRON_KEY_DIVIDE;
  559. if (symbol == XK_KP_0) return IRON_KEY_NUMPAD_0;
  560. if (symbol == XK_KP_1) return IRON_KEY_NUMPAD_1;
  561. if (symbol == XK_KP_2) return IRON_KEY_NUMPAD_2;
  562. if (symbol == XK_KP_3) return IRON_KEY_NUMPAD_3;
  563. if (symbol == XK_KP_4) return IRON_KEY_NUMPAD_4;
  564. if (symbol == XK_KP_5) return IRON_KEY_NUMPAD_5;
  565. if (symbol == XK_KP_6) return IRON_KEY_NUMPAD_6;
  566. if (symbol == XK_KP_7) return IRON_KEY_NUMPAD_7;
  567. if (symbol == XK_KP_8) return IRON_KEY_NUMPAD_8;
  568. if (symbol == XK_KP_9) return IRON_KEY_NUMPAD_9;
  569. if (symbol == XK_KP_Insert) return IRON_KEY_INSERT;
  570. if (symbol == XK_KP_Delete) return IRON_KEY_DELETE;
  571. if (symbol == XK_KP_End) return IRON_KEY_END;
  572. if (symbol == XK_KP_Home) return IRON_KEY_HOME;
  573. if (symbol == XK_KP_Left) return IRON_KEY_LEFT;
  574. if (symbol == XK_KP_Up) return IRON_KEY_UP;
  575. if (symbol == XK_KP_Right) return IRON_KEY_RIGHT;
  576. if (symbol == XK_KP_Down) return IRON_KEY_DOWN;
  577. if (symbol == XK_KP_Page_Up) return IRON_KEY_PAGE_UP;
  578. if (symbol == XK_KP_Page_Down) return IRON_KEY_PAGE_DOWN;
  579. if (symbol == XK_Menu) return IRON_KEY_CONTEXT_MENU;
  580. if (symbol == XK_a) return IRON_KEY_A;
  581. if (symbol == XK_b) return IRON_KEY_B;
  582. if (symbol == XK_c) return IRON_KEY_C;
  583. if (symbol == XK_d) return IRON_KEY_D;
  584. if (symbol == XK_e) return IRON_KEY_E;
  585. if (symbol == XK_f) return IRON_KEY_F;
  586. if (symbol == XK_g) return IRON_KEY_G;
  587. if (symbol == XK_h) return IRON_KEY_H;
  588. if (symbol == XK_i) return IRON_KEY_I;
  589. if (symbol == XK_j) return IRON_KEY_J;
  590. if (symbol == XK_k) return IRON_KEY_K;
  591. if (symbol == XK_l) return IRON_KEY_L;
  592. if (symbol == XK_m) return IRON_KEY_M;
  593. if (symbol == XK_n) return IRON_KEY_N;
  594. if (symbol == XK_o) return IRON_KEY_O;
  595. if (symbol == XK_p) return IRON_KEY_P;
  596. if (symbol == XK_q) return IRON_KEY_Q;
  597. if (symbol == XK_r) return IRON_KEY_R;
  598. if (symbol == XK_s) return IRON_KEY_S;
  599. if (symbol == XK_t) return IRON_KEY_T;
  600. if (symbol == XK_u) return IRON_KEY_U;
  601. if (symbol == XK_v) return IRON_KEY_V;
  602. if (symbol == XK_w) return IRON_KEY_W;
  603. if (symbol == XK_x) return IRON_KEY_X;
  604. if (symbol == XK_y) return IRON_KEY_Y;
  605. if (symbol == XK_z) return IRON_KEY_Z;
  606. if (symbol == XK_1) return IRON_KEY_1;
  607. if (symbol == XK_2) return IRON_KEY_2;
  608. if (symbol == XK_3) return IRON_KEY_3;
  609. if (symbol == XK_4) return IRON_KEY_4;
  610. if (symbol == XK_5) return IRON_KEY_5;
  611. if (symbol == XK_6) return IRON_KEY_6;
  612. if (symbol == XK_7) return IRON_KEY_7;
  613. if (symbol == XK_8) return IRON_KEY_8;
  614. if (symbol == XK_9) return IRON_KEY_9;
  615. if (symbol == XK_0) return IRON_KEY_0;
  616. if (symbol == XK_Escape) return IRON_KEY_ESCAPE;
  617. if (symbol == XK_F1) return IRON_KEY_F1;
  618. if (symbol == XK_F2) return IRON_KEY_F2;
  619. if (symbol == XK_F3) return IRON_KEY_F3;
  620. if (symbol == XK_F4) return IRON_KEY_F4;
  621. if (symbol == XK_F5) return IRON_KEY_F5;
  622. if (symbol == XK_F6) return IRON_KEY_F6;
  623. if (symbol == XK_F7) return IRON_KEY_F7;
  624. if (symbol == XK_F8) return IRON_KEY_F8;
  625. if (symbol == XK_F9) return IRON_KEY_F9;
  626. if (symbol == XK_F10) return IRON_KEY_F10;
  627. if (symbol == XK_F11) return IRON_KEY_F11;
  628. if (symbol == XK_F12) return IRON_KEY_F12;
  629. return IRON_KEY_UNKNOWN;
  630. }
  631. static bool _handle_messages() {
  632. static bool controlDown = false;
  633. static int ignoreKeycode = 0;
  634. static bool preventNextKeyDownEvent = false;
  635. while (xlib.XPending(x11_ctx.display)) {
  636. XEvent event;
  637. xlib.XNextEvent(x11_ctx.display, &event);
  638. Window window = event.xclient.window;
  639. struct iron_x11_window *k_window = window_from_window(window);
  640. if (k_window == NULL) {
  641. continue;
  642. }
  643. check_pen_device(k_window, &event, &x11_ctx.pen);
  644. check_pen_device(k_window, &event, &x11_ctx.eraser);
  645. switch (event.type) {
  646. case MappingNotify: {
  647. xlib.XRefreshKeyboardMapping(&event.xmapping);
  648. break;
  649. }
  650. case KeyPress: {
  651. XKeyEvent *key = (XKeyEvent *)&event;
  652. KeySym keysym;
  653. wchar_t wchar;
  654. bool wcConverted = xlib.XwcLookupString(k_window->xInputContext, key, &wchar, 1, &keysym, NULL);
  655. bool isIgnoredKeySym = keysym == XK_Escape || keysym == XK_BackSpace || keysym == XK_Delete;
  656. if (!controlDown && !xlib.XFilterEvent(&event, window) && !isIgnoredKeySym) {
  657. if (wcConverted) {
  658. iron_internal_keyboard_trigger_key_press(wchar);
  659. }
  660. }
  661. if (preventNextKeyDownEvent) {
  662. // this keypress is a repeated keystroke and should not lead to a keydown-event
  663. preventNextKeyDownEvent = false;
  664. continue;
  665. }
  666. KeySym ksKey = xlib.XkbKeycodeToKeysym(x11_ctx.display, event.xkey.keycode, 0, 0);
  667. if (ksKey == XK_Control_L || ksKey == XK_Control_R) {
  668. controlDown = true;
  669. }
  670. else if (controlDown && (ksKey == XK_v || ksKey == XK_V)) {
  671. xlib.XConvertSelection(x11_ctx.display, x11_ctx.atoms.CLIPBOARD, x11_ctx.atoms.UTF8_STRING, x11_ctx.atoms.XSEL_DATA, window, CurrentTime);
  672. }
  673. else if (controlDown && (ksKey == XK_c || ksKey == XK_C)) {
  674. xlib.XSetSelectionOwner(x11_ctx.display, x11_ctx.atoms.CLIPBOARD, window, CurrentTime);
  675. char *text = iron_internal_copy_callback();
  676. if (text != NULL)
  677. iron_copy_to_clipboard(text);
  678. }
  679. else if (controlDown && (ksKey == XK_x || ksKey == XK_X)) {
  680. xlib.XSetSelectionOwner(x11_ctx.display, x11_ctx.atoms.CLIPBOARD, window, CurrentTime);
  681. char *text = iron_internal_cut_callback();
  682. if (text != NULL)
  683. iron_copy_to_clipboard(text);
  684. }
  685. if (event.xkey.keycode == ignoreKeycode) {
  686. break;
  687. }
  688. else {
  689. ignoreKeycode = event.xkey.keycode;
  690. }
  691. if (ksKey < 97 || ksKey > 122) {
  692. ksKey = keysym;
  693. }
  694. int key_code = xk_to_iron(ksKey);
  695. if (key_code != IRON_KEY_UNKNOWN) {
  696. iron_internal_keyboard_trigger_key_down(key_code);
  697. }
  698. break;
  699. }
  700. case KeyRelease: {
  701. XKeyEvent *key = (XKeyEvent *)&event;
  702. // peek next-event to determine if this a repeated-keystroke
  703. XEvent nev;
  704. if (xlib.XPending(x11_ctx.display)) {
  705. xlib.XPeekEvent(x11_ctx.display, &nev);
  706. if (nev.type == KeyPress && nev.xkey.time == event.xkey.time && nev.xkey.keycode == event.xkey.keycode) {
  707. // repeated keystroke! prevent this keyup-event and next keydown-event from being fired
  708. preventNextKeyDownEvent = true;
  709. continue;
  710. }
  711. }
  712. KeySym keysym;
  713. char c;
  714. xlib.XLookupString(key, &c, 1, &keysym, NULL);
  715. KeySym ksKey = xlib.XkbKeycodeToKeysym(x11_ctx.display, event.xkey.keycode, 0, 0);
  716. if (ksKey == XK_Control_L || ksKey == XK_Control_R) {
  717. controlDown = false;
  718. }
  719. if (event.xkey.keycode == ignoreKeycode) {
  720. ignoreKeycode = 0;
  721. }
  722. if (ksKey < 97 || ksKey > 122) {
  723. ksKey = keysym;
  724. }
  725. int key_code = xk_to_iron(ksKey);
  726. if (key_code != IRON_KEY_UNKNOWN) {
  727. iron_internal_keyboard_trigger_key_up(key_code);
  728. }
  729. break;
  730. }
  731. case ButtonPress: {
  732. XButtonEvent *button = (XButtonEvent *)&event;
  733. switch (button->button) {
  734. case Button1:
  735. iron_internal_mouse_trigger_press(0, button->x, button->y);
  736. break;
  737. case Button2:
  738. iron_internal_mouse_trigger_press(2, button->x, button->y);
  739. break;
  740. case Button3:
  741. iron_internal_mouse_trigger_press(1, button->x, button->y);
  742. break;
  743. // buttons 4-7 are for mouse wheel events because why not
  744. case Button4:
  745. case Button5:
  746. case Button6:
  747. case Button7:
  748. break;
  749. default:
  750. iron_internal_mouse_trigger_press(button->button - Button1 - 4, button->x, button->y);
  751. break;
  752. }
  753. break;
  754. }
  755. case ButtonRelease: {
  756. XButtonEvent *button = (XButtonEvent *)&event;
  757. switch (button->button) {
  758. case Button1:
  759. iron_internal_mouse_trigger_release(0, button->x, button->y);
  760. break;
  761. case Button2:
  762. iron_internal_mouse_trigger_release(2, button->x, button->y);
  763. break;
  764. case Button3:
  765. iron_internal_mouse_trigger_release(1, button->x, button->y);
  766. break;
  767. // Button4 and Button5 provide mouse wheel events because why not
  768. case Button4:
  769. iron_internal_mouse_trigger_scroll(-1);
  770. break;
  771. case Button5:
  772. iron_internal_mouse_trigger_scroll(1);
  773. break;
  774. // button 6 and 7 seem to be horizontal scrolling, which is not exposed in Iron's api at the moment
  775. case Button6:
  776. case Button7:
  777. break;
  778. default:
  779. iron_internal_mouse_trigger_release(button->button - Button1 - 4, button->x, button->y);
  780. break;
  781. }
  782. break;
  783. }
  784. case MotionNotify: {
  785. XMotionEvent *motion = (XMotionEvent *)&event;
  786. iron_internal_mouse_trigger_move(motion->x, motion->y);
  787. break;
  788. }
  789. case ConfigureNotify: {
  790. if (event.xconfigure.width != k_window->width || event.xconfigure.height != k_window->height) {
  791. k_window->width = event.xconfigure.width;
  792. k_window->height = event.xconfigure.height;
  793. gpu_resize(event.xconfigure.width, event.xconfigure.height);
  794. iron_internal_call_resize_callback(event.xconfigure.width, event.xconfigure.height);
  795. }
  796. break;
  797. }
  798. case ClientMessage: {
  799. if (event.xclient.message_type == x11_ctx.atoms.XdndEnter) {
  800. Window source_window = event.xclient.data.l[0];
  801. XEvent m;
  802. memset(&m, 0, sizeof(m));
  803. m.type = ClientMessage;
  804. m.xclient.window = event.xclient.data.l[0];
  805. m.xclient.message_type = x11_ctx.atoms.XdndStatus;
  806. m.xclient.format = 32;
  807. m.xclient.data.l[0] = window;
  808. m.xclient.data.l[2] = 0;
  809. m.xclient.data.l[3] = 0;
  810. m.xclient.data.l[1] = 1;
  811. m.xclient.data.l[4] = x11_ctx.atoms.XdndActionCopy;
  812. xlib.XSendEvent(x11_ctx.display, source_window, false, NoEventMask, (XEvent *)&m);
  813. xlib.XFlush(x11_ctx.display);
  814. }
  815. else if (event.xclient.message_type == x11_ctx.atoms.XdndDrop) {
  816. xlib.XConvertSelection(x11_ctx.display, x11_ctx.atoms.XdndSelection, x11_ctx.atoms.XdndTextUriList, x11_ctx.atoms.XdndSelection, window,
  817. event.xclient.data.l[2]);
  818. }
  819. else if (event.xclient.data.l[0] == x11_ctx.atoms.WM_DELETE_WINDOW) {
  820. if (iron_internal_call_close_callback()) {
  821. iron_window_destroy();
  822. iron_stop();
  823. }
  824. }
  825. break;
  826. }
  827. case SelectionNotify: {
  828. if (event.xselection.selection == x11_ctx.atoms.CLIPBOARD) {
  829. char *result;
  830. unsigned long ressize, restail;
  831. int resbits;
  832. xlib.XGetWindowProperty(x11_ctx.display, window, x11_ctx.atoms.XSEL_DATA, 0, LONG_MAX / 4, False, AnyPropertyType, &x11_ctx.atoms.UTF8_STRING,
  833. &resbits, &ressize, &restail, (unsigned char **)&result);
  834. iron_internal_paste_callback(result);
  835. xlib.XFree(result);
  836. }
  837. else if (event.xselection.property == x11_ctx.atoms.XdndSelection) {
  838. Atom type;
  839. int format;
  840. unsigned long numItems;
  841. unsigned long bytesAfter = 1;
  842. unsigned char *data = 0;
  843. xlib.XGetWindowProperty(x11_ctx.display, event.xselection.requestor, event.xselection.property, 0, LONG_MAX, False, event.xselection.target,
  844. &type, &format, &numItems, &bytesAfter, &data);
  845. size_t pos = 0;
  846. size_t len = 0;
  847. while (pos < numItems) {
  848. if (data[pos] == '\r') { // Found a file
  849. wchar_t filePath[len + 1];
  850. mbstowcs(filePath, buffer, len);
  851. filePath[len] = 0;
  852. iron_internal_drop_files_callback(filePath + 7); // Strip file://
  853. pos += 2; // Avoid \n
  854. len = 0;
  855. }
  856. buffer[len++] = data[pos++];
  857. }
  858. xlib.XFree(data);
  859. }
  860. break;
  861. }
  862. case SelectionRequest: {
  863. if (event.xselectionrequest.target == x11_ctx.atoms.TARGETS) {
  864. XEvent send;
  865. send.xselection.type = SelectionNotify;
  866. send.xselection.requestor = event.xselectionrequest.requestor;
  867. send.xselection.selection = event.xselectionrequest.selection;
  868. send.xselection.target = event.xselectionrequest.target;
  869. send.xselection.property = event.xselectionrequest.property;
  870. send.xselection.time = event.xselectionrequest.time;
  871. Atom available[] = {x11_ctx.atoms.TARGETS, x11_ctx.atoms.MULTIPLE, x11_ctx.atoms.TEXT_PLAIN, x11_ctx.atoms.UTF8_STRING};
  872. xlib.XChangeProperty(x11_ctx.display, send.xselection.requestor, send.xselection.property, XA_ATOM, 32, PropModeReplace,
  873. (unsigned char *)&available[0], 4);
  874. xlib.XSendEvent(x11_ctx.display, send.xselection.requestor, True, 0, &send);
  875. }
  876. if (event.xselectionrequest.target == x11_ctx.atoms.TEXT_PLAIN || event.xselectionrequest.target == x11_ctx.atoms.UTF8_STRING) {
  877. XEvent send;
  878. send.xselection.type = SelectionNotify;
  879. send.xselection.requestor = event.xselectionrequest.requestor;
  880. send.xselection.selection = event.xselectionrequest.selection;
  881. send.xselection.target = event.xselectionrequest.target;
  882. send.xselection.property = event.xselectionrequest.property;
  883. send.xselection.time = event.xselectionrequest.time;
  884. xlib.XChangeProperty(x11_ctx.display, send.xselection.requestor, send.xselection.property, send.xselection.target, 8, PropModeReplace,
  885. (const unsigned char *)clipboardString, strlen(clipboardString));
  886. xlib.XSendEvent(x11_ctx.display, send.xselection.requestor, True, 0, &send);
  887. }
  888. break;
  889. }
  890. case Expose: {
  891. break;
  892. }
  893. case FocusIn: {
  894. iron_internal_foreground_callback();
  895. break;
  896. }
  897. case FocusOut: {
  898. controlDown = false;
  899. ignoreKeycode = 0;
  900. iron_internal_background_callback();
  901. break;
  902. }
  903. case LeaveNotify: {
  904. break;
  905. }
  906. case EnterNotify: {
  907. break;
  908. }
  909. }
  910. }
  911. return true;
  912. }
  913. bool iron_internal_handle_messages() {
  914. if (!_handle_messages()) {
  915. return false;
  916. }
  917. #ifdef WITH_GAMEPAD
  918. iron_linux_updateHIDGamepads();
  919. #endif
  920. return true;
  921. }
  922. const char *iron_system_id() {
  923. return "Linux";
  924. }
  925. void iron_set_keep_screen_on(bool on) {}
  926. void iron_keyboard_show() {}
  927. void iron_keyboard_hide() {}
  928. bool iron_keyboard_active() {
  929. return true;
  930. }
  931. void iron_load_url(const char *url) {
  932. if (strncmp(url, "http://", sizeof("http://") - 1) == 0 || strncmp(url, "https://", sizeof("https://") - 1) == 0) {
  933. char openUrlCommand[256];
  934. snprintf(openUrlCommand, 256, "xdg-open %s", url);
  935. int err = system(openUrlCommand);
  936. if (err != 0) {
  937. iron_log("Error opening url %s", url);
  938. }
  939. }
  940. }
  941. const char *iron_language() {
  942. return "en";
  943. }
  944. const char *iron_internal_save_path() {
  945. // first check for an existing directory in $HOME
  946. // if one exists, use it, else create one in $XDG_DATA_HOME
  947. // See: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
  948. if (!saveInitialized) {
  949. const char *homedir;
  950. if ((homedir = getenv("HOME")) == NULL) {
  951. homedir = getpwuid(getuid())->pw_dir;
  952. }
  953. strcpy(save, homedir);
  954. strcat(save, "/.");
  955. strcat(save, iron_application_name());
  956. strcat(save, "/");
  957. struct stat st;
  958. if (stat(save, &st) == 0) {
  959. // use existing folder in $HOME
  960. }
  961. else {
  962. // use XDG folder
  963. const char *data_home;
  964. if ((data_home = getenv("XDG_DATA_HOME")) == NULL) {
  965. // $XDG_DATA_HOME is not defined, fall back to the default, $HOME/.local/share
  966. strcpy(save, homedir);
  967. strcat(save, "/.local/share/");
  968. }
  969. else {
  970. // use $XDG_DATA_HOME
  971. strcpy(save, data_home);
  972. if (data_home[strlen(data_home) - 1] != '/') {
  973. strcat(save, "/");
  974. }
  975. }
  976. strcat(save, iron_application_name());
  977. strcat(save, "/");
  978. int res = mkdir(save, 0700);
  979. if (res != 0 && errno != EEXIST) {
  980. iron_error("Could not create save directory '%s'. Error %d", save, errno);
  981. }
  982. }
  983. saveInitialized = true;
  984. }
  985. return save;
  986. }
  987. const char **iron_video_formats() {
  988. return videoFormats;
  989. }
  990. double iron_frequency(void) {
  991. return 1000000.0;
  992. }
  993. uint64_t iron_timestamp(void) {
  994. struct timeval now;
  995. gettimeofday(&now, NULL);
  996. now.tv_sec -= start.tv_sec;
  997. now.tv_usec -= start.tv_usec;
  998. return (uint64_t)now.tv_sec * 1000000 + (uint64_t)now.tv_usec;
  999. }
  1000. void iron_init(iron_window_options_t *win) {
  1001. gettimeofday(&start, NULL);
  1002. #ifdef WITH_GAMEPAD
  1003. iron_linux_initHIDGamepads();
  1004. #endif
  1005. iron_x11_init();
  1006. iron_display_init();
  1007. iron_set_app_name(win->title);
  1008. iron_window_create(win);
  1009. }
  1010. void iron_internal_shutdown() {
  1011. gpu_destroy();
  1012. #ifdef WITH_GAMEPAD
  1013. iron_linux_closeHIDGamepads();
  1014. #endif
  1015. free(clipboardString);
  1016. xlib.XCloseDisplay(x11_ctx.display);
  1017. iron_internal_shutdown_callback();
  1018. }
  1019. #ifndef IRON_NO_MAIN
  1020. int main(int argc, char **argv) {
  1021. return kickstart(argc, argv);
  1022. }
  1023. #endif
  1024. void iron_copy_to_clipboard(const char *text) {
  1025. size_t textLength = strlen(text);
  1026. if (textLength >= clipboardStringSize) {
  1027. free(clipboardString);
  1028. clipboardStringSize = textLength + 1;
  1029. clipboardString = (char *)malloc(clipboardStringSize);
  1030. }
  1031. strcpy(clipboardString, text);
  1032. }
  1033. static int parse_number_at_end_of_line(char *line) {
  1034. char *end = &line[strlen(line) - 2];
  1035. int num = 0;
  1036. int multi = 1;
  1037. while (*end >= '0' && *end <= '9') {
  1038. num += (*end - '0') * multi;
  1039. multi *= 10;
  1040. --end;
  1041. }
  1042. return num;
  1043. }
  1044. int iron_hardware_threads(void) {
  1045. return sysconf(_SC_NPROCESSORS_ONLN);
  1046. }
  1047. void iron_internal_mouse_lock() {
  1048. iron_mouse_hide();
  1049. int width = iron_window_width();
  1050. int height = iron_window_height();
  1051. int x, y;
  1052. iron_mouse_get_position(&x, &y);
  1053. int newX = x;
  1054. int newY = y;
  1055. if (x < 0) {
  1056. newX -= x;
  1057. }
  1058. else if (x > width) {
  1059. newX -= x - width;
  1060. }
  1061. if (y < 0) {
  1062. newY -= y;
  1063. }
  1064. else if (y > height) {
  1065. newY -= y - height;
  1066. }
  1067. iron_mouse_set_position(newX, newY);
  1068. }
  1069. void iron_internal_mouse_unlock() {
  1070. iron_mouse_show();
  1071. }
  1072. bool iron_mouse_can_lock(void) {
  1073. return true;
  1074. }
  1075. void iron_mouse_show() {
  1076. struct iron_x11_window *window = &x11_ctx.windows[0];
  1077. if (mouse_hidden) {
  1078. xlib.XUndefineCursor(x11_ctx.display, window->window);
  1079. mouse_hidden = false;
  1080. }
  1081. }
  1082. void iron_mouse_hide() {
  1083. struct iron_x11_window *window = &x11_ctx.windows[0];
  1084. if (!mouse_hidden) {
  1085. XColor col;
  1086. col.pixel = 0;
  1087. col.red = 0;
  1088. col.green = 0;
  1089. col.blue = 0;
  1090. col.flags = DoRed | DoGreen | DoBlue;
  1091. col.pad = 0;
  1092. char data[1] = {'\0'};
  1093. Pixmap blank = xlib.XCreateBitmapFromData(x11_ctx.display, window->window, data, 1, 1);
  1094. Cursor cursor = xlib.XCreatePixmapCursor(x11_ctx.display, blank, blank, &col, &col, 0, 0);
  1095. xlib.XDefineCursor(x11_ctx.display, window->window, cursor);
  1096. xlib.XFreePixmap(x11_ctx.display, blank);
  1097. mouse_hidden = true;
  1098. }
  1099. }
  1100. void iron_mouse_set_cursor(iron_cursor_t cursor_index) {
  1101. if (mouse_hidden) {
  1102. return;
  1103. }
  1104. Cursor cursor;
  1105. if (cursor_index == IRON_CURSOR_HAND) {
  1106. cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "hand1");
  1107. }
  1108. else if (cursor_index == IRON_CURSOR_IBEAM) {
  1109. cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "xterm");
  1110. }
  1111. else if (cursor_index == IRON_CURSOR_SIZEWE) {
  1112. cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "sb_h_double_arrow");
  1113. }
  1114. else if (cursor_index == IRON_CURSOR_SIZENS) {
  1115. cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "sb_v_double_arrow");
  1116. }
  1117. else {
  1118. cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "left_ptr"); // "arrow"
  1119. }
  1120. struct iron_x11_window *window = &x11_ctx.windows[0];
  1121. xlib.XDefineCursor(x11_ctx.display, window->window, cursor);
  1122. }
  1123. void iron_mouse_set_position(int x, int y) {
  1124. struct iron_x11_window *window = &x11_ctx.windows[0];
  1125. xlib.XWarpPointer(x11_ctx.display, None, window->window, 0, 0, 0, 0, x, y);
  1126. xlib.XFlush(x11_ctx.display);
  1127. }
  1128. void iron_mouse_get_position(int *x, int *y) {
  1129. struct iron_x11_window *window = &x11_ctx.windows[0];
  1130. Window inwin;
  1131. Window inchildwin;
  1132. int rootx, rooty;
  1133. unsigned int mask;
  1134. xlib.XQueryPointer(x11_ctx.display, window->window, &inwin, &inchildwin, &rootx, &rooty, x, y, &mask);
  1135. }
  1136. #ifdef WITH_GAMEPAD
  1137. #include <fcntl.h>
  1138. #include <libudev.h>
  1139. #include <linux/joystick.h>
  1140. struct HIDGamepad {
  1141. int idx;
  1142. char gamepad_dev_name[256];
  1143. char name[385];
  1144. int file_descriptor;
  1145. bool connected;
  1146. struct js_event gamepadEvent;
  1147. };
  1148. static void HIDGamepad_open(struct HIDGamepad *pad) {
  1149. pad->file_descriptor = open(pad->gamepad_dev_name, O_RDONLY | O_NONBLOCK);
  1150. if (pad->file_descriptor < 0) {
  1151. pad->connected = false;
  1152. }
  1153. else {
  1154. pad->connected = true;
  1155. char buf[128];
  1156. if (ioctl(pad->file_descriptor, JSIOCGNAME(sizeof(buf)), buf) < 0) {
  1157. strncpy(buf, "Unknown", sizeof(buf));
  1158. }
  1159. pad->name[0] = 0;
  1160. // snprintf(pad->name, sizeof(pad->name), "%s(%s)", buf, pad->gamepad_dev_name); // TODO: valgrind error
  1161. iron_internal_gamepad_trigger_connect(pad->idx);
  1162. }
  1163. }
  1164. static void HIDGamepad_init(struct HIDGamepad *pad, int index) {
  1165. pad->file_descriptor = -1;
  1166. pad->connected = false;
  1167. pad->gamepad_dev_name[0] = 0;
  1168. if (index >= 0 && index < 12) {
  1169. pad->idx = index;
  1170. snprintf(pad->gamepad_dev_name, sizeof(pad->gamepad_dev_name), "/dev/input/js%d", pad->idx);
  1171. HIDGamepad_open(pad);
  1172. }
  1173. }
  1174. static void HIDGamepad_close(struct HIDGamepad *pad) {
  1175. if (pad->connected) {
  1176. iron_internal_gamepad_trigger_disconnect(pad->idx);
  1177. close(pad->file_descriptor);
  1178. pad->file_descriptor = -1;
  1179. pad->connected = false;
  1180. }
  1181. }
  1182. void HIDGamepad_processEvent(struct HIDGamepad *pad, struct js_event e) {
  1183. switch (e.type) {
  1184. case JS_EVENT_BUTTON:
  1185. iron_internal_gamepad_trigger_button(pad->idx, e.number, e.value);
  1186. break;
  1187. case JS_EVENT_AXIS: {
  1188. float value = e.number % 2 == 0 ? e.value : -e.value;
  1189. iron_internal_gamepad_trigger_axis(pad->idx, e.number, value / 32767.0f);
  1190. break;
  1191. }
  1192. default:
  1193. break;
  1194. }
  1195. }
  1196. void HIDGamepad_update(struct HIDGamepad *pad) {
  1197. if (pad->connected) {
  1198. while (read(pad->file_descriptor, &pad->gamepadEvent, sizeof(pad->gamepadEvent)) > 0) {
  1199. HIDGamepad_processEvent(pad, pad->gamepadEvent);
  1200. }
  1201. }
  1202. }
  1203. struct HIDGamepadUdevHelper {
  1204. struct udev *udevPtr;
  1205. struct udev_monitor *udevMonitorPtr;
  1206. int udevMonitorFD;
  1207. };
  1208. static struct HIDGamepadUdevHelper udev_helper;
  1209. static struct HIDGamepad gamepads[IRON_GAMEPAD_MAX_COUNT];
  1210. static void HIDGamepadUdevHelper_openOrCloseGamepad(struct HIDGamepadUdevHelper *helper, struct udev_device *dev) {
  1211. const char *action = udev_device_get_action(dev);
  1212. if (!action)
  1213. action = "add";
  1214. const char *joystickDevnodeName = strstr(udev_device_get_devnode(dev), "js");
  1215. if (joystickDevnodeName) {
  1216. int joystickDevnodeIndex;
  1217. sscanf(joystickDevnodeName, "js%d", &joystickDevnodeIndex);
  1218. if (!strcmp(action, "add")) {
  1219. HIDGamepad_open(&gamepads[joystickDevnodeIndex]);
  1220. }
  1221. if (!strcmp(action, "remove")) {
  1222. HIDGamepad_close(&gamepads[joystickDevnodeIndex]);
  1223. }
  1224. }
  1225. }
  1226. static void HIDGamepadUdevHelper_processDevice(struct HIDGamepadUdevHelper *helper, struct udev_device *dev) {
  1227. if (dev) {
  1228. if (udev_device_get_devnode(dev))
  1229. HIDGamepadUdevHelper_openOrCloseGamepad(helper, dev);
  1230. udev_device_unref(dev);
  1231. }
  1232. }
  1233. static void HIDGamepadUdevHelper_init(struct HIDGamepadUdevHelper *helper) {
  1234. struct udev *udevPtrNew = udev_new();
  1235. // enumerate
  1236. struct udev_enumerate *enumerate = udev_enumerate_new(udevPtrNew);
  1237. udev_enumerate_add_match_subsystem(enumerate, "input");
  1238. udev_enumerate_scan_devices(enumerate);
  1239. struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
  1240. struct udev_list_entry *entry;
  1241. udev_list_entry_foreach(entry, devices) {
  1242. const char *path = udev_list_entry_get_name(entry);
  1243. struct udev_device *dev = udev_device_new_from_syspath(udevPtrNew, path);
  1244. HIDGamepadUdevHelper_processDevice(helper, dev);
  1245. }
  1246. udev_enumerate_unref(enumerate);
  1247. // setup mon
  1248. helper->udevMonitorPtr = udev_monitor_new_from_netlink(udevPtrNew, "udev");
  1249. udev_monitor_filter_add_match_subsystem_devtype(helper->udevMonitorPtr, "input", NULL);
  1250. udev_monitor_enable_receiving(helper->udevMonitorPtr);
  1251. helper->udevMonitorFD = udev_monitor_get_fd(helper->udevMonitorPtr);
  1252. helper->udevPtr = udevPtrNew;
  1253. }
  1254. static void HIDGamepadUdevHelper_update(struct HIDGamepadUdevHelper *helper) {
  1255. fd_set fds;
  1256. FD_ZERO(&fds);
  1257. FD_SET(helper->udevMonitorFD, &fds);
  1258. if (FD_ISSET(helper->udevMonitorFD, &fds)) {
  1259. struct udev_device *dev = udev_monitor_receive_device(helper->udevMonitorPtr);
  1260. HIDGamepadUdevHelper_processDevice(helper, dev);
  1261. }
  1262. }
  1263. static void HIDGamepadUdevHelper_close(struct HIDGamepadUdevHelper *helper) {
  1264. udev_unref(helper->udevPtr);
  1265. }
  1266. void iron_linux_initHIDGamepads() {
  1267. for (int i = 0; i < IRON_GAMEPAD_MAX_COUNT; ++i) {
  1268. HIDGamepad_init(&gamepads[i], i);
  1269. }
  1270. HIDGamepadUdevHelper_init(&udev_helper);
  1271. }
  1272. void iron_linux_updateHIDGamepads() {
  1273. HIDGamepadUdevHelper_update(&udev_helper);
  1274. for (int i = 0; i < IRON_GAMEPAD_MAX_COUNT; ++i) {
  1275. HIDGamepad_update(&gamepads[i]);
  1276. }
  1277. }
  1278. void iron_linux_closeHIDGamepads() {
  1279. HIDGamepadUdevHelper_close(&udev_helper);
  1280. }
  1281. const char *iron_gamepad_vendor(int gamepad) {
  1282. return "Linux gamepad";
  1283. }
  1284. const char *iron_gamepad_product_name(int gamepad) {
  1285. return gamepad >= 0 && gamepad < IRON_GAMEPAD_MAX_COUNT ? gamepads[gamepad].name : "";
  1286. }
  1287. bool iron_gamepad_connected(int gamepad) {
  1288. return gamepad >= 0 && gamepad < IRON_GAMEPAD_MAX_COUNT && gamepads[gamepad].connected;
  1289. }
  1290. void iron_gamepad_rumble(int gamepad, float left, float right) {}
  1291. #endif