diff --git a/gtk/src/gtk_wayland_helpers.cpp b/gtk/src/gtk_wayland_helpers.cpp new file mode 100644 index 00000000..aaba6923 --- /dev/null +++ b/gtk/src/gtk_wayland_helpers.cpp @@ -0,0 +1,184 @@ +#include <stdio.h> +#include <string.h> + +#include "gtk_wayland_helpers.h" + +static void wl_global (void *data, + struct wl_registry *wl_registry, + uint32_t name, + const char *interface, + uint32_t version) +{ + struct wlgl_helper *wl = (struct wlgl_helper *) data; + + if (!strcmp (interface, "wl_compositor")) + wl->compositor = (struct wl_compositor *) wl_registry_bind (wl_registry, name, &wl_compositor_interface, 1); + else if (!strcmp (interface, "wl_subcompositor")) + wl->subcompositor = (struct wl_subcompositor *) wl_registry_bind (wl_registry, name, &wl_subcompositor_interface, 1); +} + +static void wl_global_remove (void *data, + struct wl_registry *wl_registry, + uint32_t name) +{ +} + +static const struct wl_registry_listener wl_registry_listener = { + wl_global, + wl_global_remove +}; + +wlgl_helper::wlgl_helper () +{ + display = NULL; + registry = NULL; + compositor = NULL; + subcompositor = NULL; + parent = NULL; + child = NULL; + subsurface = NULL; + egl_display = NULL; + egl_surface = NULL; + egl_context = NULL; + egl_config = NULL; + egl_window = NULL; +} + +wlgl_helper::~wlgl_helper () +{ + if (subsurface) + wl_subsurface_destroy (subsurface); + + if (region) + wl_region_destroy (region); + + if (child) + wl_surface_destroy (child); + + if (egl_context) + eglDestroyContext (egl_display, egl_context); + + if (egl_surface) + eglDestroySurface (egl_display, egl_surface); + + if (egl_window) + wl_egl_window_destroy (egl_window); +} + +bool wlgl_helper::attach (GdkWindow *window) +{ + int x, y, w, h; + + if (!GDK_IS_WAYLAND_WINDOW (window)) + return false; + + gdk_window = window; + gdk_window_get_geometry (gdk_window, &x, &y, &w, &h); + + display = gdk_wayland_display_get_wl_display (gdk_window_get_display (gdk_window)); + parent = gdk_wayland_window_get_wl_surface (gdk_window); + registry = wl_display_get_registry (display); + + wl_registry_add_listener (registry, &wl_registry_listener, this); + wl_display_roundtrip (display); + + if (!compositor || !subcompositor) + return false; + + child = wl_compositor_create_surface (compositor); + region = wl_compositor_create_region (compositor); + subsurface = wl_subcompositor_get_subsurface (subcompositor, child, parent); + + wl_surface_set_input_region (child, region); + wl_subsurface_set_desync (subsurface); + wl_subsurface_set_position (subsurface, x, y); + + return true; +} + +bool wlgl_helper::create_egl_context (int width, int height) +{ + int scale = gdk_window_get_scale_factor (gdk_window); + + EGLint surface_attribs[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_RED_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_NONE + }; + + EGLint context_attribs[] = { + EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT, + EGL_NONE + }; + + EGLint num_configs = 0; + + if (!subsurface) + return false; + + egl_display = eglGetDisplay ((EGLNativeDisplayType) display); + eglInitialize (egl_display, NULL, NULL); + + if (!eglChooseConfig (egl_display, surface_attribs, &egl_config, 1, &num_configs)) + { + printf ("Couldn't find matching config.\n"); + return false; + } + eglBindAPI (EGL_OPENGL_API); + + egl_window = wl_egl_window_create (child, width * scale, height * scale); + if (!egl_window) + { + printf ("Couldn't create window.\n"); + return false; + } + + egl_surface = eglCreateWindowSurface (egl_display, egl_config, (EGLNativeWindowType) egl_window, NULL); + if (!egl_surface) + { + printf ("Couldn't create surface.\n"); + return false; + } + + egl_context = eglCreateContext (egl_display, egl_config, EGL_NO_CONTEXT, context_attribs); + if (!egl_context) + { + printf ("Couldn't create context.\n"); + return false; + } + + return true; +} + +void wlgl_helper::resize (int width, int height) +{ + int x, y, w, h, scale; + + gdk_window_get_geometry (gdk_window, &x, &y, &w, &h); + scale = gdk_window_get_scale_factor (gdk_window); + + wl_egl_window_resize (egl_window, w * scale, h * scale, 0, 0); + wl_subsurface_set_position (subsurface, x, y); + + make_current (); +} + +void wlgl_helper::swap_buffers () +{ + eglSwapBuffers (egl_display, egl_surface); + wl_surface_commit (child); +} + +void wlgl_helper::make_current () +{ + eglMakeCurrent (egl_display, egl_surface, egl_surface, egl_context); +} + +void wlgl_helper::swap_interval (int frames) +{ + eglSwapInterval (egl_display, frames); +} + + diff --git a/gtk/src/gtk_wayland_helpers.h b/gtk/src/gtk_wayland_helpers.h new file mode 100644 index 00000000..84cc30ab --- /dev/null +++ b/gtk/src/gtk_wayland_helpers.h @@ -0,0 +1,35 @@ +#pragma once +#include <gdk/gdkwayland.h> +#include <wayland-egl.h> +#include <epoxy/egl.h> + +struct wlgl_helper +{ + wlgl_helper (); + ~wlgl_helper (); + bool attach (GdkWindow *window); + bool create_egl_context (int width, int height); + void resize (int width, int height); + void swap_buffers (); + void swap_interval (int frames); + void make_current (); + + GdkWindow *gdk_window; + + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_subcompositor *subcompositor; + + struct wl_surface *parent; + struct wl_surface *child; + struct wl_subsurface *subsurface; + struct wl_region *region; + + EGLDisplay egl_display; + EGLSurface egl_surface; + EGLContext egl_context; + EGLConfig egl_config; + + wl_egl_window *egl_window; +};