linux_system.c 46 KB

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