// gcc -o alphademo alphademo.c `pkg-config --cflags --libs gtk+-2.0` #include #include #include #include /* * This program shows you how to create semi-transparent windows, * without any of the historical screenshot hacks. It requires * a modern system, with a compositing manager. I use xcompmgr * and the nvidia drivers with RenderAccel, and it works well. * * I'll take you through each step as we go. Minimal GTK+ knowledge is * assumed. */ static void screen_changed(GtkWidget *widget, GdkScreen *old_screen, gpointer user_data); static gboolean expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data); static void clicked(GtkWindow *win, GdkEventButton *event, gpointer user_data); int main(int argc, char **argv) { /* boilerplate initialization code */ gtk_init(&argc, &argv); GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "Alpha Demo"); g_signal_connect(G_OBJECT(window), "delete-event", gtk_main_quit, NULL); /* Tell GTK+ that we want to draw the windows background ourself. * If we don't do this then GTK+ will clear the window to the * opaque theme default color, which isn't what we want. */ gtk_widget_set_app_paintable(window, TRUE); gtk_widget_set_double_buffered(window, FALSE); /* We need to handle two events ourself: "expose-event" and "screen-changed". * * The X server sends us an expose event when the window becomes * visible on screen. It means we need to draw the contents. On a * composited desktop expose is normally only sent when the window * is put on the screen. On a non-composited desktop it can be * sent whenever the window is uncovered by another. * * The screen-changed event means the display to which we are * drawing changed. GTK+ supports migration of running * applications between X servers, which might not support the * same features, so we need to check each time. */ g_signal_connect(G_OBJECT(window), "check-resize", G_CALLBACK(expose), NULL); g_signal_connect(G_OBJECT(window), "expose-event", G_CALLBACK(expose), NULL); g_signal_connect(G_OBJECT(window), "screen-changed", G_CALLBACK(screen_changed), NULL); /* toggle title bar on click - we add the mask to tell X we are interested in this event */ gtk_window_set_decorated(GTK_WINDOW(window), TRUE); gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK); g_signal_connect(G_OBJECT(window), "button-press-event", G_CALLBACK(clicked), NULL); /* initialize for the current display */ screen_changed(window, NULL, NULL); /* Run the program */ gtk_widget_show_all(window); gtk_main(); return 0; } /* Only some X servers support alpha channels. Always have a fallback */ 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 colormap */ GdkScreen *screen = gtk_widget_get_screen(widget); GdkColormap *colormap = gdk_screen_get_rgba_colormap(screen); if (!colormap) { printf("Your screen does not support alpha channels!\n"); colormap = gdk_screen_get_rgb_colormap(screen); supports_alpha = FALSE; } else { printf("Your screen supports alpha channels!\n"); supports_alpha = TRUE; } /* Now we have a colormap appropriate for the screen, use it */ gtk_widget_set_colormap(widget, colormap); } static void draw_circle(cairo_t *cr, int cx, int cy, int ir, int or, double r, double g, double b) { cairo_pattern_t *pat; pat = cairo_pattern_create_radial(cx, cy, 0, cx, cy, or); cairo_pattern_add_color_stop_rgba(pat, 1.0, r, g, b, 0.0); cairo_pattern_add_color_stop_rgba(pat, 0.0, r, g, b, 1.0); cairo_set_source(cr, pat); cairo_arc(cr, cx, cy, or, 0, 2 * M_PI); cairo_arc_negative(cr, cx, cy, ir, 2 * M_PI, 0); cairo_fill(cr); cairo_pattern_destroy(pat); } static void draw_rectangle(cairo_t *cr, int cx, int cy, int sx, int sy, double r, double g, double b, double a) { cairo_set_source_rgba(cr, r, g, b, a); cairo_rectangle(cr, cx, cy, cx+sx, cy+sy); cairo_fill(cr); } /* This is called when we need to draw the windows contents */ static gboolean expose(GtkWidget *widget, GdkEventExpose *event, gpointer userdata) { printf("Exposed\n"); cairo_t *cr = gdk_cairo_create(widget->window); if (supports_alpha) cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0); /* 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); int width, height; gtk_window_get_size(GTK_WINDOW(widget), &width, &height); int radius = (width < height ? width : height); int cx = width / 2; int cy = height / 2; draw_rectangle(cr, 0, 0, cx, cy, 1.0, 1.0, 1.0, 0.2); draw_rectangle(cr, cx, 0, cx, cy, 1.0, 1.0, 1.0, 0.4); draw_rectangle(cr, 0, cy, cx, cy, 1.0, 1.0, 1.0, 0.6); draw_rectangle(cr, cx, cy, cx, cy, 1.0, 1.0, 1.0, 0.8); draw_circle(cr, cx, cy, radius *3/8, radius / 2 , 1.0, 0.2, 0.2); draw_circle(cr, cx, cy, radius / 4, radius *3/8, 0.2, 1.0, 0.2); draw_circle(cr, cx, cy, 0, radius / 4 , 0.2, 0.2, 1.0); cairo_destroy(cr); return FALSE; } static void clicked(GtkWindow *win, GdkEventButton *event, gpointer user_data) { printf("Toggling frame\n"); /* toggle window manager frames */ gtk_window_set_decorated(win, !gtk_window_get_decorated(win)); }