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