diff options
Diffstat (limited to 'src/os/linux/linux_window.c')
-rw-r--r-- | src/os/linux/linux_window.c | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/src/os/linux/linux_window.c b/src/os/linux/linux_window.c new file mode 100644 index 0000000..d739b3c --- /dev/null +++ b/src/os/linux/linux_window.c @@ -0,0 +1,304 @@ +// Note: This is probably deprecated and can be deleted. + +#include <basic/basic.h> +#include <os/os.h> + +#include <SDL3/SDL.h> +#include <X11/Xlib.h> +#include <GL/gl.h> +#include <GL/glx.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +struct OSWindow { + Window xid; + Atom wm_delete_window; + u32 event_mask; + + i32 width; + i32 height; + + GLXContext glx_context; + u32 gl_texture_id; + OSOffscreenBuffer offscreen_buffer; + + OSEvent current_event; +}; + + +internal_var Display *g_display; + +internal_fn b32 +os_connect_to_x(void) +{ + const char *display_name = 0; + g_display = XOpenDisplay(display_name); + if (!g_display) + { + printf("XOpenDisplay(%s) failed\n", display_name); + return false; + } + + return true; +} + + +void +os_window_destroy_offscreen_buffer(OSOffscreenBuffer *offscreen_buffer) +{ + free(offscreen_buffer->pixels); +} + +void +os_offscreen_buffer_resize(OSOffscreenBuffer *offscreen, i32 width, i32 height) +{ + u32 *new_pixels = realloc(offscreen->pixels, width*height*4); + if (new_pixels) { + offscreen->width = width; + offscreen->height = height; + offscreen->pixels = new_pixels; + glViewport(0, 0, width, height); + } +} + +void +os_offscreen_buffer_destroy(OSOffscreenBuffer *offscreen_buffer) +{ + free(offscreen_buffer->pixels); + free(offscreen_buffer); +} + +OSOffscreenBuffer * +os_offscreen_buffer_create(i32 width, i32 height) +{ + OSOffscreenBuffer *offscreen_buffer = malloc(sizeof(OSOffscreenBuffer)); + if (offscreen_buffer) { + offscreen_buffer->green_shift = 8; + offscreen_buffer->blue_shift = 16; + offscreen_buffer->red_shift = 0; + offscreen_buffer->alpha_shift = 24; + offscreen_buffer->width = 0; + offscreen_buffer->height = 0; + offscreen_buffer->pixels = 0; + } + os_offscreen_buffer_resize(offscreen_buffer, width, height); + return offscreen_buffer; +} + +void +os_window_swap_buffers(OSWindow *window, OSOffscreenBuffer *offscreen_buffer) +{ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, window->gl_texture_id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, offscreen_buffer->width, offscreen_buffer->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, offscreen_buffer->pixels); + + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, -1.0f); + glTexCoord2f(1.0f, 0.0f); glVertex2f( 1.0f, -1.0f); + glTexCoord2f(1.0f, 1.0f); glVertex2f( 1.0f, 1.0f); + glTexCoord2f(0.0f, 1.0f); glVertex2f(-1.0f, 1.0f); + glEnd(); + + glBindTexture(GL_TEXTURE_2D, 0); + + glXSwapBuffers(g_display, window->xid); +} + + +#define ADD_KEY_PRESS(c) \ + event->type = OS_EVENT_KEY_PRESS; \ + event->ev.key_press.code = c; \ + event->ev.key_press.is_unicode = true; \ + return true; +#define ADD_SPECIAL_KEY_PRESS(c) \ + event->type = OS_EVENT_KEY_PRESS; \ + event->ev.key_press.code = c; \ + event->ev.key_press.is_unicode = false; \ + return true; + +b32 +os_window_get_event(OSWindow *window, OSEvent *event) +{ + while (XPending(g_display)) { + XEvent xevent; + XNextEvent(g_display, &xevent); + + // Todo: Rework this whole shift/caps-lock logic. + persist_var b32 is_caps; + persist_var b32 is_shift_l; + persist_var b32 is_shift_r; + b32 is_uppercase = ( is_caps && !(is_shift_l || is_shift_r)) || + (!is_caps && (is_shift_l || is_shift_r)); + + switch (xevent.type) { + case ClientMessage: { + if ((Atom)xevent.xclient.data.l[0] == window->wm_delete_window) { + event->type = OS_EVENT_WINDOW_DESTROYED; + return true; + } + continue; + } break; + + case DestroyNotify: { + event->type = OS_EVENT_WINDOW_DESTROYED; + return true; + } break; + + case ConfigureNotify: { + i32 width = xevent.xconfigure.width; + i32 height = xevent.xconfigure.height; + if (width != window->width || height != window->height) { + event->type = OS_EVENT_WINDOW_RESIZE; + event->ev.resize.width = width; + event->ev.resize.height = height; + window->width = width; + window->height = height; + return true; + } + continue; + } break; + + case KeyPress: { + int index = is_uppercase ? 1 : 0; + KeySym keysym = XLookupKeysym(&xevent.xkey, index); + if (keysym >= 32 && keysym <= 126) {ADD_KEY_PRESS(keysym);} + else if (keysym == XK_Tab) {ADD_KEY_PRESS('\t'); } + else if (keysym == XK_Return) {ADD_KEY_PRESS('\r'); } + else if (keysym == XK_BackSpace) {ADD_KEY_PRESS('\b'); } + else if (keysym == XK_Delete) {ADD_KEY_PRESS(127); } + else if (keysym == XK_Left) {ADD_SPECIAL_KEY_PRESS(OS_KEYCODE_LEFT); } + else if (keysym == XK_Right) {ADD_SPECIAL_KEY_PRESS(OS_KEYCODE_RIGHT);} + else if (keysym == XK_Up) {ADD_SPECIAL_KEY_PRESS(OS_KEYCODE_UP); } + else if (keysym == XK_Down) {ADD_SPECIAL_KEY_PRESS(OS_KEYCODE_DOWN); } + else if (keysym == XK_Shift_L) is_shift_l = true; + else if (keysym == XK_Shift_R) is_shift_r = true; + else if (keysym == XK_Caps_Lock) is_caps = true; + } break; + + case KeyRelease: { + int index = 0; + KeySym keysym = XLookupKeysym(&xevent.xkey, index); + if (keysym == XK_Shift_L) is_shift_l = false; + else if (keysym == XK_Shift_R) is_shift_r = false; + else if (keysym == XK_Caps_Lock) is_caps = false; + continue; // ignore this for now i guess + } break; + + case GraphicsExpose: { + printf("graphics exposure happened\n"); + continue; + } break; + + default:; + } + } + return false; +} + +#undef ADD_KEY_PRESS +#undef ADD_SPECIAL_KEY_PRESS + + +void os_window_destroy(OSWindow *window) { + os_window_destroy_offscreen_buffer(&window->offscreen_buffer); + XDestroyWindow(g_display, window->xid); + free(window); +} + +OSWindow *os_window_create(const char *name, i32 width, i32 height) { + if (!g_display) { + if (!os_connect_to_x()) + return 0; + } + + int screen_number = XDefaultScreen(g_display); + Window root_window_id = XRootWindow(g_display, screen_number); + if (!root_window_id) + { + printf("XDefaultRootWindow(display) failed\n"); + XCloseDisplay(g_display); + return 0; + } + + // get visual info + int depth = 24; + int va[] = { + GLX_RGBA, 1, + GLX_DOUBLEBUFFER, 1, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 8, + GLX_DEPTH_SIZE, depth, + None + }; + XVisualInfo* vinfo = glXChooseVisual(g_display, screen_number, va); + if (!vinfo) + { + printf("glXChooseVisual failed\n"); + return 0; + } + + // set window attribs + long swa_mask = CWEventMask | CWColormap | CWBackPixmap |CWBorderPixel; + long swa_event_mask = PropertyChangeMask | SubstructureNotifyMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask; + Colormap colormap = XCreateColormap(g_display, root_window_id, vinfo->visual, AllocNone); + + XSetWindowAttributes swa; + memset(&swa, 0, sizeof(swa)); + swa.background_pixmap = None; + swa.border_pixel = 0; + swa.event_mask = swa_event_mask; + swa.colormap = colormap; + + Window window_id = XCreateWindow(g_display, root_window_id, 0, 0, width, height, 0, depth, InputOutput, vinfo->visual, swa_mask, &swa); + XStoreName(g_display, window_id, name); + XMapWindow(g_display, window_id); + + + // I support the WM_DELETE_WINDOW protocol @Leak? + Atom wm_delete_window = XInternAtom(g_display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(g_display, window_id, &wm_delete_window, 1); + + + // glx context + Bool direct = True; + GLXContext glx_context = glXCreateContext(g_display, vinfo, 0, direct); + if (glx_context == 0) + { + printf("glXCreateContext failed\n"); + return 0; + } + Bool made_current = glXMakeCurrent(g_display, window_id, glx_context); + if (made_current == False) + { + printf("glXMakeCurrent failed\n"); + return 0; + } + + + OSWindow *window = malloc(sizeof(OSWindow)); + window->xid = window_id; + window->wm_delete_window = wm_delete_window; + window->event_mask = swa_event_mask; + window->glx_context = glx_context; + XSync(g_display, False); + + + glEnable(GL_TEXTURE_2D); + + glActiveTexture(GL_TEXTURE0); + glGenTextures(1, &window->gl_texture_id); + glBindTexture(GL_TEXTURE_2D, window->gl_texture_id); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + + return window; +} + |