/*- * 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" static Display *display; static Window window; static GLXContext gl_context; static int close_window = 0; void (*on_loop_event)(); void (*on_expose_event)(); void (*on_resize_event)(UIEventResize *); void (*on_mouse_press_event)(UIMouseButtonPressed *); void (*on_generic_event)(int); static void cleanup(UI *ui) { glXMakeCurrent(display, None, NULL); glXDestroyContext(display, gl_context); XDestroyWindow(display, window); XCloseDisplay(display); 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) { 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) { 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, int width, int height) { XSetWindowAttributes swa = ui_get_attributes(root, vi); Window window = XCreateWindow(display, root, 0, 0, width, height, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &swa); XMapWindow(display, window); return window; } void ui_set_title(const char *title) { XStoreName(display, window, title); } /* * 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; display = ui_open_display(); int screen = DefaultScreen(display); Window root = RootWindow(display, screen); XVisualInfo *vi = gl_choose_visual(screen); window = ui_create_window(root, vi, width, height); gl_context = glXCreateContext(display, vi, NULL, GL_TRUE); if (!glXMakeCurrent(display, window, gl_context)) { log_error(log, "Error on making GLX context"); exit(1); } 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(XEvent event) { if (event.type != KeyPress) return; 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(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(display, 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) { XEvent event; while (!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(event); ui_on_mouse_press(event); ui_on_mouse_release(event); } on_loop_event(); glXSwapBuffers(display, window); } cleanup(ui); }