#29 GtkGLExt hijacks whole window with GTK+ 2.20

v1.0
open
nobody
5
2010-05-05
2010-05-05
Chris Malloy
No

With GTK+ 2.20 GtkGLExt draws outside of it's widget's area and actually takes over the whole window. I do not know what version this happened at but this problem is not present in GTK+ 2.16.

Discussion

  • This broke with gtk 2.18 because of this:

    "GDK has been reworked to implement 'client-side windows'. This offers
    exciting new possibilities, such as transformed, offscreen rendering,
    but it breaks some long-standing assumptions that applications may
    have about GDK windows. Setting the environment variable
    GDK_NATIVE_WINDOWS makes GDK create a native X11 window for each
    GDK window, which might make problematic applications work better."
    (http://www.gtk.org/release-notes.txt)

    Although it may have been "exciting", it broke anything that relied
    on widgets having discrete handles. The recommended workaround is to
    define GDK_NATIVE_WINDOWS=1 in the environment variables. That works
    fine.

    However, there's no easy way to do this programmatically with dynamic
    libraries since GDK_NATIVE_WINDOWS is checked while gtk is
    initializing. What's more, gtk3 is completely removing support for it.
    The solution is to use gdk_window_ensure_native(), which creates a
    handle for the given window (if possible, there are some
    restrictions).

    Now, the interaction between gdk_window_ensure_native() and gtkglext
    is a bit messy. The former needs to be called on a window that has
    been realized while the latter's realized callback immediately creates
    the opengl context with the current native handle.

    The only way I found to make this work is to:

    1) register my realized callback *before* calling
    gtk_widget_set_gl_capability() (since gtk calls the handles in
    order);
    2) call gdk_window_ensure_native() in my callback and return so that
    gtkglext's callback can be executed;
    3) do any initialization the first time expose_event's callback is
    executed.

    You can't use gtkglext in your realized handler, because gtkglext's
    handler hasn't been called yet.

    Working example:

    gint on_init(GtkWidget* w, GdkEventExpose* e)
    {
    gdk_window_ensure_native(w->window);
    return true;
    }

    static bool g_inited = false;
    gint on_draw(GtkWidget* w, GdkEventExpose* e)
    {
    GdkGLContext* context = gtk_widget_get_gl_context(w);
    GdkGLDrawable* drawable = gtk_widget_get_gl_drawable(w);

    if (!g_inited)
    {
    g_inited = true;

    gdk_gl_drawable_gl_begin(drawable, context);

    glViewport(0, 0, w->allocation.width, w->allocation.height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, 100, 100, 0, -1, 1);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    gdk_gl_drawable_gl_end(drawable);
    }

    gdk_gl_drawable_gl_begin(drawable, context);
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    glBegin(GL_TRIANGLES);
    glVertex2f(50.0f, 10.0f);
    glVertex2f(10.0f, 90.0f);
    glVertex2f(90.0f, 90.0f);
    glEnd();

    gdk_gl_drawable_gl_end(drawable);
    gdk_gl_drawable_swap_buffers(drawable);

    return true;
    }

    gint on_reshape(GtkWidget* w, GdkEventConfigure* e)
    {
    GdkGLContext *context = gtk_widget_get_gl_context(w);
    GdkGLDrawable *drawable = gtk_widget_get_gl_drawable(w);

    if (!context || !drawable)
    return true;

    gdk_gl_drawable_gl_begin(drawable, context);
    glViewport(0, 0, w->allocation.width, w->allocation.height);
    gdk_gl_drawable_gl_end(drawable);

    return true;
    }

    int main(int argc, char** argv)
    {
    gtk_init(&argc, &argv);
    gtk_gl_init(&argc, &argv);

    GdkGLConfig* c = gdk_gl_config_new_by_mode(GdkGLConfigMode(
    GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH | GDK_GL_MODE_DOUBLE));

    GtkWidget* w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    GtkWidget* b = gtk_button_new();
    GtkWidget* box = gtk_vbox_new(false, 5);
    GtkWidget* gl = gtk_drawing_area_new();

    gtk_button_set_label(GTK_BUTTON(b), "hello!");

    g_signal_connect(gl, "expose_event", G_CALLBACK(on_draw), nullptr);
    g_signal_connect(gl, "realize", G_CALLBACK(on_init), nullptr);
    g_signal_connect(gl, "configure_event", G_CALLBACK(on_reshape), nullptr);

    gtk_widget_set_gl_capability(
    gl, c, nullptr, true, GDK_GL_RGBA_TYPE);

    gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(b), false, false, 5);
    gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(gl),true, true, 5);
    gtk_container_add(GTK_CONTAINER(w), GTK_WIDGET(box));

    gtk_window_set_default_size(GTK_WINDOW(w), 400, 400);
    g_signal_connect(w, "delete_event", G_CALLBACK(gtk_main_quit), nullptr);
    gtk_widget_show_all(w);

    gtk_main();
    }