| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559 |
- #include "linux_system.h"
- #include <X11/XKBlib.h>
- #include <X11/Xcursor/Xcursor.h>
- #include <X11/Xlib.h>
- #include <ctype.h>
- #include <errno.h>
- #include <iron_gpu.h>
- #include <iron_system.h>
- #include <limits.h>
- #include <pwd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/stat.h>
- #include <sys/time.h>
- #include <time.h>
- #include <unistd.h>
- #include <vulkan/vulkan.h>
- #include <vulkan/vulkan_xlib.h>
- #define Button6 6
- #define Button7 7
- static struct x11_context x11_ctx = {0};
- static size_t clipboardStringSize = 1024;
- static char *clipboardString = NULL;
- static char buffer[1024];
- static char save[2000];
- static bool saveInitialized = false;
- static const char *videoFormats[] = {"ogv", NULL};
- static struct timeval start;
- static bool mouse_hidden = false;
- static int xi_extension = -1;
- static Atom XdndAware;
- static Atom XdndDrop;
- static Atom XdndEnter;
- static Atom XdndTextUriList;
- static Atom XdndStatus;
- static Atom XdndActionCopy;
- static Atom XdndSelection;
- static Atom CLIPBOARD;
- static Atom UTF8_STRING;
- static Atom XSEL_DATA;
- static Atom TARGETS;
- static Atom MULTIPLE;
- static Atom TEXT_PLAIN;
- static Atom WM_DELETE_WINDOW;
- static Atom NET_WM_NAME;
- static Atom NET_WM_ICON_NAME;
- static Atom NET_WM_STATE;
- static Atom NET_WM_STATE_FULLSCREEN;
- static struct {
- void (*resize_callback)(int width, int height, void *data);
- void *resize_data;
- void *ppi_data;
- bool (*close_callback)(void *data);
- void *close_data;
- } iron_internal_window_callbacks[1];
- int iron_x11_error_handler(Display *display, XErrorEvent *error_event) {
- XGetErrorText(display, error_event->error_code, buffer, 1024);
- iron_error("X Error: %s", buffer);
- return 0;
- }
- void iron_x11_init() {
- XInitThreads(); // Fixes random ubuntu22 crash
- x11_ctx.display = XOpenDisplay(NULL);
- if (!x11_ctx.display) {
- return;
- }
- XSetErrorHandler(iron_x11_error_handler);
- clipboardString = (char *)malloc(clipboardStringSize);
- XdndAware = XInternAtom(x11_ctx.display, "XdndAware", False);
- XdndDrop = XInternAtom(x11_ctx.display, "XdndDrop", False);
- XdndEnter = XInternAtom(x11_ctx.display, "XdndEnter", False);
- XdndTextUriList = XInternAtom(x11_ctx.display, "text/uri-list", False);
- XdndStatus = XInternAtom(x11_ctx.display, "XdndStatus", False);
- XdndActionCopy = XInternAtom(x11_ctx.display, "XdndActionCopy", False);
- XdndSelection = XInternAtom(x11_ctx.display, "XdndSelection", False);
- CLIPBOARD = XInternAtom(x11_ctx.display, "CLIPBOARD", False);
- UTF8_STRING = XInternAtom(x11_ctx.display, "UTF8_STRING", False);
- XSEL_DATA = XInternAtom(x11_ctx.display, "XSEL_DATA", False);
- TARGETS = XInternAtom(x11_ctx.display, "TARGETS", False);
- MULTIPLE = XInternAtom(x11_ctx.display, "MULTIPLE", False);
- TEXT_PLAIN = XInternAtom(x11_ctx.display, "text/plain;charset=utf-8", False);
- WM_DELETE_WINDOW = XInternAtom(x11_ctx.display, "WM_DELETE_WINDOW", False);
- NET_WM_NAME = XInternAtom(x11_ctx.display, "_NET_WM_NAME", False);
- NET_WM_ICON_NAME = XInternAtom(x11_ctx.display, "_NET_WM_ICON_NAME", False);
- NET_WM_STATE = XInternAtom(x11_ctx.display, "_NET_WM_STATE", False);
- NET_WM_STATE_FULLSCREEN = XInternAtom(x11_ctx.display, "_NET_WM_STATE_FULLSCREEN", False);
- int event, error;
- XQueryExtension(x11_ctx.display, "XInputExtension", &xi_extension, &event, &error);
- int num_devices;
- XIDeviceInfo *devices = XIQueryDevice(x11_ctx.display, XIAllDevices, &num_devices);
- x11_ctx.pen.id = -1;
- x11_ctx.eraser.id = -1;
- for (int i = 0; i < num_devices; i++) {
- XIDeviceInfo *info = &devices[i];
- strncpy(buffer, info->name, 1023);
- buffer[1023] = 0;
- for (int j = 0; buffer[j]; j++) {
- buffer[j] = tolower(buffer[j]);
- }
- bool is_pen = strstr(buffer, "stylus") || strstr(buffer, "pen") || strstr(buffer, "wacom");
- bool is_eraser = strstr(buffer, "eraser");
- if (!is_pen && !is_eraser) {
- continue;
- }
- int pressure_index = -1;
- double max = 0;
- for (int j = 0; j < info->num_classes; j++) {
- if (info->classes[j]->type == XIValuatorClass) {
- XIValuatorClassInfo *val = (XIValuatorClassInfo *)info->classes[j];
- if (val->label == XInternAtom(x11_ctx.display, "Abs Pressure", False)) {
- pressure_index = val->number;
- max = val->max;
- break;
- }
- }
- }
- if (pressure_index == -1)
- continue;
- struct x11_pen_device *d = is_pen ? &x11_ctx.pen : &x11_ctx.eraser;
- d->id = info->deviceid;
- d->pressure_index = pressure_index;
- d->pressure_max = max;
- d->press = is_pen ? iron_internal_pen_trigger_press : iron_internal_eraser_trigger_press;
- d->move = is_pen ? iron_internal_pen_trigger_move : iron_internal_eraser_trigger_move;
- d->release = is_pen ? iron_internal_pen_trigger_release : iron_internal_eraser_trigger_release;
- }
- XIFreeDeviceInfo(devices);
- }
- void iron_display_init() {
- static bool display_initialized = false;
- if (display_initialized) {
- return;
- }
- iron_x11_init();
- Window root_window = RootWindow(x11_ctx.display, DefaultScreen(x11_ctx.display));
- XRRScreenResources *screen_resources = XRRGetScreenResourcesCurrent(x11_ctx.display, root_window);
- RROutput primary_output = XRRGetOutputPrimary(x11_ctx.display, root_window);
- for (int i = 0; i < screen_resources->noutput; i++) {
- if (i >= MAXIMUM_DISPLAYS) {
- iron_error("Too many screens (maximum %i)", MAXIMUM_DISPLAYS);
- break;
- }
- XRROutputInfo *output_info = XRRGetOutputInfo(x11_ctx.display, screen_resources, screen_resources->outputs[i]);
- if (output_info->connection != RR_Connected || output_info->crtc == None) {
- XRRFreeOutputInfo(output_info);
- continue;
- }
- XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(x11_ctx.display, screen_resources, output_info->crtc);
- struct iron_x11_display *display = &x11_ctx.displays[x11_ctx.num_displays++];
- display->index = i;
- display->x = crtc_info->x;
- display->y = crtc_info->y;
- display->width = crtc_info->width;
- display->height = crtc_info->height;
- display->primary = screen_resources->outputs[i] == primary_output;
- display->crtc = output_info->crtc;
- display->output = screen_resources->outputs[i];
- XRRFreeOutputInfo(output_info);
- XRRFreeCrtcInfo(crtc_info);
- }
- XRRFreeScreenResources(screen_resources);
- display_initialized = true;
- }
- iron_display_mode_t iron_display_current_mode(int display_index) {
- struct iron_x11_display *display = &x11_ctx.displays[display_index];
- iron_display_mode_t mode;
- mode.x = 0;
- mode.y = 0;
- mode.width = display->width;
- mode.height = display->height;
- mode.frequency = 60;
- mode.bits_per_pixel = 32;
- mode.pixels_per_inch = 96;
- Window root_window = DefaultRootWindow(x11_ctx.display);
- XRRScreenResources *screen_resources = XRRGetScreenResourcesCurrent(x11_ctx.display, root_window);
- XRROutputInfo *output_info = XRRGetOutputInfo(x11_ctx.display, screen_resources, screen_resources->outputs[display->index]);
- if (output_info->connection != RR_Connected || output_info->crtc == None) {
- XRRFreeOutputInfo(output_info);
- XRRFreeScreenResources(screen_resources);
- return mode;
- }
- XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(x11_ctx.display, screen_resources, output_info->crtc);
- for (int j = 0; j < output_info->nmode; j++) {
- RRMode rr_mode = crtc_info->mode;
- XRRModeInfo *mode_info = NULL;
- for (int k = 0; k < screen_resources->nmode; k++) {
- if (screen_resources->modes[k].id == rr_mode) {
- mode_info = &screen_resources->modes[k];
- break;
- }
- }
- if (mode_info == NULL) {
- continue;
- }
- mode.x = display->x;
- mode.y = display->y;
- mode.width = mode_info->width;
- mode.height = mode_info->height;
- mode.pixels_per_inch = 96;
- mode.bits_per_pixel = 32;
- if (mode_info->hTotal && mode_info->vTotal) {
- mode.frequency = (mode_info->dotClock / (mode_info->hTotal * mode_info->vTotal));
- }
- }
- XRRFreeOutputInfo(output_info);
- XRRFreeCrtcInfo(crtc_info);
- XRRFreeScreenResources(screen_resources);
- return mode;
- }
- int iron_primary_display(void) {
- for (int i = 0; i < x11_ctx.num_displays; i++) {
- if (x11_ctx.displays[i].primary) {
- return i;
- }
- }
- return 0;
- }
- int iron_count_displays(void) {
- return x11_ctx.num_displays;
- }
- int iron_window_x() {
- return 0;
- }
- int iron_window_y() {
- return 0;
- }
- int iron_window_width() {
- return x11_ctx.windows[0].width;
- }
- int iron_window_height() {
- return x11_ctx.windows[0].height;
- }
- void iron_window_resize(int width, int height) {
- struct iron_x11_window *window = &x11_ctx.windows[0];
- XResizeWindow(x11_ctx.display, window->window, width, height);
- }
- void iron_window_move(int x, int y) {
- struct iron_x11_window *window = &x11_ctx.windows[0];
- XMoveWindow(x11_ctx.display, window->window, x, y);
- }
- void iron_window_change_mode(iron_window_mode_t mode) {
- struct iron_x11_window *window = &x11_ctx.windows[0];
- if (mode == window->mode) {
- return;
- }
- window->mode = mode;
- XEvent xev;
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.xclient.window = window->window;
- xev.xclient.message_type = NET_WM_STATE;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = mode == IRON_WINDOW_MODE_FULLSCREEN ? 1 : 0;
- xev.xclient.data.l[1] = NET_WM_STATE_FULLSCREEN;
- xev.xclient.data.l[2] = 0;
- XMapWindow(x11_ctx.display, window->window);
- XSendEvent(x11_ctx.display, DefaultRootWindow(x11_ctx.display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
- XFlush(x11_ctx.display);
- }
- int iron_window_display() {
- struct iron_x11_window *window = &x11_ctx.windows[0];
- return window->display_index;
- }
- void iron_window_destroy() {
- XFlush(x11_ctx.display);
- struct iron_x11_window *window = &x11_ctx.windows[0];
- XDestroyIC(window->xInputContext);
- XCloseIM(window->xInputMethod);
- XDestroyWindow(x11_ctx.display, window->window);
- XFlush(x11_ctx.display);
- *window = (struct iron_x11_window){0};
- }
- void iron_window_show() {
- struct iron_x11_window *window = &x11_ctx.windows[0];
- XMapWindow(x11_ctx.display, window->window);
- }
- void iron_window_hide() {
- struct iron_x11_window *window = &x11_ctx.windows[0];
- XUnmapWindow(x11_ctx.display, window->window);
- }
- void iron_window_set_title(const char *title) {
- struct iron_x11_window *window = &x11_ctx.windows[0];
- XChangeProperty(x11_ctx.display, window->window, NET_WM_NAME, UTF8_STRING, 8, PropModeReplace, (unsigned char *)title, strlen(title));
- XChangeProperty(x11_ctx.display, window->window, NET_WM_ICON_NAME, UTF8_STRING, 8, PropModeReplace, (unsigned char *)title, strlen(title));
- XFlush(x11_ctx.display);
- }
- void iron_window_create(iron_window_options_t *win) {
- struct iron_x11_window *window = &x11_ctx.windows[0];
- window->width = win->width;
- window->height = win->height;
- Visual *visual = NULL;
- XSetWindowAttributes set_window_attribs = {0};
- XColor color;
- Colormap colormap = DefaultColormap(x11_ctx.display, DefaultScreen(x11_ctx.display));
- color.red = (32 * 65535) / 255;
- color.green = (32 * 65535) / 255;
- color.blue = (32 * 65535) / 255;
- XAllocColor(x11_ctx.display, colormap, &color);
- set_window_attribs.background_pixel = color.pixel;
- set_window_attribs.border_pixel = 0;
- set_window_attribs.event_mask =
- KeyPressMask | KeyReleaseMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask;
- int screen = DefaultScreen(x11_ctx.display);
- visual = DefaultVisual(x11_ctx.display, screen);
- int depth = DefaultDepth(x11_ctx.display, screen);
- set_window_attribs.colormap = XCreateColormap(x11_ctx.display, RootWindow(x11_ctx.display, screen), visual, AllocNone);
- window->window = XCreateWindow(x11_ctx.display, RootWindow(x11_ctx.display, DefaultScreen(x11_ctx.display)), 0, 0, win->width, win->height, 0, depth,
- InputOutput, visual, CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &set_window_attribs);
- static char nameClass[256];
- static const char *nameClassAddendum = "_IronApplication";
- strncpy(nameClass, iron_application_name(), sizeof(nameClass) - strlen(nameClassAddendum) - 1);
- strcat(nameClass, nameClassAddendum);
- char resNameBuffer[256];
- strncpy(resNameBuffer, iron_application_name(), 256);
- XClassHint classHint = {.res_name = resNameBuffer, .res_class = nameClass};
- XSetClassHint(x11_ctx.display, window->window, &classHint);
- XSetLocaleModifiers("@im=none");
- window->xInputMethod = XOpenIM(x11_ctx.display, NULL, NULL, NULL);
- window->xInputContext = XCreateIC(window->xInputMethod, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, window->window, NULL);
- XSetICFocus(window->xInputContext);
- window->mode = IRON_WINDOW_MODE_WINDOW;
- iron_window_change_mode(win->mode);
- XMapWindow(x11_ctx.display, window->window);
- Atom XdndVersion = 5;
- XChangeProperty(x11_ctx.display, window->window, XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&XdndVersion, 1);
- XSetWMProtocols(x11_ctx.display, window->window, &WM_DELETE_WINDOW, 1);
- iron_window_set_title(win->title);
- if (x11_ctx.pen.id != -1) {
- XIEventMask mask;
- unsigned char mask_data[XIMaskLen(XI_LASTEVENT)];
- memset(mask_data, 0, sizeof(mask_data));
- mask.deviceid = x11_ctx.pen.id;
- mask.mask_len = sizeof(mask_data);
- mask.mask = mask_data;
- XISetMask(mask.mask, XI_Motion);
- XISetMask(mask.mask, XI_ButtonPress);
- XISetMask(mask.mask, XI_ButtonRelease);
- XISelectEvents(x11_ctx.display, window->window, &mask, 1);
- }
- gpu_init(win->depth_bits, win->vsync);
- }
- void iron_window_set_resize_callback(void (*callback)(int width, int height, void *data), void *data) {
- iron_internal_window_callbacks[0].resize_callback = callback;
- iron_internal_window_callbacks[0].resize_data = data;
- }
- void iron_internal_call_resize_callback(int width, int height) {
- if (iron_internal_window_callbacks[0].resize_callback != NULL) {
- iron_internal_window_callbacks[0].resize_callback(width, height, iron_internal_window_callbacks[0].resize_data);
- }
- }
- void iron_window_set_close_callback(bool (*callback)(void *data), void *data) {
- iron_internal_window_callbacks[0].close_callback = callback;
- iron_internal_window_callbacks[0].close_data = data;
- }
- bool iron_internal_call_close_callback() {
- if (iron_internal_window_callbacks[0].close_callback != NULL) {
- return iron_internal_window_callbacks[0].close_callback(iron_internal_window_callbacks[0].close_data);
- }
- return true;
- }
- iron_window_mode_t iron_window_get_mode() {
- return x11_ctx.windows[0].mode;
- }
- static void check_pen_device(struct iron_x11_window *window, XEvent *event, struct x11_pen_device *pen) {
- if (!XGetEventData(x11_ctx.display, &event->xcookie)) {
- return;
- }
- XIDeviceEvent *dev_event = (XIDeviceEvent *)event->xcookie.data;
- if (dev_event->event != window->window || dev_event->deviceid != pen->id) {
- XFreeEventData(x11_ctx.display, &event->xcookie);
- return;
- }
- float p = pen->pressure_current;
- if (XIMaskIsSet(dev_event->valuators.mask, pen->pressure_index)) {
- double *values = dev_event->valuators.values;
- int idx = 0;
- for (int i = 0; i < pen->pressure_index; i++) {
- if (XIMaskIsSet(dev_event->valuators.mask, i))
- idx++;
- }
- p = values[idx] / pen->pressure_max;
- }
- if (event->xcookie.evtype == XI_ButtonPress && dev_event->detail == 1) {
- pen->press(dev_event->event_x, dev_event->event_y, p);
- }
- else if (event->xcookie.evtype == XI_ButtonRelease && dev_event->detail == 1) {
- pen->release(dev_event->event_x, dev_event->event_y, p);
- }
- else if (event->xcookie.evtype == XI_Motion && p > 0) {
- pen->move(dev_event->event_x, dev_event->event_y, p);
- }
- pen->pressure_current = p;
- XFreeEventData(x11_ctx.display, &event->xcookie);
- }
- struct iron_x11_window *window_from_window(Window window) {
- for (int i = 0; i < 1; i++) {
- if (x11_ctx.windows[i].window == window) {
- return &x11_ctx.windows[i];
- }
- }
- return NULL;
- }
- void iron_vulkan_get_instance_extensions(const char **names, int *index) {
- names[(*index)++] = VK_KHR_XLIB_SURFACE_EXTENSION_NAME;
- }
- VkBool32 iron_vulkan_get_physical_device_presentation_support(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex) {
- return vkGetPhysicalDeviceXlibPresentationSupportKHR(physicalDevice, queueFamilyIndex, x11_ctx.display,
- DefaultVisual(x11_ctx.display, DefaultScreen(x11_ctx.display))->visualid);
- }
- VkResult iron_vulkan_create_surface(VkInstance instance, VkSurfaceKHR *surface) {
- VkXlibSurfaceCreateInfoKHR info = {0};
- info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
- info.pNext = NULL;
- info.flags = 0;
- info.dpy = x11_ctx.display;
- info.window = x11_ctx.windows[0].window;
- return vkCreateXlibSurfaceKHR(instance, &info, NULL, surface);
- }
- static int xk_to_iron(KeySym symbol) {
- if (symbol == XK_Right)
- return IRON_KEY_RIGHT;
- if (symbol == XK_Left)
- return IRON_KEY_LEFT;
- if (symbol == XK_Up)
- return IRON_KEY_UP;
- if (symbol == XK_Down)
- return IRON_KEY_DOWN;
- if (symbol == XK_space)
- return IRON_KEY_SPACE;
- if (symbol == XK_BackSpace)
- return IRON_KEY_BACKSPACE;
- if (symbol == XK_Tab)
- return IRON_KEY_TAB;
- if (symbol == XK_Return)
- return IRON_KEY_RETURN;
- if (symbol == XK_Shift_L)
- return IRON_KEY_SHIFT;
- if (symbol == XK_Shift_R)
- return IRON_KEY_SHIFT;
- if (symbol == XK_Control_L)
- return IRON_KEY_CONTROL;
- if (symbol == XK_Control_R)
- return IRON_KEY_CONTROL;
- if (symbol == XK_Alt_L)
- return IRON_KEY_ALT;
- if (symbol == XK_Alt_R)
- return IRON_KEY_ALT;
- if (symbol == XK_ISO_Prev_Group)
- return IRON_KEY_ALT; // XK_ISO_Prev_Group received instead of XK_Alt_L?
- if (symbol == XK_Delete)
- return IRON_KEY_DELETE;
- if (symbol == XK_comma)
- return IRON_KEY_COMMA;
- if (symbol == XK_period)
- return IRON_KEY_PERIOD;
- if (symbol == XK_bracketleft)
- return IRON_KEY_OPEN_BRACKET;
- if (symbol == XK_bracketright)
- return IRON_KEY_CLOSE_BRACKET;
- if (symbol == XK_braceleft)
- return IRON_KEY_OPEN_CURLY_BRACKET;
- if (symbol == XK_braceright)
- return IRON_KEY_CLOSE_CURLY_BRACKET;
- if (symbol == XK_parenleft)
- return IRON_KEY_OPEN_PAREN;
- if (symbol == XK_parenright)
- return IRON_KEY_CLOSE_PAREN;
- if (symbol == XK_backslash)
- return IRON_KEY_BACK_SLASH;
- if (symbol == XK_apostrophe)
- return IRON_KEY_QUOTE;
- if (symbol == XK_colon)
- return IRON_KEY_COLON;
- if (symbol == XK_semicolon)
- return IRON_KEY_SEMICOLON;
- if (symbol == XK_minus)
- return IRON_KEY_HYPHEN_MINUS;
- if (symbol == XK_underscore)
- return IRON_KEY_UNDERSCORE;
- if (symbol == XK_slash)
- return IRON_KEY_SLASH;
- if (symbol == XK_bar)
- return IRON_KEY_PIPE;
- if (symbol == XK_question)
- return IRON_KEY_QUESTIONMARK;
- if (symbol == XK_less)
- return IRON_KEY_LESS_THAN;
- if (symbol == XK_greater)
- return IRON_KEY_GREATER_THAN;
- if (symbol == XK_asterisk)
- return IRON_KEY_ASTERISK;
- if (symbol == XK_ampersand)
- return IRON_KEY_AMPERSAND;
- if (symbol == XK_asciicircum)
- return IRON_KEY_CIRCUMFLEX;
- if (symbol == XK_percent)
- return IRON_KEY_PERCENT;
- if (symbol == XK_dollar)
- return IRON_KEY_DOLLAR;
- if (symbol == XK_numbersign)
- return IRON_KEY_HASH;
- if (symbol == XK_at)
- return IRON_KEY_AT;
- if (symbol == XK_exclam)
- return IRON_KEY_EXCLAMATION;
- if (symbol == XK_equal)
- return IRON_KEY_EQUALS;
- if (symbol == XK_plus)
- return IRON_KEY_ADD;
- if (symbol == XK_quoteleft)
- return IRON_KEY_BACK_QUOTE;
- if (symbol == XK_quotedbl)
- return IRON_KEY_DOUBLE_QUOTE;
- if (symbol == XK_asciitilde)
- return IRON_KEY_TILDE;
- if (symbol == XK_Pause)
- return IRON_KEY_PAUSE;
- if (symbol == XK_Scroll_Lock)
- return IRON_KEY_SCROLL_LOCK;
- if (symbol == XK_Home)
- return IRON_KEY_HOME;
- if (symbol == XK_Page_Up)
- return IRON_KEY_PAGE_UP;
- if (symbol == XK_Page_Down)
- return IRON_KEY_PAGE_DOWN;
- if (symbol == XK_End)
- return IRON_KEY_END;
- if (symbol == XK_Insert)
- return IRON_KEY_INSERT;
- if (symbol == XK_KP_Enter)
- return IRON_KEY_RETURN;
- if (symbol == XK_KP_Multiply)
- return IRON_KEY_MULTIPLY;
- if (symbol == XK_KP_Add)
- return IRON_KEY_ADD;
- if (symbol == XK_KP_Subtract)
- return IRON_KEY_SUBTRACT;
- if (symbol == XK_KP_Decimal)
- return IRON_KEY_DECIMAL;
- if (symbol == XK_KP_Divide)
- return IRON_KEY_DIVIDE;
- if (symbol == XK_KP_0)
- return IRON_KEY_NUMPAD_0;
- if (symbol == XK_KP_1)
- return IRON_KEY_NUMPAD_1;
- if (symbol == XK_KP_2)
- return IRON_KEY_NUMPAD_2;
- if (symbol == XK_KP_3)
- return IRON_KEY_NUMPAD_3;
- if (symbol == XK_KP_4)
- return IRON_KEY_NUMPAD_4;
- if (symbol == XK_KP_5)
- return IRON_KEY_NUMPAD_5;
- if (symbol == XK_KP_6)
- return IRON_KEY_NUMPAD_6;
- if (symbol == XK_KP_7)
- return IRON_KEY_NUMPAD_7;
- if (symbol == XK_KP_8)
- return IRON_KEY_NUMPAD_8;
- if (symbol == XK_KP_9)
- return IRON_KEY_NUMPAD_9;
- if (symbol == XK_KP_Insert)
- return IRON_KEY_INSERT;
- if (symbol == XK_KP_Delete)
- return IRON_KEY_DELETE;
- if (symbol == XK_KP_End)
- return IRON_KEY_END;
- if (symbol == XK_KP_Home)
- return IRON_KEY_HOME;
- if (symbol == XK_KP_Left)
- return IRON_KEY_LEFT;
- if (symbol == XK_KP_Up)
- return IRON_KEY_UP;
- if (symbol == XK_KP_Right)
- return IRON_KEY_RIGHT;
- if (symbol == XK_KP_Down)
- return IRON_KEY_DOWN;
- if (symbol == XK_KP_Page_Up)
- return IRON_KEY_PAGE_UP;
- if (symbol == XK_KP_Page_Down)
- return IRON_KEY_PAGE_DOWN;
- if (symbol == XK_Menu)
- return IRON_KEY_CONTEXT_MENU;
- if (symbol == XK_a)
- return IRON_KEY_A;
- if (symbol == XK_b)
- return IRON_KEY_B;
- if (symbol == XK_c)
- return IRON_KEY_C;
- if (symbol == XK_d)
- return IRON_KEY_D;
- if (symbol == XK_e)
- return IRON_KEY_E;
- if (symbol == XK_f)
- return IRON_KEY_F;
- if (symbol == XK_g)
- return IRON_KEY_G;
- if (symbol == XK_h)
- return IRON_KEY_H;
- if (symbol == XK_i)
- return IRON_KEY_I;
- if (symbol == XK_j)
- return IRON_KEY_J;
- if (symbol == XK_k)
- return IRON_KEY_K;
- if (symbol == XK_l)
- return IRON_KEY_L;
- if (symbol == XK_m)
- return IRON_KEY_M;
- if (symbol == XK_n)
- return IRON_KEY_N;
- if (symbol == XK_o)
- return IRON_KEY_O;
- if (symbol == XK_p)
- return IRON_KEY_P;
- if (symbol == XK_q)
- return IRON_KEY_Q;
- if (symbol == XK_r)
- return IRON_KEY_R;
- if (symbol == XK_s)
- return IRON_KEY_S;
- if (symbol == XK_t)
- return IRON_KEY_T;
- if (symbol == XK_u)
- return IRON_KEY_U;
- if (symbol == XK_v)
- return IRON_KEY_V;
- if (symbol == XK_w)
- return IRON_KEY_W;
- if (symbol == XK_x)
- return IRON_KEY_X;
- if (symbol == XK_y)
- return IRON_KEY_Y;
- if (symbol == XK_z)
- return IRON_KEY_Z;
- if (symbol == XK_1)
- return IRON_KEY_1;
- if (symbol == XK_2)
- return IRON_KEY_2;
- if (symbol == XK_3)
- return IRON_KEY_3;
- if (symbol == XK_4)
- return IRON_KEY_4;
- if (symbol == XK_5)
- return IRON_KEY_5;
- if (symbol == XK_6)
- return IRON_KEY_6;
- if (symbol == XK_7)
- return IRON_KEY_7;
- if (symbol == XK_8)
- return IRON_KEY_8;
- if (symbol == XK_9)
- return IRON_KEY_9;
- if (symbol == XK_0)
- return IRON_KEY_0;
- if (symbol == XK_Escape)
- return IRON_KEY_ESCAPE;
- if (symbol == XK_F1)
- return IRON_KEY_F1;
- if (symbol == XK_F2)
- return IRON_KEY_F2;
- if (symbol == XK_F3)
- return IRON_KEY_F3;
- if (symbol == XK_F4)
- return IRON_KEY_F4;
- if (symbol == XK_F5)
- return IRON_KEY_F5;
- if (symbol == XK_F6)
- return IRON_KEY_F6;
- if (symbol == XK_F7)
- return IRON_KEY_F7;
- if (symbol == XK_F8)
- return IRON_KEY_F8;
- if (symbol == XK_F9)
- return IRON_KEY_F9;
- if (symbol == XK_F10)
- return IRON_KEY_F10;
- if (symbol == XK_F11)
- return IRON_KEY_F11;
- if (symbol == XK_F12)
- return IRON_KEY_F12;
- return IRON_KEY_UNKNOWN;
- }
- void *gc_alloc(size_t size);
- static char *uri_decode(const char *src) {
- char *res = gc_alloc(1024);
- char *dst = res;
- char a, b;
- while (*src) {
- if ((*src == '%') && ((a = src[1]) && (b = src[2])) && (isxdigit(a) && isxdigit(b))) {
- if (a >= 'a') {
- a -= 'a' - 'A';
- }
- if (a >= 'A') {
- a -= ('A' - 10);
- }
- else {
- a -= '0';
- }
- if (b >= 'a') {
- b -= 'a' - 'A';
- }
- if (b >= 'A') {
- b -= ('A' - 10);
- }
- else {
- b -= '0';
- }
- *dst++ = 16 * a + b;
- src += 3;
- }
- else {
- *dst++ = *src++;
- }
- }
- *dst++ = '\0';
- return res;
- }
- static bool _handle_messages() {
- static bool controlDown = false;
- static int ignoreKeycode = 0;
- static bool preventNextKeyDownEvent = false;
- while (XPending(x11_ctx.display)) {
- XEvent event;
- XNextEvent(x11_ctx.display, &event);
- if (event.type == GenericEvent && event.xcookie.extension == xi_extension) {
- check_pen_device(&x11_ctx.windows[0], &event, &x11_ctx.pen);
- check_pen_device(&x11_ctx.windows[0], &event, &x11_ctx.eraser);
- continue;
- }
- Window window = event.xclient.window;
- struct iron_x11_window *k_window = window_from_window(window);
- if (k_window == NULL) {
- continue;
- }
- switch (event.type) {
- case MappingNotify: {
- XRefreshKeyboardMapping(&event.xmapping);
- break;
- }
- case KeyPress: {
- XKeyEvent *key = (XKeyEvent *)&event;
- KeySym keysym;
- wchar_t wchar;
- bool wcConverted = XwcLookupString(k_window->xInputContext, key, &wchar, 1, &keysym, NULL);
- bool isIgnoredKeySym = keysym == XK_Escape || keysym == XK_BackSpace || keysym == XK_Delete;
- if (!controlDown && !XFilterEvent(&event, window) && !isIgnoredKeySym) {
- if (wcConverted) {
- iron_internal_keyboard_trigger_key_press(wchar);
- }
- }
- if (preventNextKeyDownEvent) {
- // this keypress is a repeated keystroke and should not lead to a keydown-event
- preventNextKeyDownEvent = false;
- continue;
- }
- KeySym ksKey = XkbKeycodeToKeysym(x11_ctx.display, event.xkey.keycode, 0, 0);
- if (ksKey == XK_Control_L || ksKey == XK_Control_R) {
- controlDown = true;
- }
- else if (controlDown && (ksKey == XK_v || ksKey == XK_V)) {
- XConvertSelection(x11_ctx.display, CLIPBOARD, UTF8_STRING, XSEL_DATA, window, CurrentTime);
- }
- else if (controlDown && (ksKey == XK_c || ksKey == XK_C)) {
- XSetSelectionOwner(x11_ctx.display, CLIPBOARD, window, CurrentTime);
- char *text = iron_internal_copy_callback();
- if (text != NULL)
- iron_copy_to_clipboard(text);
- }
- else if (controlDown && (ksKey == XK_x || ksKey == XK_X)) {
- XSetSelectionOwner(x11_ctx.display, CLIPBOARD, window, CurrentTime);
- char *text = iron_internal_cut_callback();
- if (text != NULL)
- iron_copy_to_clipboard(text);
- }
- if (event.xkey.keycode == ignoreKeycode) {
- break;
- }
- else {
- ignoreKeycode = event.xkey.keycode;
- }
- if (ksKey < 97 || ksKey > 122) {
- ksKey = keysym;
- }
- int key_code = xk_to_iron(ksKey);
- if (key_code != IRON_KEY_UNKNOWN) {
- iron_internal_keyboard_trigger_key_down(key_code);
- }
- break;
- }
- case KeyRelease: {
- XKeyEvent *key = (XKeyEvent *)&event;
- // peek next-event to determine if this a repeated-keystroke
- XEvent nev;
- if (XPending(x11_ctx.display)) {
- XPeekEvent(x11_ctx.display, &nev);
- if (nev.type == KeyPress && nev.xkey.time == event.xkey.time && nev.xkey.keycode == event.xkey.keycode) {
- // repeated keystroke! prevent this keyup-event and next keydown-event from being fired
- preventNextKeyDownEvent = true;
- continue;
- }
- }
- KeySym keysym;
- char c;
- XLookupString(key, &c, 1, &keysym, NULL);
- KeySym ksKey = XkbKeycodeToKeysym(x11_ctx.display, event.xkey.keycode, 0, 0);
- if (ksKey == XK_Control_L || ksKey == XK_Control_R) {
- controlDown = false;
- }
- if (event.xkey.keycode == ignoreKeycode) {
- ignoreKeycode = 0;
- }
- if (ksKey < 97 || ksKey > 122) {
- ksKey = keysym;
- }
- int key_code = xk_to_iron(ksKey);
- if (key_code != IRON_KEY_UNKNOWN) {
- iron_internal_keyboard_trigger_key_up(key_code);
- }
- break;
- }
- case ButtonPress: {
- XButtonEvent *button = (XButtonEvent *)&event;
- switch (button->button) {
- case Button1:
- iron_internal_mouse_trigger_press(0, button->x, button->y);
- break;
- case Button2:
- iron_internal_mouse_trigger_press(2, button->x, button->y);
- break;
- case Button3:
- iron_internal_mouse_trigger_press(1, button->x, button->y);
- break;
- // buttons 4-7 are for mouse wheel events because why not
- case Button4:
- case Button5:
- case Button6:
- case Button7:
- break;
- default:
- iron_internal_mouse_trigger_press(button->button - Button1 - 4, button->x, button->y);
- break;
- }
- break;
- }
- case ButtonRelease: {
- XButtonEvent *button = (XButtonEvent *)&event;
- switch (button->button) {
- case Button1:
- iron_internal_mouse_trigger_release(0, button->x, button->y);
- break;
- case Button2:
- iron_internal_mouse_trigger_release(2, button->x, button->y);
- break;
- case Button3:
- iron_internal_mouse_trigger_release(1, button->x, button->y);
- break;
- // Button4 and Button5 provide mouse wheel events because why not
- case Button4:
- iron_internal_mouse_trigger_scroll(-1.0);
- break;
- case Button5:
- iron_internal_mouse_trigger_scroll(1.0);
- break;
- // button 6 and 7 seem to be horizontal scrolling, which is not exposed in Iron's api at the moment
- case Button6:
- case Button7:
- break;
- default:
- iron_internal_mouse_trigger_release(button->button - Button1 - 4, button->x, button->y);
- break;
- }
- break;
- }
- case MotionNotify: {
- XMotionEvent *motion = (XMotionEvent *)&event;
- iron_internal_mouse_trigger_move(motion->x, motion->y);
- break;
- }
- case ConfigureNotify: {
- if (event.xconfigure.width != k_window->width || event.xconfigure.height != k_window->height) {
- k_window->width = event.xconfigure.width;
- k_window->height = event.xconfigure.height;
- gpu_resize(event.xconfigure.width, event.xconfigure.height);
- iron_internal_call_resize_callback(event.xconfigure.width, event.xconfigure.height);
- }
- break;
- }
- case ClientMessage: {
- if (event.xclient.message_type == XdndEnter) {
- Window source_window = event.xclient.data.l[0];
- XEvent m;
- memset(&m, 0, sizeof(m));
- m.type = ClientMessage;
- m.xclient.window = event.xclient.data.l[0];
- m.xclient.message_type = XdndStatus;
- m.xclient.format = 32;
- m.xclient.data.l[0] = window;
- m.xclient.data.l[2] = 0;
- m.xclient.data.l[3] = 0;
- m.xclient.data.l[1] = 1;
- m.xclient.data.l[4] = XdndActionCopy;
- XSendEvent(x11_ctx.display, source_window, false, NoEventMask, (XEvent *)&m);
- XFlush(x11_ctx.display);
- }
- else if (event.xclient.message_type == XdndDrop) {
- XConvertSelection(x11_ctx.display, XdndSelection, XdndTextUriList, XdndSelection, window, event.xclient.data.l[2]);
- }
- else if (event.xclient.data.l[0] == WM_DELETE_WINDOW) {
- if (iron_internal_call_close_callback()) {
- iron_window_destroy();
- iron_stop();
- }
- }
- break;
- }
- case SelectionNotify: {
- if (event.xselection.selection == CLIPBOARD) {
- char *result;
- unsigned long ressize, restail;
- int resbits;
- XGetWindowProperty(x11_ctx.display, window, XSEL_DATA, 0, LONG_MAX / 4, False, AnyPropertyType, &UTF8_STRING, &resbits, &ressize, &restail,
- (unsigned char **)&result);
- iron_internal_paste_callback(result);
- XFree(result);
- }
- else if (event.xselection.property == XdndSelection) {
- Atom type;
- int format;
- unsigned long numItems;
- unsigned long bytesAfter = 1;
- unsigned char *data = 0;
- XGetWindowProperty(x11_ctx.display, event.xselection.requestor, event.xselection.property, 0, LONG_MAX, False, event.xselection.target, &type,
- &format, &numItems, &bytesAfter, &data);
- size_t pos = 0;
- size_t len = 0;
- while (pos < numItems) {
- if (data[pos] == '\r') { // Found a file
- buffer[len] = 0;
- len = 0;
- pos += 2; // Avoid \n
- char *res = uri_decode(buffer + 7); // + 7 -> strip file://
- iron_internal_drop_files_callback(res);
- }
- buffer[len++] = data[pos++];
- }
- XFree(data);
- }
- break;
- }
- case SelectionRequest: {
- if (event.xselectionrequest.target == TARGETS) {
- XEvent send;
- send.xselection.type = SelectionNotify;
- send.xselection.requestor = event.xselectionrequest.requestor;
- send.xselection.selection = event.xselectionrequest.selection;
- send.xselection.target = event.xselectionrequest.target;
- send.xselection.property = event.xselectionrequest.property;
- send.xselection.time = event.xselectionrequest.time;
- Atom available[] = {TARGETS, MULTIPLE, TEXT_PLAIN, UTF8_STRING};
- XChangeProperty(x11_ctx.display, send.xselection.requestor, send.xselection.property, XA_ATOM, 32, PropModeReplace,
- (unsigned char *)&available[0], 4);
- XSendEvent(x11_ctx.display, send.xselection.requestor, True, 0, &send);
- }
- if (event.xselectionrequest.target == TEXT_PLAIN || event.xselectionrequest.target == UTF8_STRING) {
- XEvent send;
- send.xselection.type = SelectionNotify;
- send.xselection.requestor = event.xselectionrequest.requestor;
- send.xselection.selection = event.xselectionrequest.selection;
- send.xselection.target = event.xselectionrequest.target;
- send.xselection.property = event.xselectionrequest.property;
- send.xselection.time = event.xselectionrequest.time;
- XChangeProperty(x11_ctx.display, send.xselection.requestor, send.xselection.property, send.xselection.target, 8, PropModeReplace,
- (const unsigned char *)clipboardString, strlen(clipboardString));
- XSendEvent(x11_ctx.display, send.xselection.requestor, True, 0, &send);
- }
- break;
- }
- case Expose: {
- break;
- }
- case FocusIn: {
- iron_internal_foreground_callback();
- break;
- }
- case FocusOut: {
- controlDown = false;
- ignoreKeycode = 0;
- iron_internal_background_callback();
- break;
- }
- case LeaveNotify: {
- break;
- }
- case EnterNotify: {
- break;
- }
- }
- }
- return true;
- }
- bool iron_internal_handle_messages() {
- if (!_handle_messages()) {
- return false;
- }
- #ifdef WITH_GAMEPAD
- iron_linux_updateHIDGamepads();
- #endif
- return true;
- }
- const char *iron_system_id() {
- return "Linux";
- }
- void iron_set_keep_screen_on(bool on) {}
- void iron_keyboard_show() {}
- void iron_keyboard_hide() {}
- bool iron_keyboard_active() {
- return true;
- }
- void iron_load_url(const char *url) {
- if (strncmp(url, "http://", sizeof("http://") - 1) == 0 || strncmp(url, "https://", sizeof("https://") - 1) == 0) {
- char openUrlCommand[256];
- snprintf(openUrlCommand, 256, "xdg-open %s", url);
- system(openUrlCommand);
- }
- }
- const char *iron_language() {
- return "en";
- }
- const char *iron_internal_save_path() {
- // first check for an existing directory in $HOME
- // if one exists, use it, else create one in $XDG_DATA_HOME
- // See: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
- if (!saveInitialized) {
- const char *homedir;
- if ((homedir = getenv("HOME")) == NULL) {
- homedir = getpwuid(getuid())->pw_dir;
- }
- strcpy(save, homedir);
- strcat(save, "/.");
- strcat(save, iron_application_name());
- strcat(save, "/");
- struct stat st;
- if (stat(save, &st) == 0) {
- // use existing folder in $HOME
- }
- else {
- // use XDG folder
- const char *data_home;
- if ((data_home = getenv("XDG_DATA_HOME")) == NULL) {
- // $XDG_DATA_HOME is not defined, fall back to the default, $HOME/.local/share
- strcpy(save, homedir);
- strcat(save, "/.local/share/");
- }
- else {
- // use $XDG_DATA_HOME
- strcpy(save, data_home);
- if (data_home[strlen(data_home) - 1] != '/') {
- strcat(save, "/");
- }
- }
- strcat(save, iron_application_name());
- strcat(save, "/");
- int res = mkdir(save, 0700);
- if (res != 0 && errno != EEXIST) {
- iron_error("Could not create save directory '%s'. Error %d", save, errno);
- }
- }
- saveInitialized = true;
- }
- return save;
- }
- const char **iron_video_formats() {
- return videoFormats;
- }
- double iron_frequency(void) {
- return 1000000.0;
- }
- uint64_t iron_timestamp(void) {
- struct timeval now;
- gettimeofday(&now, NULL);
- now.tv_sec -= start.tv_sec;
- now.tv_usec -= start.tv_usec;
- return (uint64_t)now.tv_sec * 1000000 + (uint64_t)now.tv_usec;
- }
- void iron_init(iron_window_options_t *ops) {
- gettimeofday(&start, NULL);
- #ifdef WITH_GAMEPAD
- iron_linux_initHIDGamepads();
- #endif
- iron_x11_init();
- iron_display_init();
- iron_set_app_name(ops->title);
- iron_window_create(ops);
- }
- void iron_internal_shutdown() {
- gpu_destroy();
- #ifdef WITH_GAMEPAD
- iron_linux_closeHIDGamepads();
- #endif
- free(clipboardString);
- XCloseDisplay(x11_ctx.display);
- iron_internal_shutdown_callback();
- }
- #ifndef IRON_NO_MAIN
- int main(int argc, char **argv) {
- return kickstart(argc, argv);
- }
- #endif
- void iron_copy_to_clipboard(const char *text) {
- size_t textLength = strlen(text);
- if (textLength >= clipboardStringSize) {
- free(clipboardString);
- clipboardStringSize = textLength + 1;
- clipboardString = (char *)malloc(clipboardStringSize);
- }
- strcpy(clipboardString, text);
- }
- int iron_hardware_threads(void) {
- return sysconf(_SC_NPROCESSORS_ONLN);
- }
- bool iron_mouse_can_lock(void) {
- return true;
- }
- void iron_mouse_show() {
- struct iron_x11_window *window = &x11_ctx.windows[0];
- if (mouse_hidden) {
- XUndefineCursor(x11_ctx.display, window->window);
- mouse_hidden = false;
- }
- }
- void iron_mouse_hide() {
- struct iron_x11_window *window = &x11_ctx.windows[0];
- if (!mouse_hidden) {
- XColor col;
- col.pixel = 0;
- col.red = 0;
- col.green = 0;
- col.blue = 0;
- col.flags = DoRed | DoGreen | DoBlue;
- col.pad = 0;
- char data[1] = {'\0'};
- Pixmap blank = XCreateBitmapFromData(x11_ctx.display, window->window, data, 1, 1);
- Cursor cursor = XCreatePixmapCursor(x11_ctx.display, blank, blank, &col, &col, 0, 0);
- XDefineCursor(x11_ctx.display, window->window, cursor);
- XFreePixmap(x11_ctx.display, blank);
- mouse_hidden = true;
- }
- }
- void iron_mouse_set_cursor(iron_cursor_t cursor_index) {
- if (mouse_hidden) {
- return;
- }
- Cursor cursor;
- if (cursor_index == IRON_CURSOR_HAND) {
- cursor = XcursorLibraryLoadCursor(x11_ctx.display, "hand1");
- }
- else if (cursor_index == IRON_CURSOR_IBEAM) {
- cursor = XcursorLibraryLoadCursor(x11_ctx.display, "xterm");
- }
- else if (cursor_index == IRON_CURSOR_SIZEWE) {
- cursor = XcursorLibraryLoadCursor(x11_ctx.display, "sb_h_double_arrow");
- }
- else if (cursor_index == IRON_CURSOR_SIZENS) {
- cursor = XcursorLibraryLoadCursor(x11_ctx.display, "sb_v_double_arrow");
- }
- else {
- cursor = XcursorLibraryLoadCursor(x11_ctx.display, "left_ptr"); // "arrow"
- }
- struct iron_x11_window *window = &x11_ctx.windows[0];
- XDefineCursor(x11_ctx.display, window->window, cursor);
- }
- void iron_mouse_set_position(int x, int y) {
- struct iron_x11_window *window = &x11_ctx.windows[0];
- XWarpPointer(x11_ctx.display, None, window->window, 0, 0, 0, 0, x, y);
- XFlush(x11_ctx.display);
- }
- void iron_mouse_get_position(int *x, int *y) {
- struct iron_x11_window *window = &x11_ctx.windows[0];
- Window inwin;
- Window inchildwin;
- int rootx, rooty;
- unsigned int mask;
- XQueryPointer(x11_ctx.display, window->window, &inwin, &inchildwin, &rootx, &rooty, x, y, &mask);
- }
- #ifdef WITH_GAMEPAD
- #include <fcntl.h>
- #include <libudev.h>
- #include <linux/joystick.h>
- struct HIDGamepad {
- int idx;
- char gamepad_dev_name[256];
- char name[385];
- int file_descriptor;
- bool connected;
- struct js_event gamepadEvent;
- };
- static void HIDGamepad_open(struct HIDGamepad *pad) {
- pad->file_descriptor = open(pad->gamepad_dev_name, O_RDONLY | O_NONBLOCK);
- if (pad->file_descriptor < 0) {
- pad->connected = false;
- }
- else {
- pad->connected = true;
- char buf[128];
- if (ioctl(pad->file_descriptor, JSIOCGNAME(sizeof(buf)), buf) < 0) {
- strncpy(buf, "Unknown", sizeof(buf));
- }
- pad->name[0] = 0;
- // snprintf(pad->name, sizeof(pad->name), "%s(%s)", buf, pad->gamepad_dev_name); // TODO: valgrind error
- iron_internal_gamepad_trigger_connect(pad->idx);
- }
- }
- static void HIDGamepad_init(struct HIDGamepad *pad, int index) {
- pad->file_descriptor = -1;
- pad->connected = false;
- pad->gamepad_dev_name[0] = 0;
- if (index >= 0 && index < 12) {
- pad->idx = index;
- snprintf(pad->gamepad_dev_name, sizeof(pad->gamepad_dev_name), "/dev/input/js%d", pad->idx);
- HIDGamepad_open(pad);
- }
- }
- static void HIDGamepad_close(struct HIDGamepad *pad) {
- if (pad->connected) {
- iron_internal_gamepad_trigger_disconnect(pad->idx);
- close(pad->file_descriptor);
- pad->file_descriptor = -1;
- pad->connected = false;
- }
- }
- void HIDGamepad_processEvent(struct HIDGamepad *pad, struct js_event e) {
- switch (e.type) {
- case JS_EVENT_BUTTON:
- iron_internal_gamepad_trigger_button(pad->idx, e.number, e.value);
- break;
- case JS_EVENT_AXIS: {
- float value = e.number % 2 == 0 ? e.value : -e.value;
- iron_internal_gamepad_trigger_axis(pad->idx, e.number, value / 32767.0f);
- break;
- }
- default:
- break;
- }
- }
- void HIDGamepad_update(struct HIDGamepad *pad) {
- if (pad->connected) {
- while (read(pad->file_descriptor, &pad->gamepadEvent, sizeof(pad->gamepadEvent)) > 0) {
- HIDGamepad_processEvent(pad, pad->gamepadEvent);
- }
- }
- }
- struct HIDGamepadUdevHelper {
- struct udev *udevPtr;
- struct udev_monitor *udevMonitorPtr;
- int udevMonitorFD;
- };
- static struct HIDGamepadUdevHelper udev_helper;
- static struct HIDGamepad gamepads[IRON_GAMEPAD_MAX_COUNT];
- static void HIDGamepadUdevHelper_openOrCloseGamepad(struct HIDGamepadUdevHelper *helper, struct udev_device *dev) {
- const char *action = udev_device_get_action(dev);
- if (!action)
- action = "add";
- const char *joystickDevnodeName = strstr(udev_device_get_devnode(dev), "js");
- if (joystickDevnodeName) {
- int joystickDevnodeIndex;
- sscanf(joystickDevnodeName, "js%d", &joystickDevnodeIndex);
- if (!strcmp(action, "add")) {
- HIDGamepad_open(&gamepads[joystickDevnodeIndex]);
- }
- if (!strcmp(action, "remove")) {
- HIDGamepad_close(&gamepads[joystickDevnodeIndex]);
- }
- }
- }
- static void HIDGamepadUdevHelper_processDevice(struct HIDGamepadUdevHelper *helper, struct udev_device *dev) {
- if (dev) {
- if (udev_device_get_devnode(dev))
- HIDGamepadUdevHelper_openOrCloseGamepad(helper, dev);
- udev_device_unref(dev);
- }
- }
- static void HIDGamepadUdevHelper_init(struct HIDGamepadUdevHelper *helper) {
- struct udev *udevPtrNew = udev_new();
- // enumerate
- struct udev_enumerate *enumerate = udev_enumerate_new(udevPtrNew);
- udev_enumerate_add_match_subsystem(enumerate, "input");
- udev_enumerate_scan_devices(enumerate);
- struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
- struct udev_list_entry *entry;
- udev_list_entry_foreach(entry, devices) {
- const char *path = udev_list_entry_get_name(entry);
- struct udev_device *dev = udev_device_new_from_syspath(udevPtrNew, path);
- HIDGamepadUdevHelper_processDevice(helper, dev);
- }
- udev_enumerate_unref(enumerate);
- // setup mon
- helper->udevMonitorPtr = udev_monitor_new_from_netlink(udevPtrNew, "udev");
- udev_monitor_filter_add_match_subsystem_devtype(helper->udevMonitorPtr, "input", NULL);
- udev_monitor_enable_receiving(helper->udevMonitorPtr);
- helper->udevMonitorFD = udev_monitor_get_fd(helper->udevMonitorPtr);
- helper->udevPtr = udevPtrNew;
- }
- static void HIDGamepadUdevHelper_update(struct HIDGamepadUdevHelper *helper) {
- fd_set fds;
- FD_ZERO(&fds);
- FD_SET(helper->udevMonitorFD, &fds);
- if (FD_ISSET(helper->udevMonitorFD, &fds)) {
- struct udev_device *dev = udev_monitor_receive_device(helper->udevMonitorPtr);
- HIDGamepadUdevHelper_processDevice(helper, dev);
- }
- }
- static void HIDGamepadUdevHelper_close(struct HIDGamepadUdevHelper *helper) {
- udev_unref(helper->udevPtr);
- }
- void iron_linux_initHIDGamepads() {
- for (int i = 0; i < IRON_GAMEPAD_MAX_COUNT; ++i) {
- HIDGamepad_init(&gamepads[i], i);
- }
- HIDGamepadUdevHelper_init(&udev_helper);
- }
- void iron_linux_updateHIDGamepads() {
- HIDGamepadUdevHelper_update(&udev_helper);
- for (int i = 0; i < IRON_GAMEPAD_MAX_COUNT; ++i) {
- HIDGamepad_update(&gamepads[i]);
- }
- }
- void iron_linux_closeHIDGamepads() {
- HIDGamepadUdevHelper_close(&udev_helper);
- }
- const char *iron_gamepad_vendor(int gamepad) {
- return "Linux gamepad";
- }
- const char *iron_gamepad_product_name(int gamepad) {
- return gamepad >= 0 && gamepad < IRON_GAMEPAD_MAX_COUNT ? gamepads[gamepad].name : "";
- }
- bool iron_gamepad_connected(int gamepad) {
- return gamepad >= 0 && gamepad < IRON_GAMEPAD_MAX_COUNT && gamepads[gamepad].connected;
- }
- void iron_gamepad_rumble(int gamepad, float left, float right) {}
- #endif
- #ifdef WITH_NFD // Has gtk
- #include <gtk/gtk.h>
- extern void (*iron_save_and_quit)(bool);
- #endif
- bool _save_and_quit_callback_internal() {
- #ifdef WITH_NFD // Has gtk
- bool save = false;
- XTextProperty text_prop;
- XGetTextProperty(x11_ctx.display, x11_ctx.windows[0].window, &text_prop, NET_WM_NAME);
- char *title = (char *)text_prop.value;
- bool dirty = strstr(title, "* - ArmorPaint") != NULL;
- // XFree(title);
- if (dirty) {
- gtk_init(NULL, NULL);
- GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, "Project has been modified, save changes?");
- gtk_window_set_title(GTK_WINDOW(dialog), "Save Changes?");
- gtk_widget_realize(dialog);
- gint res = gtk_dialog_run(GTK_DIALOG(dialog));
- gtk_widget_destroy(dialog);
- while (g_main_context_pending(NULL)) {
- g_main_context_iteration(NULL, FALSE);
- }
- if (res == GTK_RESPONSE_YES) {
- save = true;
- }
- else if (res == GTK_RESPONSE_NO) {
- save = false;
- }
- else {
- return false;
- }
- }
- iron_save_and_quit(save);
- #endif
- return false;
- }
- #include <signal.h>
- #include <sys/wait.h>
- static pid_t child_pid = -1;
- volatile int iron_exec_async_done = 1;
- void iron_exec_handler(int sig) {
- int status;
- if (waitpid(child_pid, &status, WNOHANG) > 0) {
- iron_exec_async_done = 1;
- }
- }
- void iron_exec_async(const char *path, char *argv[]) {
- iron_exec_async_done = 0;
- child_pid = fork();
- if (child_pid == 0) {
- execve(path, argv, NULL);
- exit(1);
- }
- else {
- struct sigaction sa = {0};
- sa.sa_handler = iron_exec_handler;
- sa.sa_flags = SA_RESTART;
- sigemptyset(&sa.sa_mask);
- sigaction(SIGCHLD, &sa, NULL);
- }
- }
|