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;
+};