1 /*
2 * Copyright (C) 2003-2020 the xine project
3 *
4 * This file is part of xine, a free video player.
5 *
6 * xine is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * xine is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19 *
20 * x11osd.c, use X11 Nonrectangular Window Shape Extension to draw xine OSD
21 *
22 * Nov 2003 - Miguel Freitas
23 *
24 * based on ideas and code of
25 * xosd Copyright (c) 2000 Andre Renaud (andre@ignavus.net)
26 *
27 * colorkey support by Yann Vernier
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <signal.h>
40 #include <time.h>
41
42 #include <netinet/in.h>
43
44 #include <X11/Xlib.h>
45 #include <X11/Xutil.h>
46 #include <X11/extensions/shape.h>
47 #include <X11/Xatom.h>
48
49 #define LOG_MODULE "x11osd"
50 #define LOG_VERBOSE
51
52 /*
53 #define LOG
54 */
55
56 #include <xine/xine_internal.h>
57 #include "x11osd.h"
58
59 struct x11osd
60 {
61 Display *display;
62 int screen;
63 enum x11osd_mode mode;
64
65 union {
66 struct {
67 Window window;
68 Pixmap mask_bitmap;
69 GC mask_gc;
70 GC mask_gc_back;
71 int mapped;
72 } shaped;
73 struct {
74 uint32_t colorkey;
75 vo_scale_t *sc;
76 } colorkey;
77 } u;
78 Window window;
79 unsigned int depth;
80 Pixmap bitmap;
81 Visual *visual;
82 Colormap cmap;
83
84 GC gc;
85
86 int width;
87 int height;
88 int x;
89 int y;
90 enum {DRAWN, WIPED, UNDEFINED} clean;
91 xine_t *xine;
92 };
93
94
95 void
x11osd_expose(x11osd * osd)96 x11osd_expose (x11osd * osd)
97 {
98 _x_assert (osd);
99
100 lprintf("expose (state:%d)\n", osd->clean );
101
102 switch (osd->mode) {
103 case X11OSD_SHAPED:
104 XShapeCombineMask (osd->display, osd->u.shaped.window, ShapeBounding, 0, 0,
105 osd->u.shaped.mask_bitmap, ShapeSet);
106 if( osd->clean==DRAWN ) {
107
108 if( !osd->u.shaped.mapped )
109 XMapRaised (osd->display, osd->u.shaped.window);
110 osd->u.shaped.mapped = 1;
111
112 XCopyArea (osd->display, osd->bitmap, osd->u.shaped.window, osd->gc, 0, 0,
113 osd->width, osd->height, 0, 0);
114 } else {
115 if( osd->u.shaped.mapped )
116 XUnmapWindow (osd->display, osd->u.shaped.window);
117 osd->u.shaped.mapped = 0;
118 }
119 break;
120 case X11OSD_COLORKEY:
121 if( osd->clean!=UNDEFINED )
122 XCopyArea (osd->display, osd->bitmap, osd->window, osd->gc, 0, 0,
123 osd->width, osd->height, 0, 0);
124 }
125 }
126
127
128 void
x11osd_resize(x11osd * osd,int width,int height)129 x11osd_resize (x11osd * osd, int width, int height)
130 {
131 _x_assert (osd);
132 _x_assert (width);
133 _x_assert (height);
134
135 lprintf("resize old:%dx%d new:%dx%d\n", osd->width, osd->height, width, height );
136
137 osd->width = width;
138 osd->height = height;
139
140 XFreePixmap (osd->display, osd->bitmap);
141 switch(osd->mode) {
142 case X11OSD_SHAPED:
143 XResizeWindow (osd->display, osd->u.shaped.window, osd->width, osd->height);
144 XFreePixmap (osd->display, osd->u.shaped.mask_bitmap);
145 osd->u.shaped.mask_bitmap =
146 XCreatePixmap (osd->display, osd->u.shaped.window, osd->width, osd->height,
147 1);
148 osd->bitmap =
149 XCreatePixmap (osd->display, osd->u.shaped.window,
150 osd->width, osd->height, osd->depth);
151 break;
152 case X11OSD_COLORKEY:
153 osd->bitmap =
154 XCreatePixmap (osd->display, osd->window,
155 osd->width, osd->height, osd->depth);
156 break;
157 }
158
159 osd->clean = UNDEFINED;
160 x11osd_clear(osd);
161 }
162
163 void
x11osd_drawable_changed(x11osd * osd,Window window)164 x11osd_drawable_changed (x11osd * osd, Window window)
165 {
166 XSetWindowAttributes attr;
167 XWindowAttributes getattr;
168
169 _x_assert (osd);
170
171 lprintf("drawable changed\n");
172
173 /*
174 Do I need to recreate the GC's??
175
176 XFreeGC (osd->display, osd->gc);
177 XFreeGC (osd->display, osd->mask_gc);
178 XFreeGC (osd->display, osd->mask_gc_back);
179 */
180 XFreePixmap (osd->display, osd->bitmap);
181 XFreeColormap (osd->display, osd->cmap);
182
183 /* we need to call XSync(), because otherwise, calling XDestroyWindow()
184 on the parent window could destroy our OSD window twice !! */
185 XSync (osd->display, False);
186
187 osd->window = window;
188
189 XGetWindowAttributes (osd->display, osd->window, &getattr);
190 osd->width = getattr.width;
191 osd->height = getattr.height;
192
193 _x_assert(osd->width);
194 _x_assert(osd->height);
195
196 switch(osd->mode) {
197 case X11OSD_SHAPED:
198 XFreePixmap (osd->display, osd->u.shaped.mask_bitmap);
199 XDestroyWindow (osd->display, osd->u.shaped.window);
200
201 attr.override_redirect = True;
202 attr.background_pixel = BlackPixel (osd->display, osd->screen);
203 osd->u.shaped.window = XCreateWindow(osd->display, osd->window,
204 0, 0, osd->width, osd->height, 0,
205 CopyFromParent, CopyFromParent, CopyFromParent,
206 CWBackPixel | CWOverrideRedirect, &attr);
207
208 XSelectInput (osd->display, osd->u.shaped.window, ExposureMask);
209 osd->u.shaped.mapped = 0;
210
211 osd->u.shaped.mask_bitmap = XCreatePixmap (osd->display, osd->u.shaped.window,
212 osd->width, osd->height, 1);
213
214 osd->bitmap = XCreatePixmap (osd->display, osd->u.shaped.window, osd->width,
215 osd->height, osd->depth);
216
217 osd->cmap = XCreateColormap(osd->display, osd->u.shaped.window,
218 osd->visual, AllocNone);
219 break;
220 case X11OSD_COLORKEY:
221 osd->bitmap = XCreatePixmap (osd->display, osd->window, osd->width,
222 osd->height, osd->depth);
223 osd->cmap = XCreateColormap(osd->display, osd->window,
224 osd->visual, AllocNone);
225
226 break;
227 }
228
229 osd->clean = UNDEFINED;
230 /* do not x11osd_clear() here: osd->u.colorkey.sc has not being updated yet */
231 }
232
233 static int x11_error = False ;
234
x11_error_handler(Display * dpy,XErrorEvent * error)235 static int x11_error_handler(Display *dpy, XErrorEvent *error)
236 {
237 (void)dpy;
238 (void)error;
239 x11_error = True;
240 return 0;
241 }
242
243 x11osd *
x11osd_create(xine_t * xine,Display * display,int screen,Window window,enum x11osd_mode mode)244 x11osd_create (xine_t *xine, Display *display, int screen, Window window, enum x11osd_mode mode)
245 {
246 x11osd *osd;
247 int event_basep, error_basep;
248 XErrorHandler old_handler = NULL;
249 XSetWindowAttributes attr;
250 XWindowAttributes getattr;
251
252 osd = calloc(1, sizeof(x11osd));
253 if (!osd)
254 return NULL;
255
256 osd->mode = mode;
257 osd->xine = xine;
258 osd->display = display;
259 osd->screen = screen;
260 osd->window = window;
261
262 x11_error = False;
263 old_handler = XSetErrorHandler(x11_error_handler);
264
265 osd->visual = DefaultVisual (osd->display, osd->screen);
266 osd->depth = DefaultDepth (osd->display, osd->screen);
267
268 XGetWindowAttributes (osd->display, osd->window, &getattr);
269 osd->width = getattr.width;
270 osd->height = getattr.height;
271
272 _x_assert(osd->width);
273 _x_assert(osd->height);
274
275 switch (mode) {
276 case X11OSD_SHAPED:
277 if (!XShapeQueryExtension (osd->display, &event_basep, &error_basep)) {
278 xprintf(osd->xine, XINE_VERBOSITY_LOG, _("x11osd: XShape extension not available. unscaled overlay disabled.\n"));
279 goto error2;
280 }
281
282 attr.override_redirect = True;
283 attr.background_pixel = BlackPixel (osd->display, osd->screen);
284 osd->u.shaped.window = XCreateWindow(osd->display, osd->window,
285 0, 0, osd->width, osd->height, 0,
286 CopyFromParent, CopyFromParent, CopyFromParent,
287 CWBackPixel | CWOverrideRedirect, &attr);
288
289 XSync(osd->display, False);
290 if( x11_error ) {
291 xprintf(osd->xine, XINE_VERBOSITY_LOG, _("x11osd: error creating window. unscaled overlay disabled.\n"));
292 goto error_window;
293 }
294
295 osd->u.shaped.mask_bitmap = XCreatePixmap (osd->display, osd->u.shaped.window, osd->width,
296 osd->height, 1);
297 XSync(osd->display, False);
298 if( x11_error ) {
299 xprintf(osd->xine, XINE_VERBOSITY_LOG, _("x11osd: error creating pixmap. unscaled overlay disabled.\n"));
300 goto error_aftermaskbitmap;
301 }
302
303 osd->bitmap = XCreatePixmap (osd->display, osd->u.shaped.window, osd->width,
304 osd->height, osd->depth);
305 osd->gc = XCreateGC (osd->display, osd->u.shaped.window, 0, NULL);
306
307 osd->u.shaped.mask_gc = XCreateGC (osd->display, osd->u.shaped.mask_bitmap, 0, NULL);
308 XSetForeground (osd->display, osd->u.shaped.mask_gc,
309 WhitePixel (osd->display, osd->screen));
310 XSetBackground (osd->display, osd->u.shaped.mask_gc,
311 BlackPixel (osd->display, osd->screen));
312
313
314 osd->u.shaped.mask_gc_back = XCreateGC (osd->display, osd->u.shaped.mask_bitmap, 0, NULL);
315 XSetForeground (osd->display, osd->u.shaped.mask_gc_back,
316 BlackPixel (osd->display, osd->screen));
317 XSetBackground (osd->display, osd->u.shaped.mask_gc_back,
318 WhitePixel (osd->display, osd->screen));
319
320 XSelectInput (osd->display, osd->u.shaped.window, ExposureMask);
321 osd->u.shaped.mapped = 0;
322 osd->cmap = XCreateColormap(osd->display, osd->u.shaped.window,
323 osd->visual, AllocNone);
324 break;
325 case X11OSD_COLORKEY:
326 osd->bitmap = XCreatePixmap (osd->display, osd->window, osd->width,
327 osd->height, osd->depth);
328 osd->gc = XCreateGC (osd->display, osd->window, 0, NULL);
329 osd->cmap = XCreateColormap(osd->display, osd->window,
330 osd->visual, AllocNone);
331 /* FIXME: the expose event doesn't seem to happen? */
332 /*XSelectInput (osd->display, osd->window, ExposureMask);*/
333 break;
334 default:
335 goto error2;
336 }
337
338 XSync(osd->display, False);
339 if( x11_error ) {
340 xprintf(osd->xine, XINE_VERBOSITY_LOG, _("x11osd: error creating pixmap. unscaled overlay disabled.\n"));
341 goto error_pixmap;
342 }
343
344 osd->clean = UNDEFINED;
345 x11osd_expose(osd);
346
347 XSetErrorHandler(old_handler);
348
349 xprintf(osd->xine, XINE_VERBOSITY_DEBUG,
350 _("x11osd: unscaled overlay created (%s mode).\n"),
351 (mode==X11OSD_SHAPED) ? "XShape" : "Colorkey" );
352
353 return osd;
354
355 /*
356 XFreeGC (osd->display, osd->gc);
357 XFreeGC (osd->display, osd->mask_gc);
358 XFreeGC (osd->display, osd->mask_gc_back);
359 */
360
361 error_pixmap:
362 XFreePixmap (osd->display, osd->bitmap);
363 error_aftermaskbitmap:
364 if(mode==X11OSD_SHAPED)
365 XFreePixmap (osd->display, osd->u.shaped.mask_bitmap);
366 error_window:
367 if(mode==X11OSD_SHAPED)
368 XDestroyWindow (osd->display, osd->u.shaped.window);
369 XSetErrorHandler(old_handler);
370 error2:
371 free (osd);
372 return NULL;
373 }
374
x11osd_colorkey(x11osd * osd,uint32_t colorkey,vo_scale_t * sc)375 void x11osd_colorkey(x11osd * osd, uint32_t colorkey, vo_scale_t *sc)
376 {
377 _x_assert (osd);
378 _x_assert (osd->mode==X11OSD_COLORKEY);
379
380 osd->u.colorkey.colorkey=colorkey;
381 osd->u.colorkey.sc=sc;
382 osd->clean = UNDEFINED;
383 x11osd_clear(osd);
384 x11osd_expose(osd);
385 }
386
387 void
x11osd_destroy(x11osd * osd)388 x11osd_destroy (x11osd * osd)
389 {
390
391 _x_assert (osd);
392
393 XFreeGC (osd->display, osd->gc);
394 XFreePixmap (osd->display, osd->bitmap);
395 XFreeColormap (osd->display, osd->cmap);
396 if(osd->mode==X11OSD_SHAPED) {
397 XFreeGC (osd->display, osd->u.shaped.mask_gc);
398 XFreeGC (osd->display, osd->u.shaped.mask_gc_back);
399 XFreePixmap (osd->display, osd->u.shaped.mask_bitmap);
400 XDestroyWindow (osd->display, osd->u.shaped.window);
401 }
402
403 free (osd);
404 }
405
x11osd_clear(x11osd * osd)406 void x11osd_clear(x11osd *osd)
407 {
408 int i;
409
410 lprintf("clear (state:%d)\n", osd->clean );
411
412 if( osd->clean != WIPED )
413 switch (osd->mode) {
414 case X11OSD_SHAPED:
415 XFillRectangle (osd->display, osd->u.shaped.mask_bitmap, osd->u.shaped.mask_gc_back,
416 0, 0, osd->width, osd->height);
417 break;
418 case X11OSD_COLORKEY:
419 XSetForeground(osd->display, osd->gc, osd->u.colorkey.colorkey);
420 if(osd->u.colorkey.sc) {
421 XFillRectangle (osd->display, osd->bitmap, osd->gc,
422 osd->u.colorkey.sc->output_xoffset,
423 osd->u.colorkey.sc->output_yoffset,
424 osd->u.colorkey.sc->output_width,
425 osd->u.colorkey.sc->output_height);
426 XSetForeground(osd->display, osd->gc, BlackPixel(osd->display, osd->screen));
427 for( i = 0; i < 4; i++ ) {
428 if( osd->u.colorkey.sc->border[i].w && osd->u.colorkey.sc->border[i].h ) {
429 XFillRectangle(osd->display, osd->bitmap, osd->gc,
430 osd->u.colorkey.sc->border[i].x, osd->u.colorkey.sc->border[i].y,
431 osd->u.colorkey.sc->border[i].w, osd->u.colorkey.sc->border[i].h);
432 }
433 }
434 } else
435 XFillRectangle (osd->display, osd->bitmap, osd->gc, 0, 0, osd->width, osd->height);
436 break;
437 }
438 osd->clean = WIPED;
439 }
440
441 #define X11OSD_TRANSPARENT 0xffffffff
442
443 #define saturate(n, l, u) ((n) < (l) ? (l) : ((n) > (u) ? (u) : (n)))
444
x11osd_blend(x11osd * osd,vo_overlay_t * overlay)445 void x11osd_blend(x11osd *osd, vo_overlay_t *overlay)
446 {
447 if (osd->clean==UNDEFINED)
448 x11osd_clear(osd); /* Workaround. Colorkey mode needs sc data before the clear. */
449
450 if (overlay->rle) {
451 int i, x, y, len, width;
452 int use_clip_palette, max_palette_colour[2];
453 uint32_t palette[2][OVL_PALETTE_SIZE];
454
455 max_palette_colour[0] = -1;
456 max_palette_colour[1] = -1;
457
458 for (i=0, x=0, y=0; i<overlay->num_rle; i++) {
459 len = overlay->rle[i].len;
460
461 while (len > 0) {
462 use_clip_palette = 0;
463 if (len > overlay->width) {
464 width = overlay->width;
465 len -= overlay->width;
466 }
467 else {
468 width = len;
469 len = 0;
470 }
471 if ((y >= overlay->hili_top) && (y <= overlay->hili_bottom) && (x <= overlay->hili_right)) {
472 if ((x < overlay->hili_left) && (x + width - 1 >= overlay->hili_left)) {
473 width -= overlay->hili_left - x;
474 len += overlay->hili_left - x;
475 }
476 else if (x > overlay->hili_left) {
477 use_clip_palette = 1;
478 if (x + width - 1 > overlay->hili_right) {
479 width -= overlay->hili_right - x;
480 len += overlay->hili_right - x;
481 }
482 }
483 }
484
485 if (overlay->rle[i].color > max_palette_colour[use_clip_palette]) {
486 int j;
487 uint32_t *src_color;
488 uint8_t *src_trans;
489
490 if (use_clip_palette) {
491 src_color = overlay->hili_color;
492 src_trans = (uint8_t *)&overlay->hili_trans;
493 }
494 else {
495 src_color = overlay->color;
496 src_trans = (uint8_t *)&overlay->trans;
497 }
498 for (j=max_palette_colour[use_clip_palette]+1; j<=overlay->rle[i].color; j++) {
499 if (src_trans[j]) {
500 union {
501 uint32_t u32;
502 clut_t c;
503 } tmp = { src_color[j] };
504
505 if (1) {
506 XColor xcolor;
507 int y, u, v, r, g, b;
508 y = saturate(tmp.c.y, 16, 235);
509 u = saturate(tmp.c.cb, 16, 240);
510 v = saturate(tmp.c.cr, 16, 240);
511 y = (9 * y) / 8;
512 r = y + (25 * v) / 16 - 218;
513 xcolor.red = (65536 * saturate(r, 0, 255)) / 256;
514 g = y + (-13 * v) / 16 + (-25 * u) / 64 + 136;
515 xcolor.green = (65536 * saturate(g, 0, 255)) / 256;
516 b = y + 2 * u - 274;
517 xcolor.blue = (65536 * saturate(b, 0, 255)) / 256;
518
519 xcolor.flags = DoRed | DoBlue | DoGreen;
520
521 XAllocColor(osd->display, osd->cmap, &xcolor);
522
523 palette[use_clip_palette][j] = xcolor.pixel;
524 }
525 else {
526 if (tmp.c.y > 127) {
527 palette[use_clip_palette][j] = WhitePixel(osd->display, osd->screen);
528 }
529 else {
530 palette[use_clip_palette][j] = BlackPixel(osd->display, osd->screen);
531 }
532 }
533 }
534 else {
535 palette[use_clip_palette][j] = X11OSD_TRANSPARENT;
536 }
537 }
538 max_palette_colour[use_clip_palette] = overlay->rle[i].color;
539 }
540
541 if(palette[use_clip_palette][overlay->rle[i].color] != X11OSD_TRANSPARENT) {
542 XSetForeground(osd->display, osd->gc, palette[use_clip_palette][overlay->rle[i].color]);
543 XFillRectangle(osd->display, osd->bitmap, osd->gc, overlay->x + x, overlay->y + y, width, 1);
544 if(osd->mode==X11OSD_SHAPED)
545 XFillRectangle(osd->display, osd->u.shaped.mask_bitmap, osd->u.shaped.mask_gc, overlay->x + x, overlay->y + y, width, 1);
546 }
547
548 x += width;
549 if (x == overlay->width) {
550 x = 0;
551 y++;
552 }
553 }
554 }
555 osd->clean = DRAWN;
556 }
557 }
558
559