Gtk opengl widget drawing result broken when maximize/restore window

I’m using gtk + egl to draw something, the gl area is constructed by custom widget type and child gdk window and EGL context(i want to add a menubar also, so gl area can only be a child window).

it works fine when i open the window and resize it, but the screen turns broken/black when i maximize/restore the window.

here is my testing code:

#include <EGL/egl.h>
#include <GL/gl.h>
#include <gdk/gdkx.h>
#include <gtk/gtk.h>

struct _GlView {
  GtkWidget parent_instance;
};

struct _GlViewClass {
  GtkWidgetClass parent_class;
};

typedef struct _GlViewClass GlViewClass;
typedef struct _GlView GlView;

G_DEFINE_TYPE(GlView, gl_view, GTK_TYPE_WIDGET)

static void gl_view_init(GlView *widget) {}

static void gl_view_size_allocate(GtkWidget *widget,
                                  GtkAllocation *allocation) {
  gtk_widget_set_allocation(widget, allocation);
}
static void gl_view_class_init(GlViewClass *klass) {
  GTK_WIDGET_CLASS(klass)->size_allocate = gl_view_size_allocate;
}

GtkWidget *gl_view_new() {
  return (GtkWidget *)g_object_new(gl_view_get_type(), NULL);
}

static EGLDisplay *egl_display;
static EGLSurface *egl_surface;
static EGLContext *egl_context;

static void realize_cb(GtkWidget *view) {
  GtkAllocation allocation;
  gtk_widget_get_allocation(view, &allocation);
  GdkWindowAttr window_attributes;
  window_attributes.window_type = GDK_WINDOW_CHILD;
  window_attributes.x = allocation.x;
  window_attributes.y = allocation.y;
  window_attributes.width = allocation.width;
  window_attributes.height = allocation.height;
  window_attributes.wclass = GDK_INPUT_OUTPUT;
  window_attributes.event_mask =
      gtk_widget_get_events(view) | GDK_EXPOSURE_MASK |
      GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK |
      GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK |
      GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK;
  gint window_attributes_mask = GDK_WA_X | GDK_WA_Y;

  GdkWindow *viewWindow =
      gdk_window_new(gtk_widget_get_parent_window(view), &window_attributes,
                     window_attributes_mask);
  gtk_widget_register_window(view, viewWindow);
  gtk_widget_set_window(view, viewWindow);
  gdk_window_show(viewWindow);

  EGLConfig egl_config;
  EGLint n_config;
  EGLint attributes[] = {EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE};

  egl_display =
      eglGetDisplay((EGLNativeDisplayType)gdk_x11_display_get_xdisplay(
          gtk_widget_get_display(view)));
  eglInitialize(egl_display, NULL, NULL);
  eglChooseConfig(egl_display, attributes, &egl_config, 1, &n_config);
  eglBindAPI(EGL_OPENGL_API);
  egl_surface = eglCreateWindowSurface(
      egl_display, egl_config, gdk_x11_window_get_xid(viewWindow), NULL);
  egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, NULL);
}
static void size_allocate_cb(GtkWidget *widget, GdkRectangle *rect,
                             void *userdata) {
  gdk_window_move_resize(gtk_widget_get_window(widget), rect->x, rect->y,
                         rect->width, rect->height);
}
static gboolean draw_cb(GtkWidget *widget, void *cr, gpointer userdata) {
  eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);

  glViewport(0, 0, gtk_widget_get_allocated_width(widget),
             gtk_widget_get_allocated_height(widget));

  glClearColor(0, 0, 0, 1);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0, 100, 0, 100, 0, 1);

  glBegin(GL_TRIANGLES);
  glColor3f(1, 0, 0);
  glVertex2f(50, 10);
  glColor3f(0, 1, 0);
  glVertex2f(90, 90);
  glColor3f(0, 0, 1);
  glVertex2f(10, 90);
  glEnd();

  eglSwapBuffers(egl_display, egl_surface);

  return TRUE;
}

int main(int argc, char **argv) {
  GtkWidget *w;
  GtkWidget *view;

  gtk_init(&argc, &argv);

  w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  view = gl_view_new();
  gtk_container_add(GTK_CONTAINER(w), view);
  gtk_widget_set_double_buffered(view, FALSE);
  gtk_widget_set_has_window(view, FALSE);
  g_signal_connect(G_OBJECT(view), "realize", G_CALLBACK(realize_cb), NULL);
  g_signal_connect(G_OBJECT(view), "size-allocate",
                   G_CALLBACK(size_allocate_cb), NULL);
  g_signal_connect(G_OBJECT(view), "draw", G_CALLBACK(draw_cb), NULL);

  gtk_widget_show_all(w);

  gtk_main();

  return 0;
}

compile:

cc `pkg-config --cflags --libs gtk+-3.0 egl gl` -Wno-deprecated-declarations  test.c -o test

No, don’t do this. Using a native window and attaching a GL context to it worked in GTK2, but it can’t really work with GTK3, and it will lead to broken behaviour.

Please, use GtkGLArea. If you can only use EGL-X11, you can patch GTK3 with the commits in this branch.

thank you, @ebassi.
the code is mostly copied from flutter’s linux shell based on gtk: https://github.com/flutter/engine/tree/master/shell/platform/linux.

1 Like

I have no idea why Flutter thinks it can create native surfaces and hijack the rendering pipeline of GTK; in any case, it will work for a very specific subset of cases, as it’s essentially fighting against the toolkit.

If you want to render something using OpenGL inside a GTK application, use GtkGLArea. GTK does not support any other mechanism.

A kinda doubt based question, does Flutter use GTK for its apps in Linux ?

you can refer to these links: https://github.com/flutter/engine/tree/master/shell/platform/linux and https://medium.com/flutter/announcing-flutter-linux-alpha-with-canonical-19eb824590a9

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.