4

If I create a normal GtkWindow with nothing inside or a GtkWindow with a GtkDrawingArea inside (or that inside a GtkScrolledWindow), I get a window with the theme's background, as in this picture.

But if I use a GtkLayout, I get a window with a solid color background, as in this picture.

The only resource anyone (myself included) could find so far was this other Stack Overflow question, but when I do what it says to do, I get a fully black background, like in this picture, even if I get a cairo context for the GtkLayout's bin window.

So how exactly do I get the GtkLayout to be transparent, or if that's not an option, to use the theme's background?

Though I am running with GTK+ 3.10, I need to target GTK+ 3.4, so gtk_widget_set_opacity() won't work. The theme shown in my screenshots is the gtk-oxygen theme; I am using KDE.

Sample program below. It has options to cover all the cases I described above. I also tried variations of the various cairo calls in draw() (commenting some out, adding another alpha color selection after setting CLEAR, not setting a clip rect, etc.); that didn't work either.

Thanks!

/* pietro gagliardi - 7-8 april 2014 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define GDK_VERSION_MIN_REQUIRED GDK_VERSION_3_4
#define GDK_VERSION_MAX_ALLOWED GDK_VERSION_3_4
#include <gtk/gtk.h>

gboolean windowonly = FALSE;
gboolean drawingarea = FALSE;
gboolean viewport = FALSE;
gboolean drawsig = FALSE;
gboolean binwin = FALSE;

void parseargs(int argc, char *argv[])
{
    if (argc != 2 && argc != 3)
        goto usage;

#define extra(str) (argc == 3 && strcmp(argv[2], str) == 0)
#define noextra(cond) if (!(cond) && argc == 3) goto usage;
    if (strcmp(argv[1], "windowOnly") == 0) {
        noextra(FALSE);
        windowonly = TRUE;
        return;
    } else if (strcmp(argv[1], "drawingArea") == 0) {
        drawingarea = TRUE;
        viewport = extra("viewport");
        noextra(viewport);
        return;
    } else if (strcmp(argv[1], "layout") == 0) {
        binwin = extra("drawbin");
        drawsig = binwin || extra("draw");
        noextra(drawsig);
        return;
    }

usage:
    fprintf(stderr, "usage:\n");
    fprintf(stderr, "\t%s windowOnly\n", argv[0]);
    fprintf(stderr, "\t%s drawingArea [viewport]\n", argv[0]);
    fprintf(stderr, "\t%s layout [draw|drawbin]\n", argv[0]);
    exit(1);
}

gboolean draw(GtkWidget *widget, cairo_t *cr, gpointer data)
{
    double x, y, w, h;

    if (binwin)
        cr = gdk_cairo_create(gtk_layout_get_bin_window((GtkLayout *) widget));

    cairo_clip_extents(cr, &x, &y, &w, &h);
    cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
    cairo_rectangle(cr, x, y, w, h);
    cairo_fill(cr);
    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
    cairo_set_source_rgba(cr, 0, 0, 0, 0);
    cairo_rectangle(cr, x, y, w, h);
    cairo_fill(cr);

    if (binwin)
        cairo_destroy(cr);

    return FALSE;
}

int main(int argc, char *argv[])
{
    GtkWidget *w;
    GtkWidget *q;

    parseargs(argc, argv);
    gtk_init(NULL, NULL);

    w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    g_signal_connect(w, "destroy", G_CALLBACK(gtk_main_quit), NULL);

    if (!windowonly) {
        if (drawingarea) {
            q = gtk_drawing_area_new();
            if (viewport) {
                GtkWidget *sw;

                sw = gtk_scrolled_window_new(NULL, NULL);
                gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), q);
                q = sw;
            }
        } else {
            q = gtk_layout_new(NULL, NULL);
            if (drawsig)
                g_signal_connect(q, "draw", G_CALLBACK(draw), NULL);
        }
        gtk_container_add(GTK_CONTAINER(w), q);
    }

    gtk_widget_show_all(w);

    gtk_main();
    return 0;
}
Community
  • 1
  • 1
andlabs
  • 11,290
  • 1
  • 31
  • 52

2 Answers2

3

Add a GtkCssProvider to your window with one or both of

GtkLayout {
  background-color: transparent;
}

GtkViewport {
  background-color: transparent;
}

loaded in it. You may also be able to do this with gtk_widget_override_background_color().

ptomato
  • 56,175
  • 13
  • 112
  • 165
  • The CSS override (using only the `GtkLayout` one) did the trick (`gtk_widget_override_background_color()` did not). Thanks! I'll probably file a feature request in the KDE bug tracker to have that CSS added to oxygen-gtk itself as well (if that wasn't already taken into consideration before). – andlabs Apr 09 '14 at 06:07
  • (I'm from gtk#) `OverrideBackgroundColor` worked for me. Make sure you set `window.Visual = window.Screen.RgbaVisual` – Carson Ip Feb 07 '15 at 13:33
1

They key is to set a proper RGBA visual for the window

w = //some GtkWidget, I used the GtkWindow to test
gtk_widget_set_app_paintable (w, TRUE); // important or you will get solid color
GdkScreen *screen = gtk_widget_get_screen (w);
GdkVisual *visual = gdk_screen_get_rgba_visual (screen);
gtk_widget_set_visual(w, visual);
gtk_widget_show_all(w);

Note: Also do

g_signal_connect(G_OBJECT(w), "screen-changed", G_CALLBACK(screen_changed_contaniing_above_code), NULL);

or weird things might happen on certain multi screen setups (could not reproduce with my local), but well.. just do it, you can't know how Xinerama or other X extensions react when dragging the window between screens...

See this SO question which has a complete compileable example.


Also I'd recommend to draw only once using CAIRO_OPERATOR_SOURCE.


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define GDK_VERSION_MIN_REQUIRED GDK_VERSION_3_4
#define GDK_VERSION_MAX_ALLOWED GDK_VERSION_3_4
#include <gtk/gtk.h>


gboolean draw(GtkWidget *widget, cairo_t *cr, gpointer data)
{
    double x, y, w, h;
    cairo_clip_extents(cr, &x, &y, &w, &h);
    cairo_set_source_rgba (cr, 1., 0., 0., 0.25); //translucent red
    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
    cairo_rectangle(cr, x, y, w, h);
    cairo_fill(cr);

    return FALSE;
}


void fix_visual(GtkWidget *w)
{
    GdkScreen *screen = gtk_widget_get_screen (w);
    GdkVisual *visual = gdk_screen_get_rgba_visual (screen);
    gtk_widget_set_visual(w, visual);
    //FIXME cleanup maybe
}


void screen_changed (GtkWidget *widget, GdkScreen *screen, gpointer user_data)
{
    fix_visual (widget);
}

int main(int argc, char *argv[])
{



    GtkWidget *w;
    GtkWidget *q;

    gtk_init(&argc, &argv);

    w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    g_signal_connect(w, "destroy", G_CALLBACK(gtk_main_quit), NULL);


    q = gtk_layout_new(NULL, NULL);

    g_signal_connect(w, "screen-changed", G_CALLBACK(screen_changed), NULL);
    g_signal_connect(q, "draw", G_CALLBACK(draw), NULL);

    gtk_container_add(GTK_CONTAINER(w), q);

    gtk_widget_set_app_paintable (w, TRUE);

    fix_visual (w);

    gtk_widget_show_all(w);


    gtk_main();
    return 0;
}

red with 0.5 translucent

Community
  • 1
  • 1
drahnr
  • 6,782
  • 5
  • 48
  • 75
  • Unfortunately, this did not work: the layout background is still black. I also tried in both an XFCE session (native) and a GNOME 3/GNOME Shell session (in Xephyr). **HOWEVER** both with and without your code, I decided to try setting the cairo color to (0,0.5,0,0.5), and I got a green color, so hm... – andlabs Apr 08 '14 at 23:10
  • I tried this and it definitly _does_ work on this machine with Cinnamon 2.0.14 (though it should not really matter). Can you try C code from the answer of that linked question? I am a bit puzzled why this should not work... – drahnr Apr 08 '14 at 23:11
  • That example worked, both in KDE and in the GNOME/Xephyr session. – andlabs Apr 08 '14 at 23:14
  • Updated answer, `gtk_widget_set_app_paintable` is required too. Forgot that I added that too. – drahnr Apr 08 '14 at 23:15
  • 1
    It was still failing, so I tried to jiggle a few things around to reproduce the translucent window... then I figured out why the translucency wasn't happening: if I call `gtk_widget_show_all()` before setting the `GdkVisual`, everything's opaque, even though there's another `gtk_window_show_all()` afterwards... Will try adopting the layout again now. – andlabs Apr 08 '14 at 23:32
  • That is not necessary for me (Cinnamon 2.0.14), one `gtk_widget_show_all` is sufficient. – drahnr Apr 08 '14 at 23:32
  • Hm.... this is weird. Also nothing seems to be making the GtkLayout respect alpha... – andlabs Apr 08 '14 at 23:58
  • I got your code above working in my code now, so I now have a translucent window. But I know what happened: you seem to think I want the window to be translucent to other windows; I want the GtkLayout to be transparent to its background *and expose the default window background* instead, as in the first screenshot. Thanks in the meantime, though, and sorry for the trouble this caused. – andlabs Apr 09 '14 at 01:15
  • The first screenshot was rather confusing... anyways. – drahnr Apr 09 '14 at 06:54