How do I make a gtkwindow background transparent on Linux?

CJD picture CJD · May 30, 2013 · Viewed 8.7k times · Source

I would like to make the background transparent, and only the widgets are visible.

Here is my code:

#include <gtk/gtk.h>
int main (int argc, char *argv[])
{
    gtk_init (&argc, &argv);
    GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

    // Title
    gtk_window_set_title(GTK_WINDOW (window), "Transparency");
    //gtk_window_set_opacity(GTK_WINDOW(window), 0.5);

    // CSS
    GtkCssProvider *provider = gtk_css_provider_new();
    GdkDisplay *display = gdk_display_get_default();
    GdkScreen *screen = gdk_display_get_default_screen(display);
    gtk_style_context_add_provider_for_screen(screen, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
    gtk_css_provider_load_from_data(GTK_CSS_PROVIDER (provider),
                                    "GtkWindow {\n"
                                    "   background-color: rgba(0,0,0,0);\n"
                                    "}\n",
                                     -1, NULL);
    g_object_unref (provider);

    // Window
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
    gtk_window_resize(GTK_WINDOW(window), 400, 300);
    gtk_widget_show_all(window);

    gtk_main();
    return 0;
}

I use gtk3. When the program execute, it just shows black. The CSS (or rgba) function does not work. I try to use gtk_window_set_opacity(), but it also just shows black. How do I fix my code?

Answer

AthanasiusOfAlex picture AthanasiusOfAlex · Jan 30, 2014

I followed the link suggested by the comment, but unfortunately it was written for Gtk 2. I have re-worked it for Gtk 3. (I am using Gtk 3.8, but as far as I know it does not use anything deprecated in Gtk 3.10). The program produces a green semi-transparent square with button in it. Of course, you could make the square completely transparent by changing the last argument for the function cairo_set_source_rgba to 0.

Note: I compiled this with the following command (assuming you call the file transparent.c):

gcc -o transparent transparent.c `pkg-config gtk+-3.0 --libs --cflags`

Here is the code:

Version for C

/**
 * Original code by: Mike - http://plan99.net/~mike/blog (now a dead link--unable to find it).
 * Modified by karlphillip for StackExchange:
 *     (https://stackoverflow.com/questions/3908565/how-to-make-gtk-window-background-transparent)
 * Re-worked for Gtk 3 by Louis Melahn, L.C., January 30, 2014.
 */

#include <gtk/gtk.h>

static void screen_changed(GtkWidget *widget, GdkScreen *old_screen, gpointer user_data);
static gboolean draw(GtkWidget *widget, cairo_t *new_cr, gpointer user_data);
static void clicked(GtkWindow *win, GdkEventButton *event, gpointer user_data);

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

    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
    gtk_window_set_default_size(GTK_WINDOW(window), 400, 400);
    gtk_window_set_title(GTK_WINDOW(window), "Alpha Demo");
    g_signal_connect(G_OBJECT(window), "delete-event", gtk_main_quit, NULL);

    gtk_widget_set_app_paintable(window, TRUE);

    g_signal_connect(G_OBJECT(window), "draw", G_CALLBACK(draw), NULL);
    g_signal_connect(G_OBJECT(window), "screen-changed", G_CALLBACK(screen_changed), NULL);

    gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
    gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);
    g_signal_connect(G_OBJECT(window), "button-press-event", G_CALLBACK(clicked), NULL);

    GtkWidget* fixed_container = gtk_fixed_new();
    gtk_container_add(GTK_CONTAINER(window), fixed_container);
    GtkWidget* button = gtk_button_new_with_label("button1");
    gtk_widget_set_size_request(button, 100, 100);
    gtk_container_add(GTK_CONTAINER(fixed_container), button);

    screen_changed(window, NULL, NULL);

    gtk_widget_show_all(window);
    gtk_main();

    return 0;
}

gboolean supports_alpha = FALSE;
static void screen_changed(GtkWidget *widget, GdkScreen *old_screen, gpointer userdata)
{
    /* To check if the display supports alpha channels, get the visual */
    GdkScreen *screen = gtk_widget_get_screen(widget);
    GdkVisual *visual = gdk_screen_get_rgba_visual(screen);

    if (!visual)
    {
        printf("Your screen does not support alpha channels!\n");
        visual = gdk_screen_get_system_visual(screen);
        supports_alpha = FALSE;
    }
    else
    {
        printf("Your screen supports alpha channels!\n");
        supports_alpha = TRUE;
    }

    gtk_widget_set_visual(widget, visual);
}

static gboolean draw(GtkWidget *widget, cairo_t *cr, gpointer userdata)
{
    cairo_save (cr);

    if (supports_alpha)
    {
        cairo_set_source_rgba (cr, 0.5, 1.0, 0.50, 0.5); /* transparent */
    }
    else
    {
        cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* opaque white */
    }

    /* draw the background */
    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
    cairo_paint (cr);

    cairo_restore (cr);

    return FALSE;
}

static void clicked(GtkWindow *win, GdkEventButton *event, gpointer user_data)
{
    /* toggle window manager frames */
    gtk_window_set_decorated(win, !gtk_window_get_decorated(win));
} 

Version for C++

I include a very similar program, this time written for gtkmm in C++. It can be compiled with the following command:

g++ -otransparent main.cpp transparent.cpp `pkg-config gtkmm-3.0 --cflags --libs` -std=c++11

Note that I used some of the features in the new C++-11 standard, so you will need a compiler that supports them. (If you don't have one, you just have to replace the auto keyword when it appears with the appropriate type, which you can figure out from the definition of the function.) There are three files: main.cpp, transparent.h, and transparent.cpp.

main.cpp

/** * main.cpp * * Code adapted from 'alphademo.c' by Mike * (http://plan99.net/~mike/blog--now a dead link--unable to find it.) * as modified by karlphillip for StackExchange: * (https://stackoverflow.com/questions/3908565/how-to-make-gtk-window-background-transparent) * Re-worked for Gtkmm 3.0 by Louis Melahn, L.C. January 31, 2014. */ #include "transparent.h" #include int main (int argc, char *argv[]) { Glib::RefPtr app = Gtk::Application::create(argc, argv, "org.gtkmm.example.transparent"); Transparent transparent; //Shows the window and returns when it is closed. return app->run(transparent); }

transparent.h

/**
 * transparent.h
 *
 * Code adapted from 'alphademo.c' by Mike
 * (http://plan99.net/~mike/blog--now a dead link--unable to find it.)
 * as modified by karlphillip for StackExchange:
 *     (https://stackoverflow.com/questions/3908565/how-to-make-gtk-window-background-transparent)
 * Re-worked for Gtkmm 3.0 by Louis Melahn, L.C. January 31, 2014.
 */

#ifndef TRANSPARENT_H_
#define TRANSPARENT_H_

#include <iostream>
#include <gtk/gtk.h>
#include <gtkmm/window.h>
#include <gtkmm/button.h>
#include <gtkmm/alignment.h>

class Transparent : public Gtk::Window
{

private:
    std::string _buttonLabel;

public:
    Transparent();
    void set_visual(Glib::RefPtr<Gdk::Visual> visual);
    virtual ~Transparent();

protected:
    // Signal handlers:
    // Note that on_draw is actually overriding a virtual function
    // from the Gtk::Window class. I set it as virtual here in case
    // someone wants to override it again in a derived class.
    void on_button_clicked();
    virtual bool on_draw(const ::Cairo::RefPtr< ::Cairo::Context>& cr);
    void on_screen_changed(const Glib::RefPtr<Gdk::Screen>& previous_screen);
    bool on_window_clicked(GdkEventButton* event);

    // Member widgets:
    Gtk::Alignment _alignment;
    Gtk::Button _button;

    bool _SUPPORTS_ALPHA = false;

};

#endif /* TRANSPARENT_H_ */

transparent.cpp

/**
 * transparent.cpp
 *
 * Code adapted from 'alphademo.c' by Mike
 * (http://plan99.net/~mike/blog--now a dead link--unable to find it.)
 * as modified by karlphillip for StackExchange:
 *     (https://stackoverflow.com/questions/3908565/how-to-make-gtk-window-background-transparent)
 * Re-worked for Gtkmm 3.0 by Louis Melahn, L.C. January 31, 2014.
 */
#include "transparent.h"

Transparent::Transparent() :
    _buttonLabel("Button1"),
    _alignment(Gtk::ALIGN_START, Gtk::ALIGN_START, 0.0, 0.0),    // Aligns the button.
    _button(_buttonLabel)                                        // Creates a new button with label '_buttonLabel'.
{

    // Set up the top-level window.
    set_title("Transparency test");
    set_default_size(400,400);
    set_decorated(false);
    add_events(Gdk::BUTTON_PRESS_MASK);
    set_position(Gtk::WIN_POS_CENTER);
    set_app_paintable(true);

    // Signal handlers
    signal_draw().connect(sigc::mem_fun(*this, &Transparent::on_draw));
    signal_screen_changed().connect(sigc::mem_fun(*this, &Transparent::on_screen_changed));
    signal_button_press_event().connect(sigc::mem_fun(*this, &Transparent::on_window_clicked));
    _button.signal_clicked().connect(sigc::mem_fun(*this, &Transparent::on_button_clicked));

    // Widgets

    on_screen_changed(get_screen());

    // This will add the aligner.
    add(_alignment);

    // Now pack the button into the aligner.
    _alignment.add(_button);

    // Set up the button
    _button.set_size_request(100, 100);

    // Show the window and all its children.
    show_all();
}

Transparent::~Transparent()
{
}

void Transparent::on_button_clicked()
{
    std::cout << "The button '" << _buttonLabel << "' was pressed." << std::endl;
}

bool Transparent::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
{
    cr->save();
    if (_SUPPORTS_ALPHA) {
        cr->set_source_rgba(0.5, 1.0, 0.5, 0.5);    // transparent
    } else {
        cr->set_source_rgb(0.5, 1.0, 0.5);          // opaque
    }
    cr->set_operator(Cairo::OPERATOR_SOURCE);
    cr->paint();
    cr->restore();

    return Gtk::Window::on_draw(cr);
}

/**
 * Checks to see if the display supports alpha channels
 */
void Transparent::on_screen_changed(const Glib::RefPtr<Gdk::Screen>& previous_screen) {
    auto screen = get_screen();
    auto visual = screen->get_rgba_visual();

    if (!visual) {
        std::cout << "Your screen does not support alpha channels!" << std::endl;
    } else {
        std::cout << "Your screen supports alpha channels!" << std::endl;
        _SUPPORTS_ALPHA = TRUE;
    }

    set_visual(visual);
}

/**
 * This simply adds a method which seems to be missing in Gtk::Widget,
 * so I had to use Gtk+ manually.
 *
 * Sets the visual for 'this' (the current widget).
 */
void Transparent::set_visual(Glib::RefPtr<Gdk::Visual> visual) {
    gtk_widget_set_visual(GTK_WIDGET(gobj()), visual->gobj());
}

/**
 * If I click somewhere other than the button, this toggles
 * between having window decorations and not having them.
 */
bool Transparent::on_window_clicked(GdkEventButton* event) {
    set_decorated(!get_decorated());
    return false;
}

Hope this helps!