summaryrefslogtreecommitdiff
path: root/alphademo.c
blob: 5e7e00ef2e55e0d967e357ca77cd6461e889d8a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// gcc -o alphademo alphademo.c `pkg-config --cflags --libs gtk+-2.0`

#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <cairo.h>
#include <math.h>

/*
 * 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));
}