/*- * Copyright (C) 2025 Alessandro Iezzi * * This file is part of Tris Game. * * Tris Game is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Tris Game is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Tris Game. If not, see . */ #include #include #include #include #include #include #include #include #include "../types.h" #include "../ui.h" typedef struct { Display *display; Window window; GLXContext gl_context; int close_window; } X11UI; void (*on_loop_event)(); void (*on_expose_event)(); void (*on_resize_event)(UIEventResize *); void (*on_mouse_press_event)(UIMouseButtonPressed *); void (*on_generic_event)(int); /* Private functions */ static void ui_cleanup(UI *ui) { Display *display = ((X11UI *) ui->extra)->display; Window window = ((X11UI *) ui->extra)->window; GLXContext gl_context = ((X11UI *) ui->extra)->gl_context; glXMakeCurrent(display, None, NULL); glXDestroyContext(display, gl_context); XDestroyWindow(display, window); XCloseDisplay(display); free(ui->extra); free(ui); } static Log *log = NULL; static void init_log(void) { if (log != NULL) return; log = log_create("UI"); } static Display * ui_open_display(void) { init_log(); Display *display = XOpenDisplay(NULL); if (!display) { log_error(log, "Can't open X11 display"); exit(1); } return display; } static XVisualInfo * gl_choose_visual(int screen, Display *display) { init_log(); GLint attribs[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None }; XVisualInfo *vi = glXChooseVisual(display, screen, attribs); if (!vi) { log_error(log, "No compatible Visual found"); exit(1); } return vi; } static XSetWindowAttributes ui_get_attributes(Window root, XVisualInfo *vi, Display *display) { Colormap colormap = XCreateColormap(display, root, vi->visual, AllocNone); XSetWindowAttributes swa; swa.colormap = colormap; swa.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask | ButtonPressMask | ButtonReleaseMask; return swa; } static Window ui_create_window(Window root, XVisualInfo *vi, Display *display, int width, int height) { XSetWindowAttributes swa = ui_get_attributes(root, vi, display); Window window = XCreateWindow(display, root, 0, 0, width, height, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &swa); XMapWindow(display, window); return window; } /****** Public functions ******/ void ui_set_title(UI *ui, const char *title) { X11UI *x11ui = ui->extra; XStoreName(x11ui->display, x11ui->window, title); } static X11UI * ui_x11_new(void) { X11UI *x11ui = malloc(sizeof(X11UI)); x11ui->display = ui_open_display(); return x11ui; } /* * This is the function used to init a UI. */ UI * ui_new(int width, int height) { init_log(); /* Inizilize the UI */ UI *ui = malloc(sizeof(UI)); if (ui == NULL) { log_error(log, "Error allocating the UI"); exit(EXIT_FAILURE); } ui->width = width; ui->height = height; ui->close_window = 0; X11UI *x11ui = ui_x11_new(); ui->extra = x11ui; Display *display = x11ui->display; int screen = DefaultScreen(display); Window root = RootWindow(display, screen); XVisualInfo *vi = gl_choose_visual(screen, display); x11ui->window = ui_create_window(root, vi, display, width, height); x11ui->gl_context = glXCreateContext(display, vi, NULL, GL_TRUE); if (!glXMakeCurrent(display, x11ui->window, x11ui->gl_context)) { log_error(log, "Error on making GLX context"); exit(EXIT_FAILURE); } return ui; } static void ui_on_expose(XEvent event) { if (event.type != Expose) return; if (on_expose_event != NULL) { on_expose_event(); } } static void ui_on_resize(UI *ui, XEvent event) { init_log(); if (event.type != ConfigureNotify) return; if (on_resize_event != NULL) { UIEventResize *er = malloc(sizeof(UIEventResize)); if (er == NULL) { log_error(log, "Error allocating UIEventResize"); exit(EXIT_FAILURE); } er->width = event.xconfigure.width; er->height = event.xconfigure.height; ui->width = er->width; ui->height = er->height; on_resize_event(er); } } static void ui_on_keypress(UI *ui, XEvent event) { if (event.type != KeyPress) return; ui->close_window = 1; } static void ui_get_mouse_position(Display *display, Window window, int *x, int *y) { Window root_return, child_return; int root_x, root_y; unsigned int mask_return; XQueryPointer(display, window, &root_return, &child_return, &root_x, &root_y, x, y, &mask_return); } static void ui_on_mouse_press(X11UI *x11ui, XEvent event) { init_log(); if (event.type != ButtonPress) return; if (on_mouse_press_event != NULL) { UIMouseButtonPressed *mbp = malloc(sizeof(UIMouseButtonPressed)); if (mbp == NULL) { log_error(log, "Error allocating UIMouseButtonPressed"); exit(EXIT_FAILURE); } ui_get_mouse_position(x11ui->display, x11ui->window, &mbp->x, &mbp->y); on_mouse_press_event(mbp); } } static void ui_on_mouse_release(XEvent event) { if (event.type != ButtonRelease) return; } static void ui_on_generic_event(XEvent event) { if (on_generic_event != NULL) { on_generic_event(event.type); } } void ui_set_loop_listener(void (*loop_event)()) { on_loop_event = loop_event; } void ui_set_expose_listener(void (*expose_event)()) { on_expose_event = expose_event; } void ui_set_resize_listener(void (*resize_event)(UIEventResize *)) { on_resize_event = resize_event; } void ui_set_mouse_press_listener(void (*mouse_press_event)(UIMouseButtonPressed *)) { on_mouse_press_event = mouse_press_event; } void ui_set_generic_listener(void (*generic_event)(int type)) { on_generic_event = generic_event; } void ui_loop(UI *ui) { X11UI *x11ui = ui->extra; Display *display = x11ui->display; Window window = x11ui->window; XEvent event; while (!ui->close_window) { while (XPending(display)) { XNextEvent(display, &event); ui_on_generic_event(event); ui_on_expose(event); ui_on_resize(ui, event); ui_on_keypress(ui, event); ui_on_mouse_press(x11ui, event); ui_on_mouse_release(event); } on_loop_event(); glXSwapBuffers(display, window); } ui_cleanup(ui); }