1 /* ghosd -- OSD with fake transparency, cairo, and pango.
2 * Copyright (C) 2006 Evan Martin <martine@danga.com>
3 *
4 * With further development by Giacomo Lozito <james@develia.org>
5 * for the ghosd-based Audacious OSD
6 * - added real transparency with X Composite Extension
7 * - added mouse event handling on OSD window
8 * - added/changed some other stuff
9 */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <sys/time.h>
14 #include <sys/poll.h>
15 #include <string.h>
16 #include <time.h>
17 #include <unistd.h>
18 #include <errno.h>
19 #include <gdk/gdk.h>
20
21 #include "ghosd.h"
22 #include "ghosd-internal.h"
23
24 static void
ghosd_main_iteration(Ghosd * ghosd)25 ghosd_main_iteration(Ghosd *ghosd) {
26 XEvent ev, pev;
27 XNextEvent(ghosd->dpy, &ev);
28
29 /* smash multiple configure/exposes into one. */
30 if (ev.type == ConfigureNotify) {
31 while (XPending(ghosd->dpy)) {
32 XPeekEvent(ghosd->dpy, &pev);
33 if (pev.type != ConfigureNotify && pev.type != Expose)
34 break;
35 XNextEvent(ghosd->dpy, &ev);
36 }
37 }
38
39 switch (ev.type) {
40 case Expose:
41 break;
42 case ConfigureNotify:
43 if (ghosd->width > 0) {
44 /* XXX if the window manager disagrees with our positioning here,
45 * we loop. */
46 if (ghosd->x != ev.xconfigure.x ||
47 ghosd->y != ev.xconfigure.y) {
48 /*width = ev.xconfigure.width;
49 height = ev.xconfigure.height;*/
50 XMoveResizeWindow(ghosd->dpy, ghosd->win,
51 ghosd->x, ghosd->y, ghosd->width, ghosd->height);
52 }
53 }
54 break;
55 case ButtonPress:
56 /* create a GhosdEventButton event and pass it to callback function */
57 if ( ghosd->eventbutton.func != NULL )
58 {
59 GhosdEventButton gevb;
60 gevb.x = ev.xbutton.x;
61 gevb.y = ev.xbutton.y;
62 gevb.x_root = ev.xbutton.x_root;
63 gevb.y_root = ev.xbutton.y_root;
64 gevb.button = ev.xbutton.button;
65 gevb.send_event = ev.xbutton.send_event;
66 gevb.time = ev.xbutton.time;
67 ghosd->eventbutton.func( ghosd , &gevb , ghosd->eventbutton.data );
68 }
69 break;
70 }
71 }
72
73 void
ghosd_main_iterations(Ghosd * ghosd)74 ghosd_main_iterations(Ghosd *ghosd) {
75 while (XPending(ghosd->dpy))
76 ghosd_main_iteration(ghosd);
77 }
78
79 void
ghosd_main_until(Ghosd * ghosd,struct timeval * until)80 ghosd_main_until(Ghosd *ghosd, struct timeval *until) {
81 struct timeval tv_now;
82
83 ghosd_main_iterations(ghosd);
84
85 for (;;) {
86 gettimeofday(&tv_now, NULL);
87 int dt = (until->tv_sec - tv_now.tv_sec )*1000 +
88 (until->tv_usec - tv_now.tv_usec)/1000;
89 if (dt <= 0) break;
90
91 struct pollfd pollfd = { ghosd_get_socket(ghosd), POLLIN, 0 };
92 int ret = poll(&pollfd, 1, dt);
93 if (ret < 0) {
94 if ( errno != EINTR )
95 {
96 perror("poll");
97 exit(1);
98 }
99 /* else go on, ignore EINTR */
100 } else if (ret > 0) {
101 ghosd_main_iterations(ghosd);
102 } else {
103 /* timer expired. */
104 break;
105 }
106 }
107 }
108
109 typedef struct {
110 cairo_surface_t* surface;
111 float alpha;
112 RenderCallback user_render;
113 } GhosdFlashData;
114
115 static void
flash_render(Ghosd * ghosd,cairo_t * cr,void * data)116 flash_render(Ghosd *ghosd, cairo_t *cr, void* data) {
117 GhosdFlashData *flash = data;
118
119 /* the first time we render, let the client render into their own surface. */
120 if (flash->surface == NULL) {
121 cairo_t *rendered_cr;
122 flash->surface = cairo_surface_create_similar(cairo_get_target(cr),
123 CAIRO_CONTENT_COLOR_ALPHA,
124 ghosd->width, ghosd->height);
125 rendered_cr = cairo_create(flash->surface);
126 flash->user_render.func(ghosd, rendered_cr, flash->user_render.data);
127 cairo_destroy(rendered_cr);
128 }
129
130 /* now that we have a rendered surface, all we normally do is copy that to
131 * the screen. */
132 cairo_set_source_surface(cr, flash->surface, 0, 0);
133 cairo_paint_with_alpha(cr, flash->alpha);
134 }
135
136 /* we don't need to free the flashdata object, because we stack-allocate that.
137 * but we do need to let the old user data free itself... */
138 static void
flash_destroy(void * data)139 flash_destroy(void *data) {
140 GhosdFlashData *flash = data;
141 if (flash->user_render.data_destroy)
142 flash->user_render.data_destroy(flash->user_render.data);
143 }
144
145 void
ghosd_flash(Ghosd * ghosd,int fade_ms,int total_display_ms)146 ghosd_flash(Ghosd *ghosd, int fade_ms, int total_display_ms) {
147 GhosdFlashData flash = {0};
148 memcpy(&flash.user_render, &ghosd->render, sizeof(RenderCallback));
149 ghosd_set_render(ghosd, flash_render, &flash, flash_destroy);
150
151 ghosd_show(ghosd);
152
153 const int STEP_MS = 50;
154 const float dalpha = 1.0 / (fade_ms / (float)STEP_MS);
155 struct timeval tv_nextupdate;
156
157 /* fade in. */
158 for (flash.alpha = 0; flash.alpha < 1.0; flash.alpha += dalpha) {
159 if (flash.alpha > 1.0) flash.alpha = 1.0;
160 ghosd_render(ghosd);
161
162 gettimeofday(&tv_nextupdate, NULL);
163 tv_nextupdate.tv_usec += STEP_MS*1000;
164 ghosd_main_until(ghosd, &tv_nextupdate);
165 }
166
167 /* full display. */
168 flash.alpha = 1.0;
169 ghosd_render(ghosd);
170
171 gettimeofday(&tv_nextupdate, NULL);
172 tv_nextupdate.tv_usec += (total_display_ms - (2*fade_ms))*1000;
173 ghosd_main_until(ghosd, &tv_nextupdate);
174
175 /* fade out. */
176 for (flash.alpha = 1.0; flash.alpha > 0.0; flash.alpha -= dalpha) {
177 ghosd_render(ghosd);
178
179 gettimeofday(&tv_nextupdate, NULL);
180 tv_nextupdate.tv_usec += STEP_MS*1000;
181 ghosd_main_until(ghosd, &tv_nextupdate);
182 }
183
184 flash.alpha = 0;
185 ghosd_render(ghosd);
186
187 /* display for another half-second,
188 * because otherwise the fade out attracts your eye
189 * and then you'll see a flash while it repaints where the ghosd was.
190 */
191 gettimeofday(&tv_nextupdate, NULL);
192 tv_nextupdate.tv_usec += 500*1000;
193 ghosd_main_until(ghosd, &tv_nextupdate);
194 }
195
196 /* vim: set ts=2 sw=2 et cino=(0 : */
197