comparison src/aosd/ghosd.c @ 569:d401f87f89f7 trunk

[svn] - added Audacious OSD, yet-another-written-from-scratch plugin to display OSD, based on Ghosd library; currently untied from configure, to compile it you have to run make in its directory; will be added to configure after some testing
author giacomo
date Mon, 29 Jan 2007 06:40:04 -0800
parents
children 6584e697e6da
comparison
equal deleted inserted replaced
568:8c64b5abdcda 569:d401f87f89f7
1 /* ghosd -- OSD with fake transparency, cairo, and pango.
2 * Copyright (C) 2006 Evan Martin <martine@danga.com>
3 */
4
5 #include "config.h"
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <cairo/cairo-xlib-xrender.h>
10 #include <X11/Xatom.h>
11
12 #include "ghosd.h"
13 #include "ghosd-internal.h"
14
15 static Pixmap
16 take_snapshot(Ghosd *ghosd) {
17 Pixmap pixmap;
18 GC gc;
19
20 /* create a pixmap to hold the screenshot. */
21 pixmap = XCreatePixmap(ghosd->dpy, ghosd->win,
22 ghosd->width, ghosd->height,
23 DefaultDepth(ghosd->dpy, DefaultScreen(ghosd->dpy)));
24
25 /* then copy the screen into the pixmap. */
26 gc = XCreateGC(ghosd->dpy, pixmap, 0, NULL);
27 XSetSubwindowMode(ghosd->dpy, gc, IncludeInferiors);
28 XCopyArea(ghosd->dpy, DefaultRootWindow(ghosd->dpy), pixmap, gc,
29 ghosd->x, ghosd->y, ghosd->width, ghosd->height,
30 0, 0);
31 XSetSubwindowMode(ghosd->dpy, gc, ClipByChildren);
32 XFreeGC(ghosd->dpy, gc);
33
34 return pixmap;
35 }
36
37 void
38 ghosd_render(Ghosd *ghosd) {
39 Pixmap pixmap;
40 GC gc;
41
42 /* make our own copy of the background pixmap as the initial surface. */
43 pixmap = XCreatePixmap(ghosd->dpy, ghosd->win, ghosd->width, ghosd->height,
44 DefaultDepth(ghosd->dpy, DefaultScreen(ghosd->dpy)));
45
46 gc = XCreateGC(ghosd->dpy, pixmap, 0, NULL);
47 if (ghosd->transparent) {
48 XCopyArea(ghosd->dpy, ghosd->background, pixmap, gc,
49 0, 0, ghosd->width, ghosd->height, 0, 0);
50 } else {
51 XFillRectangle(ghosd->dpy, pixmap, gc,
52 0, 0, ghosd->width, ghosd->height);
53 }
54 XFreeGC(ghosd->dpy, gc);
55
56 /* render with cairo. */
57 if (ghosd->render.func) {
58 /* create cairo surface using the pixmap. */
59 XRenderPictFormat *xrformat =
60 XRenderFindVisualFormat(ghosd->dpy,
61 DefaultVisual(ghosd->dpy,
62 DefaultScreen(ghosd->dpy)));
63 cairo_surface_t *surf =
64 cairo_xlib_surface_create_with_xrender_format(
65 ghosd->dpy, pixmap,
66 ScreenOfDisplay(ghosd->dpy, DefaultScreen(ghosd->dpy)),
67 xrformat,
68 ghosd->width, ghosd->height);
69
70 /* draw some stuff. */
71 cairo_t *cr = cairo_create(surf);
72 ghosd->render.func(ghosd, cr, ghosd->render.data);
73 cairo_destroy(cr);
74 }
75
76 /* point window at its new backing pixmap. */
77 XSetWindowBackgroundPixmap(ghosd->dpy, ghosd->win, pixmap);
78 /* I think it's ok to free it here because XCreatePixmap(3X11) says: "the X
79 * server frees the pixmap storage when there are no references to it".
80 */
81 XFreePixmap(ghosd->dpy, pixmap);
82
83 /* and tell the window to redraw with this pixmap. */
84 XClearWindow(ghosd->dpy, ghosd->win);
85 }
86
87 static void
88 set_hints(Display *dpy, Window win) {
89 /* we're almost a _NET_WM_WINDOW_TYPE_SPLASH, but we don't want
90 * to be centered on the screen. instead, manually request the
91 * behavior we want. */
92
93 /* turn off window decorations.
94 * we could pull this in from a motif header, but it's easier to
95 * use this snippet i found on a mailing list. */
96 Atom mwm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
97 #define MWM_HINTS_DECORATIONS (1<<1)
98 struct {
99 long flags, functions, decorations, input_mode;
100 } mwm_hints_setting = {
101 MWM_HINTS_DECORATIONS, 0, 0, 0
102 };
103 XChangeProperty(dpy, win,
104 mwm_hints, mwm_hints, 32, PropModeReplace,
105 (unsigned char *)&mwm_hints_setting, 4);
106
107 /* always on top, not in taskbar or pager. */
108 Atom win_state = XInternAtom(dpy, "_NET_WM_STATE", False);
109 Atom win_state_setting[] = {
110 XInternAtom(dpy, "_NET_WM_STATE_ABOVE", False),
111 XInternAtom(dpy, "_NET_WM_STATE_SKIP_TASKBAR", False),
112 XInternAtom(dpy, "_NET_WM_STATE_SKIP_PAGER", False)
113 };
114 XChangeProperty(dpy, win, win_state, XA_ATOM, 32,
115 PropModeReplace, (unsigned char*)&win_state_setting, 3);
116 }
117
118 static Window
119 make_window(Display *dpy) {
120 Window win;
121 XSetWindowAttributes att;
122
123 /* XXX I don't understand X well enough to know if these are the correct
124 * settings. */
125 att.backing_store = WhenMapped;
126 att.background_pixel = None;
127 att.border_pixel = 0;
128 att.background_pixmap = None;
129 att.save_under = True;
130 att.event_mask = ExposureMask | StructureNotifyMask;
131 att.override_redirect = True;
132
133 win = XCreateWindow(dpy, DefaultRootWindow(dpy),
134 -1, -1, 1, 1, 0,
135 CopyFromParent, InputOutput, CopyFromParent,
136 CWBackingStore | CWBackPixel | CWBackPixmap |
137 CWEventMask | CWSaveUnder | CWOverrideRedirect,
138 &att);
139
140 set_hints(dpy, win);
141
142 /* XXX: XSetClassHint? */
143
144 return win;
145 }
146
147 void
148 ghosd_show(Ghosd *ghosd) {
149 if (ghosd->transparent) {
150 if (ghosd->background)
151 XFreePixmap(ghosd->dpy, ghosd->background);
152 ghosd->background = take_snapshot(ghosd);
153 }
154
155 ghosd_render(ghosd);
156
157 XMapWindow(ghosd->dpy, ghosd->win);
158 }
159
160 void
161 ghosd_hide(Ghosd *ghosd) {
162 XUnmapWindow(ghosd->dpy, ghosd->win);
163 }
164
165 void
166 ghosd_set_transparent(Ghosd *ghosd, int transparent) {
167 ghosd->transparent = (transparent != 0);
168 }
169
170 void
171 ghosd_set_render(Ghosd *ghosd, GhosdRenderFunc render_func,
172 void *user_data, void (*user_data_d)(void*)) {
173 ghosd->render.func = render_func;
174 ghosd->render.data = user_data;
175 ghosd->render.data_destroy = user_data_d;
176 }
177
178 void
179 ghosd_set_position(Ghosd *ghosd, int x, int y, int width, int height) {
180 const int dpy_width = DisplayWidth(ghosd->dpy, DefaultScreen(ghosd->dpy));
181 const int dpy_height = DisplayHeight(ghosd->dpy, DefaultScreen(ghosd->dpy));
182
183 if (x == GHOSD_COORD_CENTER) {
184 x = (dpy_width - width) / 2;
185 } else if (x < 0) {
186 x = dpy_width - width + x;
187 }
188
189 if (y == GHOSD_COORD_CENTER) {
190 y = (dpy_height - height) / 2;
191 } else if (y < 0) {
192 y = dpy_height - height + y;
193 }
194
195 ghosd->x = x;
196 ghosd->y = y;
197 ghosd->width = width;
198 ghosd->height = height;
199
200 XMoveResizeWindow(ghosd->dpy, ghosd->win,
201 ghosd->x, ghosd->y, ghosd->width, ghosd->height);
202 }
203
204 #if 0
205 static int
206 x_error_handler(Display *dpy, XErrorEvent* evt) {
207 /* segfault so we can get a backtrace. */
208 char *x = NULL;
209 *x = 0;
210 return 0;
211 }
212 #endif
213
214 Ghosd*
215 ghosd_new(void) {
216 Ghosd *ghosd;
217 Display *dpy;
218 Window win;
219
220 dpy = XOpenDisplay(NULL);
221 if (dpy == NULL) {
222 fprintf(stderr, "Couldn't open display: (XXX FIXME)\n");
223 return NULL;
224 }
225
226 win = make_window(dpy);
227
228 ghosd = calloc(1, sizeof(Ghosd));
229 ghosd->dpy = dpy;
230 ghosd->win = win;
231 ghosd->transparent = 1;
232
233 return ghosd;
234 }
235
236 void
237 ghosd_destroy(Ghosd* ghosd) {
238 if (ghosd->background)
239 XFreePixmap(ghosd->dpy, ghosd->background);
240 XDestroyWindow(ghosd->dpy, ghosd->win);
241 }
242
243 int
244 ghosd_get_socket(Ghosd *ghosd) {
245 return ConnectionNumber(ghosd->dpy);
246 }
247
248 /* vim: set ts=2 sw=2 et cino=(0 : */