1 /*
2 * XOSD
3 *
4 * Copyright (c) 2000 Andre Renaud (andre@ignavus.net)
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 #include "intern.h"
21
22 #define SLIDER_SCALE 0.8
23 #define SLIDER_SCALE_ON 0.7
24 #define XOFFSET 10
25
26 const char *osd_default_font =
27 "-misc-fixed-medium-r-semicondensed--*-*-*-*-c-*-*-*";
28 #if 0
29 "-adobe-helvetica-bold-r-*-*-10-*";
30 #endif
31 const char *osd_default_colour = "green";
32
33 /** Global error string. */
34 char *xosd_error;
35
36 /* Wait until display is in next state. {{{ */
37 static void
_wait_until_update(xosd * osd,int generation)38 _wait_until_update(xosd * osd, int generation)
39 {
40 pthread_mutex_lock(&osd->mutex_sync);
41 while (osd->generation == generation) {
42 DEBUG(Dtrace, "waiting %d %d", generation, osd->generation);
43 pthread_cond_wait(&osd->cond_sync, &osd->mutex_sync);
44 }
45 pthread_mutex_unlock(&osd->mutex_sync);
46 }
47
48 /* }}} */
49
50 /* Serialize access to the X11 connection. {{{
51 *
52 * Background: xosd needs a thread which handles X11 exposures. XNextEvent()
53 * blocks and would deny any other thread - especially the thread which calls
54 * the xosd API - the usage of the same X11 connection. XInitThreads() can't be
55 * used, because xosd is a library which can be loaded dynamically way after
56 * the loading application has done its first X11 call, after which calling
57 * XInitThreads() is no longer possible. (Debian-Bug #252170)
58 *
59 * The exposure-thread gets the MUTEX and sleeps on a select([X11,pipe]). When
60 * an X11 event occurs, the tread can directly use X11 calls.
61 * When another thread needs to do an X11 call, it uses _xosd_lock(osd) to
62 * notify the exposure-thread via the pipe, which uses cond_wait to voluntarily
63 * pass the right to access X11 to the signalling thread. The calling thread
64 * acquire the MUTEX and can than use the X11 calls.
65 * After using X11, the thread calls _xosd_unlock(osd) to remove its token from
66 * the pipe and to wake up the exposure-thread via cond_signal, before
67 * releasing the MUTEX.
68 * The number of characters in the pipe is an indication for the number of
69 * threads waiting for the X11-MUTEX.
70 */
71 static /*inline */ void
_xosd_lock(xosd * osd)72 _xosd_lock(xosd * osd)
73 {
74 char c = 0;
75 FUNCTION_START(Dlocking);
76 write(osd->pipefd[1], &c, sizeof(c));
77 pthread_mutex_lock(&osd->mutex);
78 FUNCTION_END(Dlocking);
79 }
80 static /*inline */ void
_xosd_unlock(xosd * osd)81 _xosd_unlock(xosd * osd)
82 {
83 char c;
84 int generation = osd->generation, update = osd->update;
85 FUNCTION_START(Dlocking);
86 read(osd->pipefd[0], &c, sizeof(c));
87 pthread_cond_signal(&osd->cond_wait);
88 pthread_mutex_unlock(&osd->mutex);
89 if (update & UPD_show)
90 _wait_until_update(osd, generation & ~1); /* no wait when already shown. */
91 FUNCTION_END(Dlocking);
92 }
93
94 /* }}} */
95
96 /* Draw percentage/slider bar. {{{ */
97 static void /*inline */
_draw_bar(xosd * osd,int nbars,int on,XRectangle * p,XRectangle * mod,int is_slider)98 _draw_bar(xosd * osd, int nbars, int on, XRectangle * p, XRectangle * mod,
99 int is_slider)
100 {
101 int i;
102 XRectangle rs[2];
103 FUNCTION_START(Dfunction);
104
105 rs[0].x = rs[1].x = mod->x + p->x;
106 rs[0].y = (rs[1].y = mod->y + p->y) + p->height / 3;
107 rs[0].width = mod->width + p->width * SLIDER_SCALE;
108 rs[0].height = mod->height + p->height / 3;
109 rs[1].width = mod->width + p->width * SLIDER_SCALE_ON;
110 rs[1].height = mod->height + p->height;
111 for (i = 0; i < nbars; i++, rs[0].x = rs[1].x += p->width) {
112 XRectangle *r = &(rs[is_slider ? (i == on) : (i < on)]);
113 XFillRectangles(osd->display, osd->mask_bitmap, osd->mask_gc, r, 1);
114 XFillRectangles(osd->display, osd->line_bitmap, osd->gc, r, 1);
115 }
116 FUNCTION_END(Dfunction);
117 }
118 static void
draw_bar(xosd * osd,int line)119 draw_bar(xosd * osd, int line)
120 {
121 struct xosd_bar *l = &osd->lines[line].bar;
122 int is_slider = l->type == LINE_slider, nbars, on;
123 XRectangle p, m;
124 p.x = XOFFSET;
125 p.y = osd->line_height * line;
126 p.width = -osd->extent->y / 2;
127 p.height = -osd->extent->y;
128
129 assert(osd);
130 FUNCTION_START(Dfunction);
131
132 /* Calculate number of bars in automatic mode */
133 if (osd->bar_length == -1) {
134 nbars = (osd->screen_width * SLIDER_SCALE) / p.width;
135 switch (osd->align) {
136 case XOSD_center:
137 p.x = osd->screen_width * ((1 - SLIDER_SCALE) / 2);
138 break;
139 case XOSD_right:
140 p.x = osd->screen_width * (1 - SLIDER_SCALE);
141 case XOSD_left:
142 break;
143 }
144 } else {
145 nbars = osd->bar_length;
146 switch (osd->align) {
147 case XOSD_center:
148 p.x = (osd->screen_width - (nbars * p.width)) / 2;
149 break;
150 case XOSD_right:
151 p.x = osd->screen_width - (nbars * p.width) - p.x;
152 case XOSD_left:
153 break;
154 }
155 }
156 on = ((nbars - is_slider) * l->value) / 100;
157
158 DEBUG(Dvalue, "percent=%d, nbars=%d, on=%d", l->value, nbars, on);
159
160 /* Outline */
161 if (osd->outline_offset) {
162 m.x = m.y = -osd->outline_offset;
163 m.width = m.height = 2 * osd->outline_offset;
164 XSetForeground(osd->display, osd->gc, osd->outline_pixel);
165 _draw_bar(osd, nbars, on, &p, &m, is_slider);
166 }
167 /* Shadow */
168 if (osd->shadow_offset) {
169 m.x = m.y = osd->shadow_offset;
170 m.width = m.height = 0;
171 XSetForeground(osd->display, osd->gc, osd->shadow_pixel);
172 _draw_bar(osd, nbars, on, &p, &m, is_slider);
173 }
174 /* Bar/Slider */
175 if (1) {
176 m.x = m.y = m.width = m.height = 0;
177 XSetForeground(osd->display, osd->gc, osd->pixel);
178 _draw_bar(osd, nbars, on, &p, &m, is_slider);
179 }
180 }
181
182 /* }}} */
183
184 /* Draw text. {{{ */
185 static void /*inline */
_draw_text(xosd * osd,char * string,int x,int y)186 _draw_text(xosd * osd, char *string, int x, int y)
187 {
188 int len = strlen(string);
189 FUNCTION_START(Dfunction);
190 XmbDrawString(osd->display, osd->mask_bitmap, osd->fontset, osd->mask_gc, x,
191 y, string, len);
192 XmbDrawString(osd->display, osd->line_bitmap, osd->fontset, osd->gc, x, y,
193 string, len);
194 FUNCTION_END(Dfunction);
195 }
196 static void
draw_text(xosd * osd,int line)197 draw_text(xosd * osd, int line)
198 {
199 int x = XOFFSET, y = osd->line_height * line - osd->extent->y;
200 struct xosd_text *l = &osd->lines[line].text;
201
202 assert(osd);
203 FUNCTION_START(Dfunction);
204
205 if (l->string == NULL)
206 return;
207
208 if (l->width < 0) {
209 XRectangle rect;
210 XmbTextExtents(osd->fontset, l->string, strlen(l->string), NULL, &rect);
211 l->width = rect.width;
212 }
213
214 switch (osd->align) {
215 case XOSD_center:
216 x = (osd->screen_width - l->width) / 2;
217 break;
218 case XOSD_right:
219 x = osd->screen_width - l->width - x;
220 case XOSD_left:
221 break;
222 }
223
224 if (osd->shadow_offset) {
225 XSetForeground(osd->display, osd->gc, osd->shadow_pixel);
226 _draw_text(osd, l->string, x + osd->shadow_offset,
227 y + osd->shadow_offset);
228 }
229 if (osd->outline_offset) {
230 int i, j;
231 XSetForeground(osd->display, osd->gc, osd->outline_pixel);
232 /* FIXME: echo . | osd_cat -O 50 -p middle -A center */
233 for (i = 1; i <= osd->outline_offset; i++)
234 for (j = 0; j < 9; j++)
235 if (j != 4)
236 _draw_text(osd, l->string, x + (j / 3 - 1) * i,
237 y + (j % 3 - 1) * i);
238 }
239 if (1) {
240 XSetForeground(osd->display, osd->gc, osd->pixel);
241 _draw_text(osd, l->string, x, y);
242 }
243 }
244
245 /* }}} */
246
247 /* Handles X11 events, timeouts and does the drawing. {{{
248 * This is running in it's own thread for Expose-events.
249 * The order of update handling is important:
250 * 1. The size must be correct -> UPD_size first
251 * 2. Change the position, which might expose part of window -> UPD_pos
252 * 3. The XShape must be set before something is drawn -> UPD_mask, UPD_lines
253 * 4. The window should be mapped before something is drawn -> UPD_show
254 * 5. Start the timer last to not account for processing time -> UPD_timer
255 * If you change this order, you'll get a broken display. You've been warned!
256 */
257 static void *
event_loop(void * osdv)258 event_loop(void *osdv)
259 {
260 xosd *osd = osdv;
261 int xfd, max;
262
263 FUNCTION_START(Dfunction);
264 DEBUG(Dtrace, "event thread started");
265 assert(osd);
266
267 xfd = ConnectionNumber(osd->display);
268 max = (osd->pipefd[0] > xfd) ? osd->pipefd[0] : xfd;
269
270 pthread_mutex_lock(&osd->mutex);
271 DEBUG(Dtrace, "Request exposure events");
272 XSelectInput(osd->display, osd->window, ExposureMask);
273 osd->update |= UPD_size | UPD_pos | UPD_mask;
274 while (!osd->done) {
275 int retval, line;
276 fd_set readfds;
277 struct timeval tv, *tvp = NULL;
278
279 FD_ZERO(&readfds);
280 FD_SET(xfd, &readfds);
281 FD_SET(osd->pipefd[0], &readfds);
282
283 /* Hide display requested. */
284 if (osd->update & UPD_hide) {
285 DEBUG(Dupdate, "UPD_hide");
286 if (osd->generation & 1) {
287 XUnmapWindow(osd->display, osd->window);
288 osd->generation++;
289 }
290 }
291 /* The font, outline or shadow was changed. Recalculate line height,
292 * resize window and bitmaps. */
293 if (osd->update & UPD_size) {
294 XFontSetExtents *extents = XExtentsOfFontSet(osd->fontset);
295 DEBUG(Dupdate, "UPD_size");
296 osd->extent = &extents->max_logical_extent;
297 osd->line_height = osd->extent->height + osd->shadow_offset + 2 *
298 osd->outline_offset;
299 osd->height = osd->line_height * osd->number_lines;
300 for (line = 0; line < osd->number_lines; line++)
301 if (osd->lines[line].type == LINE_text)
302 osd->lines[line].text.width = -1;
303
304 XResizeWindow(osd->display, osd->window, osd->screen_width,
305 osd->height);
306 XFreePixmap(osd->display, osd->mask_bitmap);
307 osd->mask_bitmap = XCreatePixmap(osd->display, osd->window,
308 osd->screen_width, osd->height, 1);
309 XFreePixmap(osd->display, osd->line_bitmap);
310 osd->line_bitmap = XCreatePixmap(osd->display, osd->window,
311 osd->screen_width, osd->height,
312 osd->depth);
313 }
314 /* H/V offset or vertical positon was changed. Horizontal alignment is
315 * handles internally as line realignment with UPD_content. */
316 if (osd->update & UPD_pos) {
317 int x = 0, y = 0;
318 DEBUG(Dupdate, "UPD_pos");
319 switch (osd->align) {
320 case XOSD_left:
321 case XOSD_center:
322 x = osd->screen_xpos + osd->hoffset;
323 break;
324 case XOSD_right:
325 x = osd->screen_xpos - osd->hoffset;
326 }
327 switch (osd->pos) {
328 case XOSD_bottom:
329 y = osd->screen_height - osd->height - osd->voffset;
330 break;
331 case XOSD_middle:
332 y = (osd->screen_height - osd->height) / 2 - osd->voffset;
333 break;
334 case XOSD_top:
335 y = osd->voffset;
336 }
337 XMoveWindow(osd->display, osd->window, x, y);
338 }
339 /* If the content changed, redraw lines in background buffer.
340 * Also update XShape unless only colours were changed. */
341 if (osd->update & (UPD_mask | UPD_lines)) {
342 DEBUG(Dupdate, "UPD_lines");
343 for (line = 0; line < osd->number_lines; line++) {
344 int y = osd->line_height * line;
345 #ifdef DEBUG_XSHAPE
346 XSetForeground(osd->display, osd->gc, osd->outline_pixel);
347 XFillRectangle(osd->display, osd->line_bitmap, osd->gc, 0,
348 y, osd->screen_width, osd->line_height);
349 #endif
350 if (osd->update & UPD_mask) {
351 XFillRectangle(osd->display, osd->mask_bitmap, osd->mask_gc_back, 0,
352 y, osd->screen_width, osd->line_height);
353 }
354 switch (osd->lines[line].type) {
355 case LINE_text:
356 draw_text(osd, line);
357 break;
358 case LINE_percentage:
359 case LINE_slider:
360 draw_bar(osd, line);
361 case LINE_blank:
362 break;
363 }
364 }
365 }
366 #ifndef DEBUG_XSHAPE
367 /* More than colours was changed, also update XShape. */
368 if (osd->update & UPD_mask) {
369 DEBUG(Dupdate, "UPD_mask");
370 XShapeCombineMask(osd->display, osd->window, ShapeBounding, 0, 0,
371 osd->mask_bitmap, ShapeSet);
372 }
373 #endif
374 /* Show display requested. */
375 if (osd->update & UPD_show) {
376 DEBUG(Dupdate, "UPD_show");
377 if (~osd->generation & 1) {
378 osd->generation++;
379 XMapRaised(osd->display, osd->window);
380 }
381 }
382 /* Copy content, if window was changed or exposed. */
383 if ((osd->generation & 1)
384 && osd->update & (UPD_size | UPD_pos | UPD_lines | UPD_show)) {
385 DEBUG(Dupdate, "UPD_copy");
386 XCopyArea(osd->display, osd->line_bitmap, osd->window, osd->gc, 0, 0,
387 osd->screen_width, osd->height, 0, 0);
388 }
389 /* Flush all pennding X11 requests, if any. */
390 if (osd->update & ~UPD_timer) {
391 XFlush(osd->display);
392 osd->update &= UPD_timer;
393 }
394 /* Restart the timer when requested. */
395 if (osd->update & UPD_timer) {
396 DEBUG(Dupdate, "UPD_timer");
397 osd->update = UPD_none;
398 if ((osd->generation & 1) && (osd->timeout > 0))
399 gettimeofday(&osd->timeout_start, NULL);
400 else
401 timerclear(&osd->timeout_start);
402 }
403 /* Calculate timeout delta or hide display. */
404 if (timerisset(&osd->timeout_start)) {
405 gettimeofday(&tv, NULL);
406 tv.tv_sec -= osd->timeout;
407 if (timercmp(&tv, &osd->timeout_start, <)) {
408 tv.tv_sec = osd->timeout_start.tv_sec - tv.tv_sec;
409 tv.tv_usec = osd->timeout_start.tv_usec - tv.tv_usec;
410 if (tv.tv_usec < 0) {
411 tv.tv_usec += 1000000;
412 tv.tv_sec -= 1;
413 }
414 tvp = &tv;
415 } else {
416 timerclear(&osd->timeout_start);
417 if (osd->generation & 1)
418 osd->update |= UPD_hide;
419 continue; /* Hide the window first and than restart the loop */
420 }
421 }
422
423 /* Signal update */
424 pthread_mutex_lock(&osd->mutex_sync);
425 pthread_cond_broadcast(&osd->cond_sync);
426 pthread_mutex_unlock(&osd->mutex_sync);
427
428 /* Wait for the next X11 event or an API request via the pipe. */
429 retval = select(max + 1, &readfds, NULL, NULL, tvp);
430 DEBUG(Dvalue, "SELECT=%d PIPE=%ld X11=%ld", retval,
431 FD_ISSET(osd->pipefd[0], &readfds), FD_ISSET(xfd, &readfds));
432
433 if (retval == -1 && errno == EINTR) {
434 DEBUG(Dselect, "select() EINTR");
435 continue;
436 } else if (retval == -1) {
437 DEBUG(Dselect, "select() error %d", errno);
438 osd->done = 1;
439 break;
440 } else if (retval == 0) {
441 DEBUG(Dselect, "select() timeout");
442 continue; /* timeout */
443 } else if (FD_ISSET(osd->pipefd[0], &readfds)) {
444 /* Another thread wants to use the X11 connection */
445 pthread_cond_wait(&osd->cond_wait, &osd->mutex);
446 DEBUG(Dselect, "Resume exposure thread after X11 call");
447 continue;
448 } else if (FD_ISSET(xfd, &readfds)) {
449 XEvent report;
450 /* There is a event, but it might not be an Exposure-event, so don't use
451 * XWindowEvent(), since that might block. */
452 XNextEvent(osd->display, &report);
453 /* ignore sent by server/manual send flag */
454 switch (report.type & 0x7f) {
455 case Expose:
456 {
457 XExposeEvent *XE = &report.xexpose;
458 /* http://x.holovko.ru/Xlib/chap10.html#10.9.1 */
459 DEBUG(Dvalue, "expose %d: x=%d y=%d w=%d h=%d", XE->count,
460 XE->x, XE->y, XE->width, XE->height);
461 #if 0
462 if (report.xexpose.count == 0) {
463 int ytop, ybot;
464 ytop = report.xexpose.y / osd->line_height;
465 ybot =
466 (report.xexpose.y + report.xexpose.height) / osd->line_height;
467 do {
468 osd->lines[ytop].width = -1;
469 } while (ytop++ < ybot);
470 }
471 #endif
472 XCopyArea(osd->display, osd->line_bitmap, osd->window, osd->gc,
473 report.xexpose.x, report.xexpose.y, report.xexpose.width,
474 report.xexpose.height, report.xexpose.x, report.xexpose.y);
475 break;
476 }
477 case GraphicsExpose:
478 {
479 XGraphicsExposeEvent *XE = &report.xgraphicsexpose;
480 DEBUG(Dvalue, "gfxexpose %d: x=%d y=%d w=%d h=%d code=%d",
481 XE->count, XE->x, XE->y, XE->width, XE->height, XE->major_code);
482 break;
483 }
484 case NoExpose:
485 {
486 XNoExposeEvent *XE = &report.xnoexpose;
487 DEBUG(Dvalue, "noexpose: code=%d", XE->major_code);
488 break;
489 }
490 default:
491 DEBUG(Dvalue, "XEvent=%d", report.type);
492 break;
493 }
494 continue;
495 } else {
496 DEBUG(Dselect, "select() FATAL %d", retval);
497 exit(-1); /* Impossible */
498 }
499 }
500 pthread_mutex_unlock(&osd->mutex);
501
502 return NULL;
503 }
504
505 /* }}} */
506
507 /* Parse textual colour value. {{{ */
508 static int
parse_colour(xosd * osd,XColor * col,unsigned long * pixel,const char * colour)509 parse_colour(xosd * osd, XColor * col, unsigned long *pixel,
510 const char *colour)
511 {
512 Colormap colourmap;
513 int retval = 0;
514
515 FUNCTION_START(Dfunction);
516 DEBUG(Dtrace, "getting colourmap");
517 colourmap = DefaultColormap(osd->display, osd->screen);
518
519 DEBUG(Dtrace, "parsing colour");
520 if (XParseColor(osd->display, colourmap, colour, col)) {
521 DEBUG(Dtrace, "attempting to allocate colour");
522 if (XAllocColor(osd->display, colourmap, col)) {
523 DEBUG(Dtrace, "allocation sucessful");
524 *pixel = col->pixel;
525 } else {
526 DEBUG(Dtrace, "defaulting to white. could not allocate colour");
527 *pixel = WhitePixel(osd->display, osd->screen);
528 retval = -1;
529 }
530 } else {
531 DEBUG(Dtrace, "could not poarse colour. defaulting to white");
532 *pixel = WhitePixel(osd->display, osd->screen);
533 retval = -1;
534 }
535
536 return retval;
537 }
538
539 /* }}} */
540
541 /* Tell window manager to put window topmost. {{{ */
542 void
stay_on_top(Display * dpy,Window win)543 stay_on_top(Display * dpy, Window win)
544 {
545 Atom gnome, net_wm, type;
546 int format;
547 unsigned long nitems, bytesafter;
548 unsigned char *args = NULL;
549 Window root = DefaultRootWindow(dpy);
550
551 FUNCTION_START(Dfunction);
552 /*
553 * build atoms
554 */
555 gnome = XInternAtom(dpy, "_WIN_SUPPORTING_WM_CHECK", False);
556 net_wm = XInternAtom(dpy, "_NET_SUPPORTED", False);
557
558 /*
559 * gnome-compilant
560 * tested with icewm + WindowMaker
561 */
562 if (Success == XGetWindowProperty
563 (dpy, root, gnome, 0, (65536 / sizeof(long)), False,
564 AnyPropertyType, &type, &format, &nitems, &bytesafter, &args) &&
565 nitems > 0) {
566 /*
567 * FIXME: check capabilities
568 */
569 XClientMessageEvent xev;
570 Atom gnome_layer = XInternAtom(dpy, "_WIN_LAYER", False);
571
572 memset(&xev, 0, sizeof(xev));
573 xev.type = ClientMessage;
574 xev.window = win;
575 xev.message_type = gnome_layer;
576 xev.format = 32;
577 xev.data.l[0] = 6 /* WIN_LAYER_ONTOP */ ;
578
579 XSendEvent(dpy, DefaultRootWindow(dpy), False, SubstructureNotifyMask,
580 (XEvent *) & xev);
581 XFree(args);
582 }
583 /*
584 * netwm compliant.
585 * tested with kde
586 */
587 else if (Success == XGetWindowProperty
588 (dpy, root, net_wm, 0, (65536 / sizeof(long)), False,
589 AnyPropertyType, &type, &format, &nitems, &bytesafter, &args)
590 && nitems > 0) {
591 XEvent e;
592 Atom net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
593 Atom net_wm_top = XInternAtom(dpy, "_NET_WM_STATE_STAYS_ON_TOP", False);
594
595 memset(&e, 0, sizeof(e));
596 e.xclient.type = ClientMessage;
597 e.xclient.message_type = net_wm_state;
598 e.xclient.display = dpy;
599 e.xclient.window = win;
600 e.xclient.format = 32;
601 e.xclient.data.l[0] = 1 /* _NET_WM_STATE_ADD */ ;
602 e.xclient.data.l[1] = net_wm_top;
603 e.xclient.data.l[2] = 0l;
604 e.xclient.data.l[3] = 0l;
605 e.xclient.data.l[4] = 0l;
606
607 XSendEvent(dpy, DefaultRootWindow(dpy), False,
608 SubstructureRedirectMask, &e);
609 XFree(args);
610 }
611 XRaiseWindow(dpy, win);
612 }
613
614 /* }}} */
615
616 /* xosd_init -- Create a new xosd "object" {{{
617 * Deprecated: Use xosd_create. */
618 xosd *
xosd_init(const char * font,const char * colour,int timeout,xosd_pos pos,int voffset,int shadow_offset,int number_lines)619 xosd_init(const char *font, const char *colour, int timeout, xosd_pos pos,
620 int voffset, int shadow_offset, int number_lines)
621 {
622 xosd *osd = xosd_create(number_lines);
623
624 FUNCTION_START(Dfunction);
625 if (osd == NULL)
626 return NULL;
627
628 if (xosd_set_font(osd, font) == -1) {
629 xosd_destroy(osd);
630 /*
631 * we do not set xosd_error, as set_font has already set it to
632 * a sensible error message.
633 */
634 return NULL;
635 }
636 xosd_set_colour(osd, colour);
637 xosd_set_timeout(osd, timeout);
638 xosd_set_pos(osd, pos);
639 xosd_set_vertical_offset(osd, voffset);
640 xosd_set_shadow_offset(osd, shadow_offset);
641
642 return osd;
643 }
644
645 /* }}} */
646
647 /* xosd_create -- Create a new xosd "object" {{{ */
648 xosd *
xosd_create(int number_lines)649 xosd_create(int number_lines)
650 {
651 xosd *osd;
652 int event_basep, error_basep, i;
653 char *display;
654 XSetWindowAttributes setwinattr;
655 XGCValues xgcv = { .graphics_exposures = False };
656 #ifdef HAVE_XINERAMA
657 int screens;
658 int dummy_a, dummy_b;
659 XineramaScreenInfo *screeninfo = NULL;
660 #endif
661
662 FUNCTION_START(Dfunction);
663 DEBUG(Dtrace, "getting display");
664 display = getenv("DISPLAY");
665 if (!display) {
666 xosd_error = "No display";
667 return NULL;
668 }
669
670 DEBUG(Dtrace, "Mallocing osd");
671 osd = malloc(sizeof(xosd));
672 memset(osd, 0, sizeof(xosd));
673 if (osd == NULL) {
674 xosd_error = "Out of memory";
675 goto error0;
676 }
677
678 DEBUG(Dtrace, "Creating pipe");
679 if (pipe(osd->pipefd) == -1) {
680 xosd_error = "Error creating pipe";
681 goto error0b;
682 }
683
684 DEBUG(Dtrace, "initializing mutex");
685 pthread_mutex_init(&osd->mutex, NULL);
686 pthread_mutex_init(&osd->mutex_sync, NULL);
687 DEBUG(Dtrace, "initializing condition");
688 pthread_cond_init(&osd->cond_wait, NULL);
689 pthread_cond_init(&osd->cond_sync, NULL);
690
691 DEBUG(Dtrace, "initializing number lines");
692 osd->number_lines = number_lines;
693 osd->lines = malloc(sizeof(union xosd_line) * osd->number_lines);
694 if (osd->lines == NULL) {
695 xosd_error = "Out of memory";
696 goto error1;
697 }
698
699 for (i = 0; i < osd->number_lines; i++)
700 memset(&osd->lines[i], 0, sizeof(union xosd_line));
701
702 DEBUG(Dtrace, "misc osd variable initialization");
703 osd->generation = 0;
704 osd->done = 0;
705 osd->pos = XOSD_top;
706 osd->hoffset = 0;
707 osd->align = XOSD_left;
708 osd->voffset = 0;
709 osd->timeout = -1;
710 timerclear(&osd->timeout_start);
711 osd->fontset = NULL;
712 osd->bar_length = -1; /* old automatic width calculation */
713
714 DEBUG(Dtrace, "Display query");
715 osd->display = XOpenDisplay(display);
716 if (!osd->display) {
717 xosd_error = "Cannot open display";
718 goto error2;
719 }
720 osd->screen = XDefaultScreen(osd->display);
721
722 DEBUG(Dtrace, "x shape extension query");
723 if (!XShapeQueryExtension(osd->display, &event_basep, &error_basep)) {
724 xosd_error = "X-Server does not support shape extension";
725 goto error3;
726 }
727
728 osd->visual = DefaultVisual(osd->display, osd->screen);
729 osd->depth = DefaultDepth(osd->display, osd->screen);
730
731 DEBUG(Dtrace, "font selection info");
732 xosd_set_font(osd, osd_default_font);
733 if (osd->fontset == NULL) {
734 /*
735 * if we still don't have a fontset, then abort
736 */
737 xosd_error = "Default font not found";
738 goto error3;
739 }
740
741 DEBUG(Dtrace, "width and height initialization");
742 #ifdef HAVE_XINERAMA
743 if (XineramaQueryExtension(osd->display, &dummy_a, &dummy_b) &&
744 (screeninfo = XineramaQueryScreens(osd->display, &screens)) &&
745 XineramaIsActive(osd->display)) {
746 osd->screen_width = screeninfo[0].width;
747 osd->screen_height = screeninfo[0].height;
748 osd->screen_xpos = screeninfo[0].x_org;
749 } else
750 #endif
751 {
752 osd->screen_width = XDisplayWidth(osd->display, osd->screen);
753 osd->screen_height = XDisplayHeight(osd->display, osd->screen);
754 osd->screen_xpos = 0;
755 }
756 #ifdef HAVE_XINERAMA
757 if (screeninfo)
758 XFree(screeninfo);
759 #endif
760 osd->line_height = 10 /*Dummy value */ ;
761 osd->height = osd->line_height * osd->number_lines;
762
763 DEBUG(Dtrace, "creating X Window");
764 setwinattr.override_redirect = 1;
765
766 osd->window = XCreateWindow(osd->display,
767 XRootWindow(osd->display, osd->screen),
768 0, 0,
769 osd->screen_width, osd->height,
770 0,
771 osd->depth,
772 CopyFromParent,
773 osd->visual, CWOverrideRedirect, &setwinattr);
774 XStoreName(osd->display, osd->window, "XOSD");
775
776 osd->mask_bitmap =
777 XCreatePixmap(osd->display, osd->window, osd->screen_width,
778 osd->height, 1);
779 osd->line_bitmap =
780 XCreatePixmap(osd->display, osd->window, osd->screen_width,
781 osd->line_height, osd->depth);
782
783 osd->gc = XCreateGC(osd->display, osd->window, GCGraphicsExposures, &xgcv);
784 osd->mask_gc = XCreateGC(osd->display, osd->mask_bitmap, GCGraphicsExposures, &xgcv);
785 osd->mask_gc_back = XCreateGC(osd->display, osd->mask_bitmap, GCGraphicsExposures, &xgcv);
786
787 XSetBackground(osd->display, osd->gc,
788 WhitePixel(osd->display, osd->screen));
789
790 XSetForeground(osd->display, osd->mask_gc_back,
791 BlackPixel(osd->display, osd->screen));
792 XSetBackground(osd->display, osd->mask_gc_back,
793 WhitePixel(osd->display, osd->screen));
794
795 XSetForeground(osd->display, osd->mask_gc,
796 WhitePixel(osd->display, osd->screen));
797 XSetBackground(osd->display, osd->mask_gc,
798 BlackPixel(osd->display, osd->screen));
799
800
801 DEBUG(Dtrace, "setting colour");
802 xosd_set_colour(osd, osd_default_colour);
803
804 DEBUG(Dtrace, "stay on top");
805 stay_on_top(osd->display, osd->window);
806
807 DEBUG(Dtrace, "initializing event thread");
808 pthread_create(&osd->event_thread, NULL, event_loop, osd);
809
810 return osd;
811
812 error3:
813 XCloseDisplay(osd->display);
814 error2:
815 free(osd->lines);
816 error1:
817 pthread_cond_destroy(&osd->cond_sync);
818 pthread_cond_destroy(&osd->cond_wait);
819 pthread_mutex_destroy(&osd->mutex_sync);
820 pthread_mutex_destroy(&osd->mutex);
821 close(osd->pipefd[0]);
822 close(osd->pipefd[1]);
823 error0b:
824 free(osd);
825 error0:
826 return NULL;
827 }
828
829 /* }}} */
830
831 /* xosd_uninit -- Destroy a xosd "object" {{{
832 * Deprecated: Use xosd_destroy. */
833 int
xosd_uninit(xosd * osd)834 xosd_uninit(xosd * osd)
835 {
836 FUNCTION_START(Dfunction);
837 return xosd_destroy(osd);
838 }
839
840 /* }}} */
841
842 /* xosd_destroy -- Destroy a xosd "object" {{{ */
843 int
xosd_destroy(xosd * osd)844 xosd_destroy(xosd * osd)
845 {
846 int i;
847
848 FUNCTION_START(Dfunction);
849 if (osd == NULL)
850 return -1;
851
852 DEBUG(Dtrace, "waiting for threads to exit");
853 _xosd_lock(osd);
854 osd->done = 1;
855 _xosd_unlock(osd);
856
857 DEBUG(Dtrace, "join threads");
858 pthread_join(osd->event_thread, NULL);
859
860 DEBUG(Dtrace, "freeing X resources");
861 XFreeGC(osd->display, osd->gc);
862 XFreeGC(osd->display, osd->mask_gc);
863 XFreeGC(osd->display, osd->mask_gc_back);
864 XFreePixmap(osd->display, osd->line_bitmap);
865 XFreeFontSet(osd->display, osd->fontset);
866 XFreePixmap(osd->display, osd->mask_bitmap);
867 XDestroyWindow(osd->display, osd->window);
868
869 XCloseDisplay(osd->display);
870
871 DEBUG(Dtrace, "freeing lines");
872 for (i = 0; i < osd->number_lines; i++)
873 if (osd->lines[i].type == LINE_text && osd->lines[i].text.string)
874 free(osd->lines[i].text.string);
875 free(osd->lines);
876
877 DEBUG(Dtrace, "destroying condition and mutex");
878 pthread_cond_destroy(&osd->cond_sync);
879 pthread_cond_destroy(&osd->cond_wait);
880 pthread_mutex_destroy(&osd->mutex_sync);
881 pthread_mutex_destroy(&osd->mutex);
882 close(osd->pipefd[0]);
883 close(osd->pipefd[1]);
884
885 DEBUG(Dtrace, "freeing osd structure");
886 free(osd);
887
888 FUNCTION_END(Dfunction);
889 return 0;
890 }
891
892 /* }}} */
893
894 /* xosd_set_bar_length -- Set length of percentage and slider bar {{{ */
895 int
xosd_set_bar_length(xosd * osd,int length)896 xosd_set_bar_length(xosd * osd, int length)
897 {
898 FUNCTION_START(Dfunction);
899 if (osd == NULL)
900 return -1;
901
902 if (length == 0)
903 return -1;
904 if (length < -1)
905 return -1;
906
907 osd->bar_length = length;
908
909 return 0;
910 }
911
912 /* }}} */
913
914 /* xosd_display -- Display information {{{ */
915 int
xosd_display(xosd * osd,int line,xosd_command command,...)916 xosd_display(xosd * osd, int line, xosd_command command, ...)
917 {
918 int ret = -1;
919 union xosd_line newline = { type:LINE_blank };
920 va_list a;
921
922 FUNCTION_START(Dfunction);
923 if (osd == NULL)
924 return -1;
925
926 if (line < 0 || line >= osd->number_lines) {
927 xosd_error = "xosd_display: Invalid Line Number";
928 return -1;
929 }
930
931 va_start(a, command);
932 switch (command) {
933 case XOSD_string:
934 case XOSD_printf:
935 {
936 char buf[XOSD_MAX_PRINTF_BUF_SIZE];
937 struct xosd_text *l = &newline.text;
938 char *string = va_arg(a, char *);
939 if (command == XOSD_printf) {
940 if (vsnprintf(buf, sizeof(buf), string, a) >= sizeof(buf)) {
941 xosd_error = "xosd_display: Buffer too small";
942 goto error;
943 }
944 string = buf;
945 }
946 if (string && *string) {
947 ret = strlen(string);
948 l->type = LINE_text;
949 l->string = malloc(ret + 1);
950 memcpy(l->string, string, ret + 1);
951 } else {
952 ret = 0;
953 l->type = LINE_blank;
954 }
955 l->width = -1;
956 break;
957 }
958
959 case XOSD_percentage:
960 case XOSD_slider:
961 {
962 struct xosd_bar *l = &newline.bar;
963 ret = va_arg(a, int);
964 ret = (ret < 0) ? 0 : (ret > 100) ? 100 : ret;
965 l->type = (command == XOSD_percentage) ? LINE_percentage : LINE_slider;
966 l->value = ret;
967 break;
968 }
969
970 default:
971 {
972 xosd_error = "xosd_display: Unknown command";
973 goto error;
974 }
975 }
976
977 _xosd_lock(osd);
978 /* Free old entry */
979 switch (osd->lines[line].type) {
980 case LINE_text:
981 free(osd->lines[line].text.string);
982 case LINE_blank:
983 case LINE_percentage:
984 case LINE_slider:
985 break;
986 }
987 osd->lines[line] = newline;
988 osd->update |= UPD_content | UPD_timer | UPD_show;
989 _xosd_unlock(osd);
990
991 error:
992 va_end(a);
993 return ret;
994 }
995
996 /* }}} */
997
998 /* xosd_is_onscreen -- Returns weather the display is show {{{ */
999 int
xosd_is_onscreen(xosd * osd)1000 xosd_is_onscreen(xosd * osd)
1001 {
1002 FUNCTION_START(Dfunction);
1003 if (osd == NULL)
1004 return -1;
1005 return osd->generation & 1;
1006 }
1007
1008 /* }}} */
1009
1010 /* xosd_wait_until_no_display -- Wait until nothing is displayed {{{ */
1011 int
xosd_wait_until_no_display(xosd * osd)1012 xosd_wait_until_no_display(xosd * osd)
1013 {
1014 int generation;
1015 FUNCTION_START(Dfunction);
1016 if (osd == NULL)
1017 return -1;
1018
1019 if ((generation = osd->generation) & 1)
1020 _wait_until_update(osd, generation);
1021
1022 FUNCTION_END(Dfunction);
1023 return 0;
1024 }
1025
1026 /* }}} */
1027
1028 /* xosd_set_colour -- Change the colour of the display {{{ */
1029 int
xosd_set_colour(xosd * osd,const char * colour)1030 xosd_set_colour(xosd * osd, const char *colour)
1031 {
1032 int retval = 0;
1033
1034 FUNCTION_START(Dfunction);
1035 if (osd == NULL)
1036 return -1;
1037
1038 _xosd_lock(osd);
1039 retval = parse_colour(osd, &osd->colour, &osd->pixel, colour);
1040 osd->update |= UPD_lines;
1041 _xosd_unlock(osd);
1042
1043 return retval;
1044 }
1045
1046 /* }}} */
1047
1048 /* xosd_set_shadow_colour -- Change the colour of the shadow {{{ */
1049 int
xosd_set_shadow_colour(xosd * osd,const char * colour)1050 xosd_set_shadow_colour(xosd * osd, const char *colour)
1051 {
1052 int retval = 0;
1053
1054 FUNCTION_START(Dfunction);
1055 if (osd == NULL)
1056 return -1;
1057
1058 _xosd_lock(osd);
1059 retval = parse_colour(osd, &osd->shadow_colour, &osd->shadow_pixel, colour);
1060 osd->update |= UPD_lines;
1061 _xosd_unlock(osd);
1062
1063 return retval;
1064 }
1065
1066 /* }}} */
1067
1068 /* xosd_set_outline_colour -- Change the colour of the outline {{{ */
1069 int
xosd_set_outline_colour(xosd * osd,const char * colour)1070 xosd_set_outline_colour(xosd * osd, const char *colour)
1071 {
1072 int retval = 0;
1073
1074 FUNCTION_START(Dfunction);
1075 if (osd == NULL)
1076 return -1;
1077
1078 _xosd_lock(osd);
1079 retval =
1080 parse_colour(osd, &osd->outline_colour, &osd->outline_pixel, colour);
1081 osd->update |= UPD_lines;
1082 _xosd_unlock(osd);
1083
1084 return retval;
1085 }
1086
1087 /* }}} */
1088
1089 /* xosd_set_font -- Change the text-display font {{{
1090 * Might return error if fontset can't be created. **/
1091 int
xosd_set_font(xosd * osd,const char * font)1092 xosd_set_font(xosd * osd, const char *font)
1093 {
1094 XFontSet fontset2;
1095 char **missing;
1096 int nmissing;
1097 char *defstr;
1098 int ret = 0;
1099
1100 FUNCTION_START(Dfunction);
1101 if (osd == NULL)
1102 return -1;
1103 if (font == NULL)
1104 return -1;
1105
1106 /*
1107 * Try to create the new font. If it doesn't succeed, keep old font.
1108 */
1109 _xosd_lock(osd);
1110 fontset2 = XCreateFontSet(osd->display, font, &missing, &nmissing, &defstr);
1111 XFreeStringList(missing);
1112 if (fontset2 == NULL) {
1113 xosd_error = "Requested font not found";
1114 ret = -1;
1115 } else {
1116 if (osd->fontset != NULL)
1117 XFreeFontSet(osd->display, osd->fontset);
1118 osd->fontset = fontset2;
1119 osd->update |= UPD_font;
1120 }
1121 _xosd_unlock(osd);
1122
1123 return ret;
1124 }
1125
1126 /* }}} */
1127
1128 /* xosd_set_shadow_offset -- Change the offset of the text shadow {{{ */
1129 int
xosd_set_shadow_offset(xosd * osd,int shadow_offset)1130 xosd_set_shadow_offset(xosd * osd, int shadow_offset)
1131 {
1132 FUNCTION_START(Dfunction);
1133 if (osd == NULL)
1134 return -1;
1135 if (shadow_offset < 0)
1136 return -1;
1137
1138 _xosd_lock(osd);
1139 osd->shadow_offset = shadow_offset;
1140 osd->update |= UPD_font;
1141 _xosd_unlock(osd);
1142
1143 return 0;
1144 }
1145
1146 /* }}} */
1147
1148 /* xosd_set_outline_offset -- Change the offset of the text outline {{{ */
1149 int
xosd_set_outline_offset(xosd * osd,int outline_offset)1150 xosd_set_outline_offset(xosd * osd, int outline_offset)
1151 {
1152 FUNCTION_START(Dfunction);
1153 if (osd == NULL)
1154 return -1;
1155 if (outline_offset < 0)
1156 return -1;
1157
1158 _xosd_lock(osd);
1159 osd->outline_offset = outline_offset;
1160 osd->update |= UPD_font;
1161 _xosd_unlock(osd);
1162
1163 return 0;
1164 }
1165
1166 /* }}} */
1167
1168 /* xosd_set_vertical_offset -- Change the number of pixels the display is offset from the position {{{ */
1169 int
xosd_set_vertical_offset(xosd * osd,int voffset)1170 xosd_set_vertical_offset(xosd * osd, int voffset)
1171 {
1172 FUNCTION_START(Dfunction);
1173 if (osd == NULL)
1174 return -1;
1175
1176 _xosd_lock(osd);
1177 osd->voffset = voffset;
1178 osd->update |= UPD_pos;
1179 _xosd_unlock(osd);
1180
1181 return 0;
1182 }
1183
1184 /* }}} */
1185
1186 /* xosd_set_horizontal_offset -- Change the number of pixels the display is offset from the position {{{ */
1187 int
xosd_set_horizontal_offset(xosd * osd,int hoffset)1188 xosd_set_horizontal_offset(xosd * osd, int hoffset)
1189 {
1190 FUNCTION_START(Dfunction);
1191 if (osd == NULL)
1192 return -1;
1193
1194 _xosd_lock(osd);
1195 osd->hoffset = hoffset;
1196 osd->update |= UPD_pos;
1197 _xosd_unlock(osd);
1198
1199 return 0;
1200 }
1201
1202 /* }}} */
1203
1204 /* xosd_set_pos -- Change the vertical position of the display {{{ */
1205 int
xosd_set_pos(xosd * osd,xosd_pos pos)1206 xosd_set_pos(xosd * osd, xosd_pos pos)
1207 {
1208 FUNCTION_START(Dfunction);
1209 if (osd == NULL)
1210 return -1;
1211
1212 _xosd_lock(osd);
1213 osd->pos = pos;
1214 osd->update |= UPD_pos;
1215 _xosd_unlock(osd);
1216
1217 return 0;
1218 }
1219
1220 /* }}} */
1221
1222 /* xosd_set_align -- Change the horizontal alignment of the display {{{ */
1223 int
xosd_set_align(xosd * osd,xosd_align align)1224 xosd_set_align(xosd * osd, xosd_align align)
1225 {
1226 FUNCTION_START(Dfunction);
1227 if (osd == NULL)
1228 return -1;
1229
1230 _xosd_lock(osd);
1231 osd->align = align;
1232 osd->update |= UPD_content; /* XOSD_right depends on text width */
1233 _xosd_unlock(osd);
1234
1235 return 0;
1236 }
1237
1238 /* }}} */
1239
1240 /* xosd_get_colour -- Gets the RGB value of the display's colour {{{ */
1241 int
xosd_get_colour(xosd * osd,int * red,int * green,int * blue)1242 xosd_get_colour(xosd * osd, int *red, int *green, int *blue)
1243 {
1244 FUNCTION_START(Dfunction);
1245 if (osd == NULL)
1246 return -1;
1247
1248 if (red)
1249 *red = osd->colour.red;
1250 if (blue)
1251 *blue = osd->colour.blue;
1252 if (green)
1253 *green = osd->colour.green;
1254
1255 return 0;
1256 }
1257
1258 /* }}} */
1259
1260 /* xosd_set_timeout -- Change the time before display is hidden. {{{ */
1261 int
xosd_set_timeout(xosd * osd,int timeout)1262 xosd_set_timeout(xosd * osd, int timeout)
1263 {
1264 FUNCTION_START(Dfunction);
1265 if (osd == NULL)
1266 return -1;
1267 _xosd_lock(osd);
1268 osd->timeout = timeout;
1269 osd->update |= UPD_timer;
1270 _xosd_unlock(osd);
1271 return 0;
1272 }
1273
1274 /* }}} */
1275
1276 /* xosd_hide -- hide the display {{{ */
1277 int
xosd_hide(xosd * osd)1278 xosd_hide(xosd * osd)
1279 {
1280 FUNCTION_START(Dfunction);
1281 if (osd == NULL)
1282 return -1;
1283
1284 if (osd->generation & 1) {
1285 _xosd_lock(osd);
1286 osd->update &= ~UPD_show;
1287 osd->update |= UPD_hide;
1288 _xosd_unlock(osd);
1289 return 0;
1290 }
1291 return -1;
1292 }
1293
1294 /* }}} */
1295
1296 /* xosd_show -- Show the display after being hidden {{{ */
1297 int
xosd_show(xosd * osd)1298 xosd_show(xosd * osd)
1299 {
1300 FUNCTION_START(Dfunction);
1301 if (osd == NULL)
1302 return -1;
1303
1304 if (~osd->generation & 1) {
1305 _xosd_lock(osd);
1306 osd->update &= ~UPD_hide;
1307 osd->update |= UPD_show | UPD_timer;
1308 _xosd_unlock(osd);
1309 return 0;
1310 }
1311 return -1;
1312 }
1313
1314 /* }}} */
1315
1316 /* xosd_scroll -- Scroll the display up "lines" number of lines {{{ */
1317 int
xosd_scroll(xosd * osd,int lines)1318 xosd_scroll(xosd * osd, int lines)
1319 {
1320 int i;
1321 union xosd_line *src, *dst;
1322
1323 FUNCTION_START(Dfunction);
1324 if (osd == NULL)
1325 return -1;
1326 if (lines <= 0 || lines > osd->number_lines)
1327 return -1;
1328
1329 _xosd_lock(osd);
1330 /* Clear old text */
1331 for (i = 0, src = osd->lines; i < lines; i++, src++)
1332 if (src->type == LINE_text && src->text.string) {
1333 free(src->text.string);
1334 src->text.string = NULL;
1335 }
1336 /* Move following lines forward */
1337 for (dst = osd->lines; i < osd->number_lines; i++)
1338 *dst++ = *src++;
1339 /* Blank new lines */
1340 for (; dst < src; dst++) {
1341 dst->type = LINE_blank;
1342 dst->text.string = NULL;
1343 }
1344 osd->update |= UPD_content;
1345 _xosd_unlock(osd);
1346 return 0;
1347 }
1348
1349 /* }}} */
1350
1351 /* xosd_get_number_lines -- Get the maximum number of lines allowed {{{ */
1352 int
xosd_get_number_lines(xosd * osd)1353 xosd_get_number_lines(xosd * osd)
1354 {
1355 FUNCTION_START(Dfunction);
1356 if (osd == NULL)
1357 return -1;
1358
1359 return osd->number_lines;
1360 }
1361
1362 /* }}} */
1363
1364 /* vim: foldmethod=marker tabstop=2 shiftwidth=2 expandtab
1365 */
1366