From a66516e34ed1037cdc38fce8bd957fd77152c39f Mon Sep 17 00:00:00 2001
From: dec05eba <dec05eba@protonmail.com>
Date: Wed, 30 Mar 2022 18:09:17 +0200
Subject: wip

---
 src/main.cpp         |  55 ++++++++++++++-
 src/window_texture.c | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 238 insertions(+), 2 deletions(-)
 create mode 100644 src/window_texture.c

(limited to 'src')

diff --git a/src/main.cpp b/src/main.cpp
index 537011c..fb1f8ec 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,4 +1,5 @@
 #include "../include/gui/Button.hpp"
+#include "../include/window_texture.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -45,6 +46,20 @@ static bool string_to_i64(const char *str, int64_t *result) {
     return true;
 }
 
+static void window_texture_get_size_or(WindowTexture *window_texture, int *width, int *height, int fallback_width, int fallback_height) {
+    Window root_window;
+    int x, y;
+    unsigned int w = 0, h = 0;
+    unsigned int border_width, depth;
+    if(!XGetGeometry(window_texture->display, window_texture->pixmap, &root_window, &x, &y, &w, &h, &border_width, &depth) || w == 0 || h == 0) {
+        *width = fallback_width;
+        *height = fallback_height;
+    } else {
+        *width = w;
+        *height = h;
+    }
+}
+
 int main(int argc, char **argv) {
     if(argc != 2)
         usage();
@@ -65,7 +80,7 @@ int main(int argc, char **argv) {
         return 1;
     }
 
-    XSelectInput(display, target_window, StructureNotifyMask);
+    XSelectInput(display, target_window, VisibilityChangeMask | StructureNotifyMask);
 
     mgl::vec2i target_window_size = { target_win_attr.width, target_win_attr.height };
 
@@ -199,7 +214,8 @@ int main(int argc, char **argv) {
 
         const mgl::vec2i settings_button_size(128, 128);
         shapes[main_buttons.size()] = {
-            (short)(main_buttons_start_pos.x + overlay_desired_size.x), (short)(main_buttons_start_pos.y - settings_button_size.y), (unsigned short)settings_button_size.x, (unsigned short)settings_button_size.y
+            (short)(main_buttons_start_pos.x + overlay_desired_size.x), (short)(main_buttons_start_pos.y - settings_button_size.y),
+            (unsigned short)settings_button_size.x, (unsigned short)settings_button_size.y
         };
 
         if(shape_pixmap) {
@@ -229,6 +245,27 @@ int main(int argc, char **argv) {
     };
 
     update_overlay_shape();
+
+    WindowTexture target_window_texture;
+    window_texture_init(&target_window_texture, display, target_window);
+
+    int target_window_texture_width = 0;
+    int target_window_texture_height = 0;
+    window_texture_get_size_or(&target_window_texture, &target_window_texture_width, &target_window_texture_height, target_window_size.x, target_window_size.y);
+
+    mgl_texture window_texture_ref = {
+        window_texture_get_opengl_texture_id(&target_window_texture),
+        target_window_texture_width,
+        target_window_texture_height,
+        MGL_TEXTURE_FORMAT_RGB,
+        32768,
+        32768,
+        true
+    };
+
+    mgl::Texture window_texture = mgl::Texture::reference(window_texture_ref);
+    mgl::Sprite window_texture_sprite(&window_texture);
+
     window.set_visible(true);
 
     Cursor default_cursor = XCreateFontCursor(display, XC_arrow);
@@ -241,12 +278,26 @@ int main(int argc, char **argv) {
     mgl::Event event;
 
     while(window.is_open()) {
+        if(XCheckTypedWindowEvent(display, target_window, VisibilityNotify, &xev)) {
+            if(xev.xvisibility.state) {
+                window_texture_on_resize(&target_window_texture);
+                window_texture_ref.id = window_texture_get_opengl_texture_id(&target_window_texture);
+                window_texture_get_size_or(&target_window_texture, &window_texture_ref.width, &window_texture_ref.height, target_window_size.x, target_window_size.y);
+                window_texture = mgl::Texture::reference(window_texture_ref);
+            }
+        }
+
         if(XCheckTypedWindowEvent(display, target_window, ConfigureNotify, &xev) && (xev.xconfigure.width != target_window_size.x || xev.xconfigure.height != target_window_size.y)) {
             while(XCheckTypedWindowEvent(display, target_window, ConfigureNotify, &xev)) {}
             target_window_size.x = xev.xconfigure.width;
             target_window_size.y = xev.xconfigure.height;
             window.set_size(target_window_size);
             update_overlay_shape();
+
+            window_texture_on_resize(&target_window_texture);
+            window_texture_ref.id = window_texture_get_opengl_texture_id(&target_window_texture);
+            window_texture_get_size_or(&target_window_texture, &window_texture_ref.width, &window_texture_ref.height, target_window_size.x, target_window_size.y);
+            window_texture = mgl::Texture::reference(window_texture_ref);
         }
 
         if(window.poll_event(event)) {
diff --git a/src/window_texture.c b/src/window_texture.c
new file mode 100644
index 0000000..bf687c6
--- /dev/null
+++ b/src/window_texture.c
@@ -0,0 +1,185 @@
+#include "../include/window_texture.h"
+#include <X11/extensions/Xcomposite.h>
+#include <mgl/mgl.h>
+#include <stdio.h>
+
+static int x11_supports_composite_named_window_pixmap(Display *display) {
+    int extension_major;
+    int extension_minor;
+    if(!XCompositeQueryExtension(display, &extension_major, &extension_minor))
+        return 0;
+
+    int major_version;
+    int minor_version;
+    return XCompositeQueryVersion(display, &major_version, &minor_version) && (major_version > 0 || minor_version >= 2);
+}
+
+int window_texture_init(WindowTexture *window_texture, Display *display, Window window) {
+    window_texture->display = display;
+    window_texture->window = window;
+    window_texture->pixmap = None;
+    window_texture->glx_pixmap = None;
+    window_texture->texture_id = 0;
+    window_texture->redirected = 0;
+    
+    if(!x11_supports_composite_named_window_pixmap(display))
+        return 1;
+
+    XCompositeRedirectWindow(display, window, CompositeRedirectAutomatic);
+    window_texture->redirected = 1;
+    return window_texture_on_resize(window_texture);
+}
+
+static void window_texture_cleanup(WindowTexture *self, int delete_texture) {
+    mgl_context *context = mgl_get_context();
+
+    if(delete_texture && self->texture_id) {
+        context->gl.glDeleteTextures(1, &self->texture_id);
+        self->texture_id = 0;
+    }
+
+    if(self->glx_pixmap) {
+        glXDestroyPixmap(self->display, self->glx_pixmap);
+        glXReleaseTexImageEXT(self->display, self->glx_pixmap, GLX_FRONT_EXT);
+        self->glx_pixmap = None;
+    }
+
+    if(self->pixmap) {
+        XFreePixmap(self->display, self->pixmap);
+        self->pixmap = None;
+    }
+}
+
+void window_texture_deinit(WindowTexture *self) {
+    if(self->redirected) {
+        XCompositeUnredirectWindow(self->display, self->window, CompositeRedirectAutomatic);
+        self->redirected = 0;
+    }
+    window_texture_cleanup(self, 1);
+}
+
+int window_texture_on_resize(WindowTexture *self) {
+    mgl_context *context = mgl_get_context();
+    window_texture_cleanup(self, 0);
+
+    int result = 0;
+    GLXFBConfig *configs = NULL;
+    Pixmap pixmap = None;
+    GLXPixmap glx_pixmap = None;
+    GLuint texture_id = 0;
+    int glx_pixmap_bound = 0;
+
+    const int pixmap_config[] = {
+        GLX_BIND_TO_TEXTURE_RGB_EXT, True,
+        GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT | GLX_WINDOW_BIT,
+        GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
+        /*GLX_BIND_TO_MIPMAP_TEXTURE_EXT, True,*/
+        GLX_BUFFER_SIZE, 24,
+        GLX_RED_SIZE, 8,
+        GLX_GREEN_SIZE, 8,
+        GLX_BLUE_SIZE, 8,
+        GLX_ALPHA_SIZE, 0,
+        None
+    };
+
+    const int pixmap_attribs[] = {
+        GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
+        GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGB_EXT,
+        /*GLX_MIPMAP_TEXTURE_EXT, True,*/
+        None
+    };
+
+    XWindowAttributes attr;
+    if (!XGetWindowAttributes(self->display, self->window, &attr)) {
+        fprintf(stderr, "Failed to get window attributes\n");
+        return 1;
+    }
+
+    GLXFBConfig config;
+    int c;
+    configs = glXChooseFBConfig(self->display, 0, pixmap_config, &c);
+    if(!configs) {
+        fprintf(stderr, "Failed to choose fb config\n");
+        return 1;
+    }
+
+    int found = 0;
+    for (int i = 0; i < c; i++) {
+        config = configs[i];
+        XVisualInfo *visual = glXGetVisualFromFBConfig(self->display, config);
+        if (!visual)
+            continue;
+
+        if (attr.depth != visual->depth) {
+            XFree(visual);
+            continue;
+        }
+        XFree(visual);
+        found = 1;
+        break;
+    }
+
+    if(!found) {
+        fprintf(stderr, "No matching fb config found\n");
+        result = 1;
+        goto cleanup;
+    }
+
+    pixmap = XCompositeNameWindowPixmap(self->display, self->window);
+    if(!pixmap) {
+        result = 2;
+        goto cleanup;
+    }
+
+    glx_pixmap = glXCreatePixmap(self->display, config, pixmap, pixmap_attribs);
+    if(!glx_pixmap) {
+        result = 3;
+        goto cleanup;
+    }
+
+    if(self->texture_id == 0) {
+        context->gl.glGenTextures(1, &texture_id);
+        if(texture_id == 0) {
+            result = 4;
+            goto cleanup;
+        }
+        context->gl.glBindTexture(GL_TEXTURE_2D, texture_id);
+    } else {
+        context->gl.glBindTexture(GL_TEXTURE_2D, self->texture_id);
+    }
+
+    glXBindTexImageEXT(self->display, glx_pixmap, GLX_FRONT_EXT, NULL);
+    glx_pixmap_bound = 1;
+
+    context->gl.glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
+    context->gl.glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
+    context->gl.glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+    context->gl.glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+ 
+    /*
+    float fLargest = 0.0f;
+    glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest);
+    */
+
+    context->gl.glBindTexture(GL_TEXTURE_2D, 0);
+
+    XFree(configs);
+    self->pixmap = pixmap;
+    self->glx_pixmap = glx_pixmap;
+    if(texture_id != 0)
+        self->texture_id = texture_id;
+    return 0;
+
+    cleanup:
+    if(texture_id != 0)     context->gl.glDeleteTextures(1, &texture_id);
+    if(glx_pixmap)          glXDestroyPixmap(self->display, glx_pixmap);
+    if(glx_pixmap_bound)    glXReleaseTexImageEXT(self->display, glx_pixmap, GLX_FRONT_EXT);
+    if(pixmap)              XFreePixmap(self->display, pixmap);
+    if(configs)             XFree(configs);
+    return result;
+}
+
+GLuint window_texture_get_opengl_texture_id(WindowTexture *self) {
+    return self->texture_id;
+}
-- 
cgit v1.2.3-70-g09d2