Dali Clock 2.46 released

Portability and installation fixes for X11.

It probably still doesn't work on some systems running GTK earlier than 3.22 and I could use some help with that.

I'm putting bits on the screen using OpenGL (because typically that is the fastest way to do so), and the code is written for OpenGL 1.3, AKA "Real GL" (because that's what I had already, and because every Linux system supports it and will until the end of time).

However, apparently GtkGLArea has other notions, and on some systems, creates a GL context that only works with... OpenGLES 2.0? OpenGL 4.3? Who can tell. If I set the "use_es" flag on the GtkGLArea, things work on those systems, but that flag was only implemented in GTK 3.22.

So, two requests:

  1. If you have a pre-3.22 system, and it is not working, and you can figure out how to make it work, please let me know. Possibly this involves the "create_context" callback in window.c, but I dunno.

  2. If you would care to rewrite my dead simple GL code that just blasts a texture onto a quad in such a way that those failing systems are happy with it, please let me know what you did. I'm guessing this means using GLSL, but since I don't know what version of the OpenGL spec is being targeted by GtkGLArea, I'm not sure.

The failing systems are absolutely capable of running OpenGL 1.3 code, because they are all capable of running XScreenSaver and that's what it uses, so GtkGLArea's requirements are baffling, particularly that they shift like the sand depending on.... distro? Video card? Who can tell.

Oh yeah, some other stuff:

  1. Is there a way to tell GtkWindow "draw on this X11 Window ID instead of creating your own"? Asking for a friend who is a screen saver.

  2. Is there a way to tell the compositor to respect the alpha channel of the OpenGL frame buffer, so that (as in the Mac version) the clock can have a translucent background while the foreground digits are fully opaque?

By the way, here's a rundown on the various versions of OpenGL, ask me how I know:

OpenGL 1.0 1992: Standardized version of SGI's "GL"
OpenGL 1.1 1997: Improved texture support
OpenGL 1.2 1998: Nothing interesting
OpenGL 1.3 2001: Multisampling, cubemaps
OpenGL 1.4 2002: Added auto-mipmapping
OpenGLES 1.0 2003: Deprecated 80% of the language; fork of OpenGL 1.3
OpenGL 1.5 2003: Added VBOs
OpenGLES 1.1 2004: Fork of OpenGL 1.5
OpenGL 2.0 2004: A political quagmire, added shader language GLSL 1.1
OpenGLES 2.0 2007: Deprecated 95% of the language; fork of OpenGL 2.0; GLSL 1.20
OpenGL 3.0 2008: Added FBOs, VAOs, deprecated 60% of the language
OpenGL 3.3 2010: OpenGL 3.3 and OpenGL 4.0 released concurrently; 3.3 has GLSL 3.30, but 4.0 has GLSL 4.00
OpenGLES 3.0 2012: Same as WebGL 2.0, but has GLSL 3.00
OpenGL 4.3 2012: Superset of GLES 3.0, but has GLSL 4.30
Anything later: My ignorance is blissful.

Previously, previously, previously.

Tags: , , , , , ,

14 Responses:

  1. chipaca says:

    It works, huzzah. On an Ubuntu 20.04 with intel and/or nvidia graphics (via nvidia prime). GTK is 3.24 so I can't shed light on that side of things though.
    Thank you.

  2. It just works™

    F36
    nVIDIA 515.65.01

    🍾 🥂

  3. Bill Paul says:

    Success for me.

    OS:

    FreeBSD core 13.1-RELEASE FreeBSD 13.1-RELEASE releng/13.1-n250148-fc952ac2212 GENERIC amd64

    Graphics:

    GL_VERSION: 3.1 Mesa 21.3.8
    GL_RENDERER: AMD CAICOS (DRM 2.50.0 / 13.1-RELEASE, LLVM 13.0.1)
    GL_VENDOR: X.Org
    
    vgapci1@pci0:131:0:0:   class=0x030000 rev=0x00 hdr=0x00 vendor=0x1002 device=0x6771 subvendor=0x103c subdevice=0x90b8
        vendor     = 'Advanced Micro Devices, Inc. [AMD/ATI]'
        device     = 'Caicos XTX [Radeon HD 8490 / R5 235X OEM]'
        class      = display
        subclass   = VGA

    Compiler:

    FreeBSD clang version 13.0.0 (git@github.com:llvm/llvm-project.git llvmorg-13.0.0-0-gd7b669b3a303)
    Target: x86_64-unknown-freebsd13.1
    Thread model: posix
    InstalledDir: /usr/bin

    GTK version:

    Name           : gtk3
    Version        : 3.24.33
    Installed on   : Fri May 20 15:51:59 2022 PDT
    Origin         : x11-toolkits/gtk30
    Architecture   : FreeBSD:13:amd64
    Prefix         : /usr/local
    Categories     : x11-toolkits
    Licenses       : LGPL20
    Maintainer     : desktop@FreeBSD.org
    WWW            : https://www.gtk.org/
    Comment        : Gimp Toolkit for X11 GUI (current stable version)

    Configured with:

    ./configure --prefix=/usr/local/xdaliclock

    Compiled with no warnings (using GNU make; BSD make is famously snooty about syntax). Everything is installed under /usr/local/xdaliclock as expected. The schemas are installed under /usr/local/xdaliclock/share/glib-2.0/schemas, which of course is not searched by default at runtime. To get around this, I set the following environment variable:

    % setenv XDG_DATA_DIRS /usr/local/share:/usr/local/xdaliclock/share

    An alternate solution seems to be to put the xdaliclock-specific files under ~/.local/share/glib-2.0/schemas .

    Once one of those two things is done, it seems to run fine, and is very trippy. I can bring up the configuration menu and everything.

    Sorry, I don't have an easy way to test with older GTK3 versions.

  4. nwnk says:

    Pre-3.22, GtkGLArea never tries to create a GLES context. But, then and since, it does prefer core GL contexts over compatibility. I think (have only read the code, not tried it) you want to do

    gtk_gl_area_set_required_version(priv->glarea, 1, 3);
    instead of set_use_es. Especially since if you do get a GLES context none of your glVertexPointer() stuff is going to work anyway. (This works somewhat by accident since gdk will try to create a core 1.3 context, which is not a thing, and then fall back to the old glXCreateNewContext. But it should work.)

    The way to get the compositor to respect the alpha channel is to create your window on a visual that has an alpha channel, ie one where depth is 32 not 24.

    • jwz says:

      gtk_gl_area_set_required_version(priv->glarea, 1, 3);

      Well, except that the source I've seen for that function says "versions less than 3.2 are not supported". And some users (but not all) get that as an error message.

      The way to get the compositor to respect the alpha channel is to create your window on a visual that has an alpha channel, ie one where depth is 32 not 24.

      Is there any sample GTK code out there that successfully does this? I looked but did not find.

  5. nwnk says:
    1

    Well, except that the source I've seen for that function says "versions less than 3.2 are not supported". And some users (but not all) get that as an error message.

    That's gdk_gl_context_set_required_version, which is not what I said.

    • nwnk says:

      Erk, apologies for the thread break. To the gtk question, I don't have sample code at hand, but gtk4 does away with non-RGBA windows altogether, and the 3-to-4 migration guide suggests the way to prepare for that is:

      gdk_window_set_visual (gdk_screen_get_rgba_visual ())

      after you've created the GtkWindow.

    • jwz says:

      Hmm. Well, on my emulated Debian 11.4 system, doing either gdk_gl_context_set_required_version or gtk_gl_area_set_required_version results in "invalid enum" errors, and gtk_gl_area_set_use_es makes it work.

  6. jwz says:

    For number 3, I have discovered gdk_x11_window_foreign_new_for_display which lets you create a GdkWindow with a pre-existing X11 Window backing it; and gtk_widget_set_window which allegedly allows you to change the GdkWindow that is backing a GtkWidget or GtkWindow. One would hope that you could use this to cause a GtkWindow to render onto a pre-existing X11 window, but I have not been able to make that work.

    • FW says:

      The top gist at https://gist.github.com/mmozeiko/2401933b1fa89e5d5bd238b33eab0465 seems to work on my system. It gives the X11 Window to a GdkWindow with that API call , and then does a gtk_widget_set_window(widget, (GdkWindow*)user) during a "realize" callback, and then does things like XCreateGC() and XDrawRectangle() using the original X11 Window.

      • jwz says:

        Hmm, almost there! That test does work for me, but when I try to translate it to the Gtk3 "GApplication / g_application_run" style, it stops rendering sub-widgets.

      • jwz says:

        Hey, if anyone can figure out how to make this go when you change the #if 0 to 1, lemme know... Without the X stuff, I get a white window with a menubar, a label and a button. With, I get a black on black window, with a menubar that shows up if you click where it should be, and no label or button. So it's like half working?

        /* gcc test2.c `pkg-config --cflags --libs gtk+-3.0 gdk-3.0` -lX11 && ./a.out */
        
        #include <gtk/gtk.h>
        #include <gdk/gdkx.h>
        
        static void
        file_quit (GtkWidget *w, gpointer user)
        {
          exit(0);
        }
        
        static void
        realize (GtkWidget* widget, gpointer user)
        {
          gtk_widget_set_window (widget, GDK_WINDOW(user));
        }
        
        static void
        clicked (GtkButton *btn, gpointer data)
        {
          gtk_button_set_label (btn, "Fuck");
        }
        
        static void
        activate (GApplication *app, gpointer data)
        {
          GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));
        
        #if 0
          GdkDisplay *gdpy = gdk_display_get_default();
          Display *xdpy = GDK_DISPLAY_XDISPLAY(gdpy);
          Window xwin = XCreateSimpleWindow (xdpy, DefaultRootWindow(xdpy),
                                             0, 0, 300, 300, 0, 0, 0);
          XMapRaised (xdpy, xwin);
          GdkWindow *gwin = gdk_x11_window_foreign_new_for_display (gdpy, xwin);
          g_signal_connect (win, "realize", G_CALLBACK(realize), gwin);
          gtk_widget_set_has_window (GTK_WIDGET (win), TRUE);
        #endif
        
          GtkWidget *menubar = gtk_menu_bar_new();
          GtkWidget *file = gtk_menu_item_new_with_label("File");
          GtkWidget *filemenu = gtk_menu_new();
          GtkWidget *quit = gtk_menu_item_new_with_label("Quit");
        
          GtkWidget *label = gtk_label_new("Hello this is label");
          GtkWidget *btn = gtk_button_new_with_label ("Shit");
          g_signal_connect (btn, "clicked", G_CALLBACK (clicked), NULL);
        
          gtk_menu_shell_append (GTK_MENU_SHELL(filemenu), quit);
          gtk_menu_item_set_submenu (GTK_MENU_ITEM(file), filemenu);
          gtk_menu_shell_append (GTK_MENU_SHELL(menubar), file);
        
          GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
          gtk_box_pack_start (GTK_BOX(box), menubar, FALSE, FALSE, 3);
          gtk_box_pack_start (GTK_BOX(box), label, FALSE, FALSE, 3);
          gtk_box_pack_start (GTK_BOX(box), btn, FALSE, FALSE, 3);
        
          gtk_container_add(GTK_CONTAINER(win), box);
        
          g_signal_connect (G_OBJECT(quit), "activate", G_CALLBACK(file_quit), NULL);
        
          gtk_widget_show_all (GTK_WIDGET (win));
        }
        
        int
        main (int argc, char **argv)
        {
          GtkApplication *app = gtk_application_new("org.jwz.test", 0);
          g_signal_connect (app, "activate", G_CALLBACK(activate), NULL);
          return g_application_run (G_APPLICATION (app), argc, argv);
        }
        
        • FW says:

          I have not yet solved the problem, but I will leave my work here in case it helps someone. I decided to see if even "normal" external Gdk windows could be supplied to GtkApplications, and it took some doing but it works. I isolated the changes to make A/B testing a little easier. I plan on poking at differences between the results of the two GtkWindow-providing functions, to see what might be tripping things up. It's also interesting that the make_gdk_window() version doesn't produce any Glib warnings, but the make_x11_window() version produces several ugly-looking ones that have resisted my efforts at interpretation.

          #include <stdlib.h>
          #include <gtk/gtk.h>
          #include <gdk/gdkx.h>
          
          static void
          file_quit (GtkWidget *w, gpointer user)
          {
            exit(0);
          }
          
          static void
          clicked (GtkButton *btn, gpointer data)
          {
            gtk_button_set_label (btn, "Fuck");
          }
          
          static void
          realize (GtkWidget* widget, gpointer user)
          {
              if (GDK_WINDOW(user) != NULL) {
                  gtk_widget_set_window(widget, GDK_WINDOW(user));
              }
          }
          
          static GdkWindow *
          make_gdk_window(void)
          {
              GdkWindowAttr attributes;
              attributes.x = 0;
              attributes.y = 0;
              attributes.width = 300;
              attributes.height = 300;
              attributes.window_type = GDK_WINDOW_TOPLEVEL;
              attributes.wclass      = GDK_INPUT_OUTPUT;
          
              guint attrs_mask;
              attrs_mask = GDK_WA_X | GDK_WA_Y;
          
              return gdk_window_new(NULL, &attributes, attrs_mask);
          }
          
          static GdkWindow *
          make_x11_window(void)
          {
              GdkDisplay *gdpy = gdk_display_get_default();
              Display *xdpy = GDK_DISPLAY_XDISPLAY(gdpy);
              Window xwin = XCreateSimpleWindow (xdpy, DefaultRootWindow(xdpy),
                      0, 0, 300, 300, 0, 0, 0);
              XMapRaised (xdpy, xwin);
              GdkWindow *gwin = gdk_x11_window_foreign_new_for_display (gdpy, xwin);
              return gwin;
          }
          
          static GtkWidget *
          make_window( GtkApplication *application )
          {
          #if 1
              GdkWindow * gwin = make_x11_window();
          #else
              GdkWindow * gwin = make_gdk_window();
          #endif
              GtkWidget * win;
              win = gtk_application_window_new( application );
              gtk_widget_set_has_window(win, TRUE);
          
              // This was a key API call to get make_gdk_window() to work
              gdk_window_set_user_data(gwin, win);
          
              g_signal_connect(win, "realize", G_CALLBACK(realize), gwin);
              return win;
          }
          
          static void
          populate_window( GtkWidget * widget )
          {
              GtkWidget *filemenu = gtk_menu_new();
              GtkWidget *quit = gtk_menu_item_new_with_label("Quit");
              g_signal_connect (G_OBJECT(quit), "activate", G_CALLBACK(file_quit), NULL);
              gtk_menu_shell_append (GTK_MENU_SHELL(filemenu), quit);
          
              GtkWidget *file = gtk_menu_item_new_with_label("File");
              gtk_menu_item_set_submenu (GTK_MENU_ITEM(file), filemenu);
          
              GtkWidget *menubar = gtk_menu_bar_new();
              gtk_menu_shell_append (GTK_MENU_SHELL(menubar), file);
          
              GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
          
              GtkWidget *label = gtk_label_new("Hello this is label");
              GtkWidget *btn = gtk_button_new_with_label ("Shit");
              g_signal_connect (btn, "clicked", G_CALLBACK (clicked), NULL);
          
              gtk_box_pack_start (GTK_BOX(box), menubar, FALSE, FALSE, 3);
              gtk_box_pack_start (GTK_BOX(box), label, FALSE, FALSE, 3);
              gtk_box_pack_start (GTK_BOX(box), btn, FALSE, FALSE, 3);
          
              gtk_container_add(GTK_CONTAINER(widget), box);
          }
          
          static void
          activate ( GtkApplication *application )
          {
              GtkWidget *window;
          
              window = make_window(application);
          
              populate_window(window);
          
              gtk_widget_show_all(GTK_WIDGET(window));
          
              gtk_window_present(GTK_WINDOW(window));
          }
          
          int
          main (int argc, char **argv)
          {
              GtkApplication *app = gtk_application_new("org.jwz.test", 0);
              g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
              return g_application_run(G_APPLICATION(app), argc, argv);
          }
          
          
          • jwz says:

            Yeah, I'm also getting nowhere with this. I've been reading through the GTK3 source and the differences between what make_gdk_window and gdk_x11_window_foreign_new_for_display do are not obvious. Those "frame clock" warnings suggest that the window does not have one of those, but there is no public setter method as far as I can tell.

  • Previously