1 /*****************************************************************
2  * gmerlin - a general purpose multimedia framework and applications
3  *
4  * Copyright (c) 2001 - 2011 Members of the Gmerlin project
5  * gmerlin-general@lists.sourceforge.net
6  * http://gmerlin.sourceforge.net
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  * *****************************************************************/
21 
22 #include <config.h>
23 #include <stdio.h>
24 #include <string.h>
25 
26 #include <stdlib.h>
27 
28 #include <time.h>
29 #include <sys/time.h>
30 
31 #include <X11/Xatom.h>
32 
33 #include <gmerlin/translation.h>
34 #include <gmerlin/utils.h>
35 #include <gmerlin/log.h>
36 #define LOG_DOMAIN "x11"
37 
38 #include <x11/x11.h>
39 #include <x11/x11_window_private.h>
40 
41 #ifdef HAVE_GLX
42 #include <GL/glx.h>
43 #endif
44 
45 #define _NET_WM_STATE_REMOVE        0    /* remove/unset property */
46 #define _NET_WM_STATE_ADD           1    /* add/set property */
47 
48 // #define FULLSCREEN_MODE_OLD    0
49 #define FULLSCREEN_MODE_NET_FULLSCREEN   (1<<0)
50 #define FULLSCREEN_MODE_NET_ABOVE        (1<<1)
51 #define FULLSCREEN_MODE_NET_STAYS_ON_TOP (1<<2)
52 
53 #define FULLSCREEN_MODE_WIN_LAYER      (1<<2)
54 
55 #define HAVE_XSHM
56 
57 
58 static char bm_no_data[] = { 0,0,0,0, 0,0,0,0 };
59 
60 /* since it doesn't seem to be defined on some platforms */
61 int XShmGetEventBase (Display *);
62 
63 /* Screensaver detection */
64 
check_disable_screensaver(bg_x11_window_t * w)65 static int check_disable_screensaver(bg_x11_window_t * w)
66   {
67   if(!w->current)
68     return 0;
69   /* Never change global settings if we are embedded */
70   if(w->current->parent != w->root)
71     return 0;
72 
73   if(!w->is_fullscreen)
74     return w->disable_screensaver_normal;
75   else
76     return w->disable_screensaver_fullscreen;
77   }
78 
bg_x11_window_ping_screensaver(bg_x11_window_t * w)79 void bg_x11_window_ping_screensaver(bg_x11_window_t * w)
80   {
81   }
82 
83 
84 
85 static int
wm_check_capability(Display * dpy,Window root,Atom list,Atom wanted)86 wm_check_capability(Display *dpy, Window root, Atom list, Atom wanted)
87   {
88   Atom            type;
89   int             format;
90   unsigned int    i;
91   unsigned long   nitems, bytesafter;
92   unsigned char   *args;
93   unsigned long   *ldata;
94   int             retval = 0;
95 
96   if (Success != XGetWindowProperty
97       (dpy, root, list, 0, 16384, False,
98        AnyPropertyType, &type, &format, &nitems, &bytesafter, &args))
99     return 0;
100   if (type != XA_ATOM)
101     return 0;
102   ldata = (unsigned long*)args;
103   for (i = 0; i < nitems; i++)
104     {
105     if (ldata[i] == wanted)
106       retval = 1;
107 
108     }
109   XFree(ldata);
110   return retval;
111   }
112 
113 void
bg_x11_window_set_netwm_state(Display * dpy,Window win,Window root,int action,Atom state)114 bg_x11_window_set_netwm_state(Display * dpy, Window win, Window root, int action, Atom state)
115   {
116   /* Setting _NET_WM_STATE by XSendEvent works only, if the window
117      is already mapped!! */
118 
119   XEvent e;
120   memset(&e,0,sizeof(e));
121   e.xclient.type = ClientMessage;
122   e.xclient.message_type = XInternAtom(dpy, "_NET_WM_STATE", False);
123   e.xclient.window = win;
124   e.xclient.send_event = True;
125   e.xclient.format = 32;
126   e.xclient.data.l[0] = action;
127   e.xclient.data.l[1] = state;
128 
129   XSendEvent(dpy, root, False,
130              SubstructureRedirectMask | SubstructureNotifyMask, &e);
131   }
132 
get_fullscreen_mode(bg_x11_window_t * w)133 static int get_fullscreen_mode(bg_x11_window_t * w)
134   {
135   int ret = 0;
136 
137   Atom            type;
138   int             format;
139   unsigned int    i;
140   unsigned long   nitems, bytesafter;
141   unsigned char   *args;
142   unsigned long   *ldata;
143   if (Success != XGetWindowProperty
144       (w->dpy, w->root, w->_NET_SUPPORTED, 0, (65536 / sizeof(long)), False,
145        AnyPropertyType, &type, &format, &nitems, &bytesafter, &args))
146     return 0;
147   if (type != XA_ATOM)
148     return 0;
149   ldata = (unsigned long*)args;
150   for (i = 0; i < nitems; i++)
151     {
152     if (ldata[i] == w->_NET_WM_STATE_FULLSCREEN)
153       {
154       ret |= FULLSCREEN_MODE_NET_FULLSCREEN;
155       }
156     if (ldata[i] == w->_NET_WM_STATE_ABOVE)
157       {
158       ret |= FULLSCREEN_MODE_NET_ABOVE;
159       }
160     if (ldata[i] == w->_NET_WM_STATE_STAYS_ON_TOP)
161       {
162       ret |= FULLSCREEN_MODE_NET_STAYS_ON_TOP;
163       }
164     }
165   XFree(ldata);
166 
167   if(wm_check_capability(w->dpy, w->root, w->WIN_PROTOCOLS,
168                          w->WIN_LAYER))
169     {
170     ret |= FULLSCREEN_MODE_WIN_LAYER;
171     }
172 
173   return ret;
174   }
175 
176 #define INIT_ATOM(a) w->a = XInternAtom(w->dpy, #a, False);
177 
init_atoms(bg_x11_window_t * w)178 static void init_atoms(bg_x11_window_t * w)
179   {
180   INIT_ATOM(WM_DELETE_WINDOW);
181   INIT_ATOM(WM_TAKE_FOCUS);
182   INIT_ATOM(WIN_PROTOCOLS);
183   INIT_ATOM(WM_PROTOCOLS);
184   INIT_ATOM(WIN_LAYER);
185   INIT_ATOM(_NET_SUPPORTED);
186   INIT_ATOM(_NET_WM_STATE);
187   INIT_ATOM(_NET_WM_STATE_FULLSCREEN);
188   INIT_ATOM(_NET_WM_STATE_ABOVE);
189   INIT_ATOM(_NET_WM_STATE_STAYS_ON_TOP);
190   INIT_ATOM(_NET_MOVERESIZE_WINDOW);
191   INIT_ATOM(_XEMBED_INFO);
192   INIT_ATOM(_XEMBED);
193   INIT_ATOM(WM_CLASS);
194   INIT_ATOM(STRING);
195   }
196 
bg_x11_window_set_fullscreen_mapped(bg_x11_window_t * win,window_t * w)197 void bg_x11_window_set_fullscreen_mapped(bg_x11_window_t * win,
198                                          window_t * w)
199   {
200   if(win->fullscreen_mode & FULLSCREEN_MODE_NET_ABOVE)
201     {
202     bg_x11_window_set_netwm_state(win->dpy, w->win, win->root,
203                                   _NET_WM_STATE_ADD, win->_NET_WM_STATE_ABOVE);
204     }
205   else if(win->fullscreen_mode & FULLSCREEN_MODE_NET_STAYS_ON_TOP)
206     {
207     bg_x11_window_set_netwm_state(win->dpy, w->win, win->root,
208                                   _NET_WM_STATE_ADD, win->_NET_WM_STATE_STAYS_ON_TOP);
209     }
210   if(win->fullscreen_mode & FULLSCREEN_MODE_NET_FULLSCREEN)
211     {
212     bg_x11_window_set_netwm_state(win->dpy, w->win, win->root,
213                                   _NET_WM_STATE_ADD, win->_NET_WM_STATE_FULLSCREEN);
214     }
215   }
216 
show_window(bg_x11_window_t * win,window_t * w,int show)217 static void show_window(bg_x11_window_t * win, window_t * w, int show)
218   {
219   unsigned long buffer[2];
220 
221   if(!w)
222     return;
223 
224   if(!show)
225     {
226 
227     if(w->win == None)
228       return;
229 
230     if(w->win == w->toplevel)
231       {
232       XUnmapWindow(win->dpy, w->win);
233       XWithdrawWindow(win->dpy, w->win,
234                       DefaultScreen(win->dpy));
235       }
236     else if(w->parent_xembed)
237       {
238       buffer[0] = 0; // Version
239       buffer[1] = 0;
240 
241       XChangeProperty(win->dpy,
242                       w->win,
243                       win->_XEMBED_INFO,
244                       win->_XEMBED_INFO, 32,
245                       PropModeReplace,
246                       (unsigned char *)buffer, 2);
247       return;
248       }
249     else
250       XUnmapWindow(win->dpy, w->win);
251     XSync(win->dpy, False);
252     return;
253     }
254 
255   /* Do it the XEMBED way */
256   if(w->parent_xembed)
257     {
258     buffer[0] = 0; // Version
259     buffer[1] = XEMBED_MAPPED;
260 
261     XChangeProperty(win->dpy,
262                     w->win,
263                     win->_XEMBED_INFO,
264                     win->_XEMBED_INFO, 32,
265                     PropModeReplace,
266                     (unsigned char *)buffer, 2);
267     return;
268     }
269 
270   /* If the window was already mapped, raise it */
271   if(w->mapped)
272     XRaiseWindow(win->dpy, w->win);
273   else
274     XMapWindow(win->dpy, w->win);
275 
276   if(w->win == w->toplevel)
277     {
278     if(!w->fullscreen)
279       XMoveResizeWindow(win->dpy, w->win,
280                         win->window_x, win->window_y,
281                         win->window_width, win->window_height);
282     }
283   }
284 
bg_x11_window_clear(bg_x11_window_t * win)285 void bg_x11_window_clear(bg_x11_window_t * win)
286   {
287 //  XSetForeground(win->dpy, win->gc, win->black);
288 //  XFillRectangle(win->dpy, win->normal_window, win->gc, 0, 0, win->window_width,
289 //                 win->window_height);
290   if(win->normal.win != None)
291     XClearArea(win->dpy, win->normal.win, 0, 0,
292                win->window_width, win->window_height, True);
293 
294   if(win->fullscreen.win != None)
295     XClearArea(win->dpy, win->fullscreen.win, 0, 0,
296                win->window_width, win->window_height, True);
297 
298   //   XSync(win->dpy, False);
299   }
300 
301 
302 
303 /* MWM decorations */
304 
305 #define MWM_HINTS_DECORATIONS (1L << 1)
306 #define MWM_HINTS_FUNCTIONS     (1L << 0)
307 
308 #define MWM_FUNC_ALL                 (1L<<0)
309 #define PROP_MOTIF_WM_HINTS_ELEMENTS 5
310 typedef struct
311   {
312   CARD32 flags;
313   CARD32 functions;
314   CARD32 decorations;
315   INT32 inputMode;
316     CARD32 status;
317   } PropMotifWmHints;
318 
319 static
mwm_set_decorations(bg_x11_window_t * w,Window win,int set)320 int mwm_set_decorations(bg_x11_window_t * w, Window win, int set)
321   {
322   PropMotifWmHints motif_hints;
323   Atom hintsatom;
324 
325   /* setup the property */
326   motif_hints.flags = MWM_HINTS_DECORATIONS | MWM_HINTS_FUNCTIONS;
327   motif_hints.decorations = set;
328   motif_hints.functions   = set ? MWM_FUNC_ALL : 0;
329 
330   /* get the atom for the property */
331   hintsatom = XInternAtom(w->dpy, "_MOTIF_WM_HINTS", False);
332 
333   XChangeProperty(w->dpy, win, hintsatom, hintsatom, 32, PropModeReplace,
334                   (unsigned char *) &motif_hints, PROP_MOTIF_WM_HINTS_ELEMENTS);
335   return 1;
336   }
337 
set_fullscreen(bg_x11_window_t * w,Window win)338 static void set_fullscreen(bg_x11_window_t * w, Window win)
339   {
340   if(w->fullscreen_mode & FULLSCREEN_MODE_NET_FULLSCREEN)
341     {
342 
343     }
344   else
345     mwm_set_decorations(w, win, 0);
346   }
347 
set_min_size(bg_x11_window_t * w,Window win,int width,int height)348 static void set_min_size(bg_x11_window_t * w, Window win, int width, int height)
349   {
350   XSizeHints * h;
351   h = XAllocSizeHints();
352 
353   h->flags = PMinSize;
354   h->min_width = width;
355   h->min_height = height;
356 
357   XSetWMNormalHints(w->dpy, win, h);
358 
359   XFree(h);
360   }
361 
open_display(bg_x11_window_t * w)362 static int open_display(bg_x11_window_t * w)
363   {
364   char * normal_id;
365   char * fullscreen_id;
366 #ifdef HAVE_LIBXINERAMA
367   int foo,bar;
368 #endif
369 
370   /*
371    *  Display string is in the form
372    *  <XDisplayName(DisplayString(dpy)>:<normal_id>:<fullscreen_id>
373    *  It can be NULL. Also, <fullscreen_id> can be missing
374    */
375 
376   if(!w->display_string_parent)
377     {
378     w->dpy = XOpenDisplay(NULL);
379     if(!w->dpy)
380       return 0;
381     w->normal.parent = None;
382     w->fullscreen.parent = None;
383     }
384   else
385     {
386     fullscreen_id = strrchr(w->display_string_parent, ':');
387     if(!fullscreen_id)
388       {
389       return 0;
390       }
391     *fullscreen_id = '\0';
392     fullscreen_id++;
393 
394     normal_id = strrchr(w->display_string_parent, ':');
395     if(!normal_id)
396       {
397       return 0;
398       }
399     *normal_id = '\0';
400     normal_id++;
401 
402     w->dpy = XOpenDisplay(w->display_string_parent);
403     if(!w->dpy)
404       return 0;
405 
406     w->normal.parent = strtoul(normal_id, NULL, 16);
407 
408     if(!(*fullscreen_id))
409       w->fullscreen.parent = None;
410     else
411       w->fullscreen.parent = strtoul(fullscreen_id, NULL, 16);
412 
413     //    fprintf(stderr, "Initialized windows: %ld %ld\n",
414     //            w->normal.parent, w->fullscreen.parent);
415     }
416 
417   w->screen = DefaultScreen(w->dpy);
418   w->root =   RootWindow(w->dpy, w->screen);
419 
420   if(w->normal.parent == None)
421     w->normal.parent = w->root;
422   if(w->fullscreen.parent == None)
423     w->fullscreen.parent = w->root;
424 
425   w->normal.child = None;
426   w->fullscreen.child = None;
427 
428   init_atoms(w);
429 
430   /* Check, which fullscreen modes we have */
431 
432   w->fullscreen_mode = get_fullscreen_mode(w);
433 
434   bg_x11_screensaver_init(&w->scr, w->dpy);
435 
436   /* Get xinerama screens */
437 
438 #ifdef HAVE_LIBXINERAMA
439   if (XineramaQueryExtension(w->dpy,&foo,&bar) &&
440       XineramaIsActive(w->dpy))
441     {
442     w->xinerama = XineramaQueryScreens(w->dpy,&w->nxinerama);
443     }
444 #endif
445 
446   /* Check for XShm */
447   w->have_shm = bg_x11_window_check_shm(w->dpy, &w->shm_completion_type);
448 
449   return 1;
450   }
451 
bg_x11_window_get_coords(Display * dpy,Window win,int * x,int * y,int * width,int * height)452 void bg_x11_window_get_coords(Display * dpy,
453                               Window win,
454                               int * x, int * y, int * width,
455                               int * height)
456   {
457   Window root_return;
458   Window parent_return;
459   Window * children_return;
460   unsigned int nchildren_return;
461   int x_return, y_return;
462   unsigned int width_return, height_return;
463   unsigned int border_width_return;
464   unsigned int depth_return;
465   //  Window child_return;
466 
467   XGetGeometry(dpy, win, &root_return, &x_return, &y_return,
468                &width_return, &height_return,
469                &border_width_return, &depth_return);
470 
471   XQueryTree(dpy, win, &root_return, &parent_return,
472              &children_return, &nchildren_return);
473 
474   if(nchildren_return)
475     XFree(children_return);
476 
477   if(x) *x = x_return;
478   if(y) *y = y_return;
479 
480   if(width)  *width  = width_return;
481   if(height) *height = height_return;
482 
483   if((x || y) && (parent_return != root_return))
484     {
485     XGetGeometry(dpy, parent_return, &root_return,
486                  &x_return, &y_return,
487                  &width_return, &height_return,
488                  &border_width_return, &depth_return);
489     if(x) *x = x_return;
490     if(y) *y = y_return;
491     }
492   }
493 
window_is_viewable(Display * dpy,Window w)494 static int window_is_viewable(Display * dpy, Window w)
495   {
496   XWindowAttributes attr;
497 
498   if(w == None)
499     return 0;
500 
501   XGetWindowAttributes(dpy, w, &attr);
502   if(attr.map_state == IsViewable)
503     return 1;
504   return 0;
505   }
506 
507 
bg_x11_window_size_changed(bg_x11_window_t * w)508 void bg_x11_window_size_changed(bg_x11_window_t * w)
509   {
510   /* Frame size remains unchanged, to let set_rectangles
511      find out, if the image must be reallocated */
512 
513   /* Nothing changed actually. */
514   if((w->window_format.image_width == w->window_width) &&
515      (w->window_format.image_height == w->window_height))
516     return;
517 
518   w->window_format.image_width  = w->window_width;
519   w->window_format.image_height = w->window_height;
520 
521   if(w->callbacks && w->callbacks->size_changed)
522     w->callbacks->size_changed(w->callbacks->data,
523                                w->window_width,
524                                w->window_height);
525   }
526 
bg_x11_window_init(bg_x11_window_t * w)527 void bg_x11_window_init(bg_x11_window_t * w)
528   {
529   int send_event = -1;
530   /* Decide current window */
531   if((w->fullscreen.parent != w->root) &&
532      window_is_viewable(w->dpy, w->fullscreen.parent))
533     {
534     //    fprintf(stderr, "Is fullscreen\n");
535 
536     if(!w->is_fullscreen)
537       send_event = 1;
538     w->current = &w->fullscreen;
539     w->is_fullscreen = 1;
540     }
541   else
542     {
543     if(w->is_fullscreen)
544       send_event = 0;
545     w->current = &w->normal;
546     w->is_fullscreen = 0;
547     }
548 
549 #if 1
550   if(w->current->parent != w->root)
551     {
552     //    fprintf(stderr, "bg_x11_window_init %ld %ld\n", w->current->win,
553     //            w->current->parent);
554     bg_x11_window_get_coords(w->dpy, w->current->parent,
555                       NULL, NULL,
556                       &w->window_width, &w->window_height);
557     XMoveResizeWindow(w->dpy, w->current->win, 0, 0,
558                       w->window_width, w->window_height);
559     }
560   else
561     bg_x11_window_get_coords(w->dpy, w->current->win,
562                       NULL, NULL,
563                       &w->window_width, &w->window_height);
564 #endif
565   //  fprintf(stderr, "Window size: %dx%d\n", w->window_width, w->window_height);
566   if((send_event >= 0) && w->callbacks &&
567      w->callbacks->set_fullscreen)
568     w->callbacks->set_fullscreen(w->callbacks->data, send_event);
569 
570   bg_x11_window_size_changed(w);
571 
572 
573   }
574 
bg_x11_window_embed_parent(bg_x11_window_t * win,window_t * w)575 void bg_x11_window_embed_parent(bg_x11_window_t * win,
576                                 window_t * w)
577   {
578   unsigned long buffer[2];
579 
580 
581   buffer[0] = 0; // Version
582   buffer[1] = 0;
583 
584 
585   //   XMapWindow(dpy, child);
586   XChangeProperty(win->dpy,
587                   w->win,
588                   win->_XEMBED_INFO,
589                   win->_XEMBED_INFO, 32,
590                   PropModeReplace,
591                   (unsigned char *)buffer, 2);
592   w->toplevel = bg_x11_window_get_toplevel(win, w->parent);
593 
594   /* We need StructureNotify of both the toplevel and the
595      parent */
596 
597   XSelectInput(win->dpy, w->parent, StructureNotifyMask);
598 
599   if(w->parent != w->toplevel)
600     XSelectInput(win->dpy, w->toplevel, StructureNotifyMask);
601 
602   XSync(win->dpy, False);
603   }
604 
bg_x11_window_embed_child(bg_x11_window_t * win,window_t * w)605 void bg_x11_window_embed_child(bg_x11_window_t * win,
606                                window_t * w)
607   {
608   XSelectInput(win->dpy, w->child, FocusChangeMask | ExposureMask |
609                PropertyChangeMask);
610 
611   /* Define back the cursor */
612   XDefineCursor(win->dpy, w->win, None);
613   win->pointer_hidden = 0;
614 
615   bg_x11_window_check_embed_property(win, w);
616   }
617 
create_subwin(bg_x11_window_t * w,window_t * win,int depth,Visual * v,unsigned long attr_mask,XSetWindowAttributes * attr)618 static void create_subwin(bg_x11_window_t * w,
619                           window_t * win,
620                           int depth, Visual * v, unsigned long attr_mask,
621                           XSetWindowAttributes * attr)
622   {
623   int width, height;
624 
625   bg_x11_window_get_coords(w->dpy, win->win,
626                            NULL, NULL, &width, &height);
627   win->subwin = XCreateWindow(w->dpy,
628                               win->win, 0, 0, width, height, 0,
629                               depth, InputOutput, v, attr_mask, attr);
630   XMapWindow(w->dpy, win->subwin);
631   }
632 
bg_x11_window_create_subwins(bg_x11_window_t * w,int depth,Visual * v)633 void bg_x11_window_create_subwins(bg_x11_window_t * w,
634                                   int depth, Visual * v)
635   {
636   unsigned long attr_mask;
637   XSetWindowAttributes attr;
638 
639   w->sub_colormap = XCreateColormap(w->dpy, RootWindow(w->dpy, w->screen),
640                                     v, AllocNone);
641 
642   memset(&attr, 0, sizeof(attr));
643 
644   attr_mask = CWEventMask | CWColormap;
645 
646   attr.event_mask =
647     ExposureMask |
648     ButtonPressMask |
649     ButtonReleaseMask |
650     KeyPressMask |
651     PointerMotionMask |
652     KeyReleaseMask;
653 
654   attr.colormap = w->sub_colormap;
655 
656   create_subwin(w, &w->normal, depth, v, attr_mask, &attr);
657   create_subwin(w, &w->fullscreen, depth, v, attr_mask, &attr);
658   }
659 
destroy_subwin(bg_x11_window_t * w,window_t * win)660 static void destroy_subwin(bg_x11_window_t * w,
661                            window_t * win)
662   {
663   if(win->subwin != None)
664     {
665     XDestroyWindow(w->dpy, win->subwin);
666     win->subwin = None;
667     }
668   }
669 
670 
bg_x11_window_destroy_subwins(bg_x11_window_t * w)671 void bg_x11_window_destroy_subwins(bg_x11_window_t * w)
672   {
673   destroy_subwin(w, &w->normal);
674   destroy_subwin(w, &w->fullscreen);
675 
676   }
677 
create_window(bg_x11_window_t * w,int width,int height)678 static int create_window(bg_x11_window_t * w,
679                          int width, int height)
680   {
681   const char * display_name;
682   unsigned long event_mask;
683   XColor black;
684   Atom wm_protocols[2];
685   XWMHints * wmhints;
686 
687   //  int i;
688   /* Stuff for making the cursor */
689 
690   XSetWindowAttributes attr;
691   unsigned long attr_flags;
692 
693   if((!w->dpy) && !open_display(w))
694     return 0;
695 
696   wmhints = XAllocWMHints();
697 
698   wmhints->input = True;
699   wmhints->initial_state = NormalState;
700   wmhints->flags |= InputHint|StateHint;
701 
702   /* Creating windows without colormap results in a BadMatch error */
703   w->colormap = XCreateColormap(w->dpy, RootWindow(w->dpy, w->screen),
704                                 w->visual,
705                                 AllocNone);
706 
707   /* Setup event mask */
708 
709   event_mask =
710     StructureNotifyMask |
711     PointerMotionMask |
712     ExposureMask |
713     ButtonPressMask |
714     ButtonReleaseMask |
715     PropertyChangeMask |
716     KeyPressMask |
717     KeyReleaseMask |
718     FocusChangeMask;
719 
720   /* Create normal window */
721 
722   memset(&attr, 0, sizeof(attr));
723 
724   attr.backing_store = NotUseful;
725   attr.border_pixel = 0;
726   attr.background_pixel = 0;
727   attr.event_mask = event_mask;
728   attr.colormap = w->colormap;
729   attr_flags = (CWBackingStore | CWEventMask | CWBorderPixel |
730                 CWBackPixel | CWColormap);
731 
732   wm_protocols[0] = w->WM_DELETE_WINDOW;
733   wm_protocols[1] = w->WM_TAKE_FOCUS;
734 
735   /* Create normal window */
736 
737   w->normal.win = XCreateWindow(w->dpy, w->normal.parent,
738                                 0 /* x */,
739                                 0 /* y */,
740                                 width, height,
741                                 0 /* border_width */, w->depth,
742                                 InputOutput,
743                                 w->visual,
744                                 attr_flags,
745                                 &attr);
746 
747   if(w->normal.parent == w->root)
748     {
749     //    set_decorations(w, w->normal.win, 1);
750     XSetWMProtocols(w->dpy, w->normal.win, wm_protocols, 2);
751 
752     if(w->min_width && w->min_height)
753       set_min_size(w, w->normal.win, w->min_width, w->min_height);
754 
755     w->normal.toplevel = w->normal.win;
756 
757     w->normal.focus_child = XCreateSimpleWindow(w->dpy, w->normal.win,
758                                                   -1, -1, 1, 1, 0,
759                                                   0, 0);
760     XSelectInput(w->dpy, w->normal.focus_child,
761                  KeyPressMask | KeyReleaseMask | FocusChangeMask);
762 
763     XSetWMHints(w->dpy, w->normal.win, wmhints);
764     XMapWindow(w->dpy, w->normal.focus_child);
765     w->normal.child_accel_map = bg_accelerator_map_create();
766     }
767   else
768     bg_x11_window_embed_parent(w, &w->normal);
769 
770   /* The fullscreen window will be created with the same size for now */
771 
772   w->fullscreen.win = XCreateWindow (w->dpy, w->fullscreen.parent,
773                                      0 /* x */,
774                                      0 /* y */,
775                                      width, height,
776                                      0 /* border_width */, w->depth,
777                                      InputOutput, w->visual,
778                                      attr_flags,
779                                      &attr);
780 
781   w->fullscreen.fullscreen = 1;
782 
783   if(w->fullscreen.parent == w->root)
784     {
785     /* Setup protocols */
786 
787     set_fullscreen(w, w->fullscreen.win);
788     XSetWMProtocols(w->dpy, w->fullscreen.win, wm_protocols, 2);
789     w->fullscreen.toplevel = w->fullscreen.win;
790 
791 #if 0
792     w->fullscreen.focus_child =
793       XCreateWindow(w->dpy, w->fullscreen.win,
794                     0, 0,
795                     width, height,
796                     0,
797                     0,
798                     InputOnly,
799                     vi->visual,
800                     attr_flags_input_only,
801                     &attr);
802 #endif
803     w->fullscreen.focus_child = XCreateSimpleWindow(w->dpy,
804                                                     w->fullscreen.win,
805                                                     -1, -1, 1, 1, 0,
806                                                     0, 0);
807     XSelectInput(w->dpy, w->fullscreen.focus_child,
808                  KeyPressMask | KeyReleaseMask | FocusChangeMask);
809 
810     XSetWMHints(w->dpy, w->fullscreen.win, wmhints);
811     XMapWindow(w->dpy, w->fullscreen.focus_child);
812     w->fullscreen.child_accel_map = bg_accelerator_map_create();
813     }
814   else
815     {
816     bg_x11_window_embed_parent(w, &w->fullscreen);
817     }
818 
819   /* Set the final event masks now. We blocked SubstructureNotifyMask
820    * before because we don't want our focus children as
821    * embeded clients..
822    */
823 
824   XSync(w->dpy, False);
825   XSelectInput(w->dpy, w->normal.win,
826                event_mask | SubstructureNotifyMask);
827   XSelectInput(w->dpy, w->fullscreen.win,
828                event_mask | SubstructureNotifyMask);
829 
830   /* Create GC */
831 
832   w->gc = XCreateGC(w->dpy, w->normal.win, 0, NULL);
833 
834   /* Create colormap and fullscreen cursor */
835 
836   w->fullscreen_cursor_pixmap =
837     XCreateBitmapFromData(w->dpy, w->fullscreen.win,
838                           bm_no_data, 8, 8);
839 
840   black.pixel = BlackPixel(w->dpy, w->screen);
841   XQueryColor(w->dpy, DefaultColormap(w->dpy, w->screen), &black);
842 
843   w->fullscreen_cursor=
844     XCreatePixmapCursor(w->dpy, w->fullscreen_cursor_pixmap,
845                         w->fullscreen_cursor_pixmap,
846                         &black, &black, 0, 0);
847 
848   w->black = BlackPixel(w->dpy, w->screen);
849 
850 
851   display_name = XDisplayName(DisplayString(w->dpy));
852 
853   XFree(wmhints);
854 
855   /* Determine if we are in fullscreen mode */
856   bg_x11_window_init(w);
857 
858 
859   return 1;
860   }
861 
862 
get_fullscreen_coords(bg_x11_window_t * w,int * x,int * y,int * width,int * height)863 static void get_fullscreen_coords(bg_x11_window_t * w,
864                                   int * x, int * y, int * width, int * height)
865   {
866 #ifdef HAVE_LIBXINERAMA
867   int x_return, y_return;
868   int i;
869   Window child;
870   /* Get the coordinates of the normal window */
871 
872   *x = 0;
873   *y = 0;
874   *width  = DisplayWidth(w->dpy, w->screen);
875   *height = DisplayHeight(w->dpy, w->screen);
876 
877   if(w->nxinerama)
878     {
879     XTranslateCoordinates(w->dpy, w->normal.win, w->root, 0, 0,
880                           &x_return,
881                           &y_return,
882                           &child);
883 
884     /* Get the xinerama screen we are on */
885 
886     for(i = 0; i < w->nxinerama; i++)
887       {
888       if((x_return >= w->xinerama[i].x_org) &&
889          (y_return >= w->xinerama[i].y_org) &&
890          (x_return < w->xinerama[i].x_org + w->xinerama[i].width) &&
891          (y_return < w->xinerama[i].y_org + w->xinerama[i].height))
892         {
893         *x = w->xinerama[i].x_org;
894         *y = w->xinerama[i].y_org;
895         *width = w->xinerama[i].width;
896         *height = w->xinerama[i].height;
897         break;
898         }
899       }
900     }
901 #else
902   *x = 0;
903   *y = 0;
904   *width  = DisplayWidth(w->dpy, w->screen);
905   *height = DisplayHeight(w->dpy, w->screen);
906 #endif
907   }
908 
bg_x11_window_set_fullscreen(bg_x11_window_t * w,int fullscreen)909 int bg_x11_window_set_fullscreen(bg_x11_window_t * w,int fullscreen)
910   {
911   int ret = 0;
912   int width;
913   int height;
914   int x;
915   int y;
916 
917   /* Return early if there is nothing to do */
918   if(!!fullscreen == !!w->is_fullscreen)
919     return 0;
920 
921   /* Normal->fullscreen */
922 
923   if(fullscreen)
924     {
925     if(w->normal.parent != w->root)
926       return 0;
927     get_fullscreen_coords(w, &x, &y, &width, &height);
928 
929     w->normal_width = w->window_width;
930     w->normal_height = w->window_height;
931 
932     w->window_width = width;
933     w->window_height = height;
934 
935     w->current = &w->fullscreen;
936     w->is_fullscreen = 1;
937     w->need_fullscreen = 1;
938     bg_x11_window_show(w, 1);
939 
940     if(w->callbacks && w->callbacks->set_fullscreen)
941       w->callbacks->set_fullscreen(w->callbacks->data, 1);
942 
943     bg_x11_window_clear(w);
944 
945     /* Hide old window */
946     if(w->normal.win == w->normal.toplevel)
947       XWithdrawWindow(w->dpy, w->normal.toplevel, w->screen);
948 
949     XFlush(w->dpy);
950     ret = 1;
951     }
952   else
953     {
954     if(w->fullscreen.parent != w->root)
955       return 0;
956     /* Unmap fullscreen window */
957 #if 1
958     bg_x11_window_set_netwm_state(w->dpy, w->fullscreen.win, w->root,
959                                   _NET_WM_STATE_REMOVE, w->_NET_WM_STATE_FULLSCREEN);
960 
961     bg_x11_window_set_netwm_state(w->dpy, w->fullscreen.win, w->root,
962                                   _NET_WM_STATE_REMOVE, w->_NET_WM_STATE_ABOVE);
963     XUnmapWindow(w->dpy, w->fullscreen.win);
964 #endif
965     XWithdrawWindow(w->dpy, w->fullscreen.win, w->screen);
966 
967     /* Map normal window */
968     w->current = &w->normal;
969     w->is_fullscreen = 0;
970 
971     w->window_width  = w->normal_width;
972     w->window_height = w->normal_height;
973 
974     bg_x11_window_show(w, 1);
975 
976     if(w->callbacks && w->callbacks->set_fullscreen)
977       w->callbacks->set_fullscreen(w->callbacks->data, 0);
978 
979     bg_x11_window_clear(w);
980     XFlush(w->dpy);
981     ret = 1;
982     }
983 
984   if(ret)
985     {
986     if(check_disable_screensaver(w))
987       bg_x11_screensaver_disable(&w->scr);
988     else
989       bg_x11_screensaver_enable(&w->scr);
990     }
991   return ret;
992   }
993 
bg_x11_window_resize(bg_x11_window_t * win,int width,int height)994 void bg_x11_window_resize(bg_x11_window_t * win,
995                           int width, int height)
996   {
997   win->normal_width = width;
998   win->normal_height = height;
999   if(!win->is_fullscreen && (win->normal.parent == win->root))
1000     {
1001     win->window_width = width;
1002     win->window_height = height;
1003     XResizeWindow(win->dpy, win->normal.win, width, height);
1004     }
1005   }
1006 
bg_x11_window_get_size(bg_x11_window_t * win,int * width,int * height)1007 void bg_x11_window_get_size(bg_x11_window_t * win, int * width, int * height)
1008   {
1009   *width = win->window_width;
1010   *height = win->window_height;
1011   }
1012 
1013 /* Public methods */
1014 
bg_x11_window_create(const char * display_string)1015 bg_x11_window_t * bg_x11_window_create(const char * display_string)
1016   {
1017   bg_x11_window_t * ret;
1018   ret = calloc(1, sizeof(*ret));
1019   ret->display_string_parent = bg_strdup(ret->display_string_parent, display_string);
1020   ret->scaler = gavl_video_scaler_create();
1021 
1022   /* Set default OpenGL attributes */
1023 
1024   bg_x11_window_set_gl_attribute(ret, BG_GL_ATTRIBUTE_RGBA, 1);
1025   bg_x11_window_set_gl_attribute(ret, BG_GL_ATTRIBUTE_RED_SIZE, 8);
1026   bg_x11_window_set_gl_attribute(ret, BG_GL_ATTRIBUTE_GREEN_SIZE, 8);
1027   bg_x11_window_set_gl_attribute(ret, BG_GL_ATTRIBUTE_BLUE_SIZE, 8);
1028   bg_x11_window_set_gl_attribute(ret, BG_GL_ATTRIBUTE_DOUBLEBUFFER, 1);
1029 
1030   ret->icon      = None;
1031   ret->icon_mask = None;
1032 
1033   return ret;
1034   }
1035 
bg_x11_window_get_display_string(bg_x11_window_t * w)1036 const char * bg_x11_window_get_display_string(bg_x11_window_t * w)
1037   {
1038   if(w->normal.win == None)
1039     create_window(w, w->window_width, w->window_height);
1040 
1041   if(!w->display_string_child)
1042     w->display_string_child = bg_sprintf("%s:%08lx:%08lx",
1043                                          XDisplayName(DisplayString(w->dpy)),
1044                                          w->normal.win, w->fullscreen.win);
1045   return w->display_string_child;
1046   }
1047 
bg_x11_window_destroy(bg_x11_window_t * w)1048 void bg_x11_window_destroy(bg_x11_window_t * w)
1049   {
1050   bg_x11_window_cleanup_video(w);
1051   bg_x11_window_cleanup_gl(w);
1052   bg_x11_window_destroy_subwins(w);
1053 
1054   if(w->colormap != None)
1055     XFreeColormap(w->dpy, w->colormap);
1056 
1057   if(w->normal.win != None)
1058     XDestroyWindow(w->dpy, w->normal.win);
1059 
1060   if(w->fullscreen.win != None)
1061     XDestroyWindow(w->dpy, w->fullscreen.win);
1062 
1063   if(w->fullscreen_cursor != None)
1064     XFreeCursor(w->dpy, w->fullscreen_cursor);
1065 
1066   if(w->fullscreen_cursor_pixmap != None)
1067     XFreePixmap(w->dpy, w->fullscreen_cursor_pixmap);
1068 
1069   if(w->gc != None)
1070     XFreeGC(w->dpy, w->gc);
1071 
1072   if(w->normal.child_accel_map)     bg_accelerator_map_destroy(w->normal.child_accel_map);
1073   if(w->fullscreen.child_accel_map) bg_accelerator_map_destroy(w->fullscreen.child_accel_map);
1074 
1075 #ifdef HAVE_LIBXINERAMA
1076   if(w->xinerama)
1077     XFree(w->xinerama);
1078 #endif
1079 
1080 #ifdef GAVE_GLX
1081   if(w->gl_fbconfigs)
1082     XFree(w->gl_fbconfigs);
1083 #endif
1084 
1085   if(w->icon != None)
1086     XFreePixmap(w->dpy, w->icon);
1087 
1088   if(w->icon_mask != None)
1089     XFreePixmap(w->dpy, w->icon_mask);
1090 
1091   if(w->dpy)
1092     {
1093     XCloseDisplay(w->dpy);
1094     bg_x11_screensaver_cleanup(&w->scr);
1095     }
1096   if(w->display_string_parent)
1097     free(w->display_string_parent);
1098   if(w->display_string_child)
1099     free(w->display_string_child);
1100 
1101   if(w->scaler)
1102     gavl_video_scaler_destroy(w->scaler);
1103 
1104   free(w);
1105   }
1106 
1107 static const bg_parameter_info_t common_parameters[] =
1108   {
1109     {
1110       BG_LOCALE,
1111       .name =        "window",
1112       .long_name =   TRS("General"),
1113     },
1114     {
1115       .name =        "auto_resize",
1116       .long_name =   TRS("Auto resize window"),
1117       .type =        BG_PARAMETER_CHECKBUTTON,
1118       .val_default = { .val_i = 1 }
1119     },
1120     {
1121       .name =        "window_width",
1122       .long_name =   "Window width",
1123       .type =        BG_PARAMETER_INT,
1124       .flags =       BG_PARAMETER_HIDE_DIALOG,
1125       .val_default = { .val_i = 320 }
1126     },
1127     {
1128       .name =        "window_height",
1129       .long_name =   "Window height",
1130       .type =        BG_PARAMETER_INT,
1131       .flags =       BG_PARAMETER_HIDE_DIALOG,
1132       .val_default = { .val_i = 240 }
1133     },
1134     {
1135       .name =        "window_x",
1136       .long_name =   "Window x",
1137       .type =        BG_PARAMETER_INT,
1138       .flags =       BG_PARAMETER_HIDE_DIALOG,
1139       .val_default = { .val_i = 100 }
1140     },
1141     {
1142       .name =        "window_y",
1143       .long_name =   "Window y",
1144       .type =        BG_PARAMETER_INT,
1145       .flags =       BG_PARAMETER_HIDE_DIALOG,
1146       .val_default = { .val_i = 100 }
1147     },
1148     {
1149       .name =        "disable_xscreensaver_normal",
1150       .long_name =   TRS("Disable Screensaver for normal playback"),
1151       .type =        BG_PARAMETER_CHECKBUTTON,
1152       .val_default = { .val_i = 0 }
1153     },
1154     {
1155       .name =        "disable_xscreensaver_fullscreen",
1156       .long_name =   TRS("Disable Screensaver for fullscreen playback"),
1157       .type =        BG_PARAMETER_CHECKBUTTON,
1158       .val_default = { .val_i = 1 }
1159     },
1160     {
1161       .name =        "force_hw_scale",
1162       .long_name =   TRS("Force hardware scaling"),
1163       .type =        BG_PARAMETER_CHECKBUTTON,      .val_default = { .val_i = 1 },
1164       .help_string = TRS("Use hardware scaling even if it involves more CPU intensive pixelformat conversions"),
1165 
1166     },
1167 #ifdef HAVE_GLX
1168     {
1169       .name =        "background_color",
1170       .long_name =   TRS("Background color"),
1171       .type =        BG_PARAMETER_COLOR_RGB,
1172       .flags =       BG_PARAMETER_SYNC,
1173       .help_string = TRS("Specify the background color for videos with alpha channel. This is only used by the OpenGL driver."),
1174 
1175     },
1176 #endif
1177     {
1178       .name =        "sw_scaler",
1179       .long_name =   TRS("Software scaler"),
1180       .type =        BG_PARAMETER_SECTION,
1181     },
1182     {
1183       .name =        "scale_mode",
1184       .long_name =   TRS("Scale mode"),
1185       .type =        BG_PARAMETER_STRINGLIST,
1186       .multi_names =  (char const*[]){ "auto",
1187                                "nearest",
1188                                "bilinear",
1189                                "quadratic",
1190                                "cubic_bspline",
1191                                "cubic_mitchell",
1192                                "cubic_catmull",
1193                                "sinc_lanczos",
1194                                NULL },
1195       .multi_labels = (char const*[]){ TRS("Auto"),
1196                                TRS("Nearest"),
1197                                TRS("Bilinear"),
1198                                TRS("Quadratic"),
1199                                TRS("Cubic B-Spline"),
1200                                TRS("Cubic Mitchell-Netravali"),
1201                                TRS("Cubic Catmull-Rom"),
1202                                TRS("Sinc with Lanczos window"),
1203                                NULL },
1204       .val_default = { .val_str = "auto" },
1205       .help_string = TRS("Choose scaling method. Auto means to choose based on the conversion quality. Nearest is fastest, Sinc with Lanczos window is slowest."),
1206     },
1207     {
1208       .name =        "scale_order",
1209       .long_name =   TRS("Scale order"),
1210       .type =        BG_PARAMETER_INT,
1211       .val_min =     { .val_i = 4 },
1212       .val_max =     { .val_i = 1000 },
1213       .val_default = { .val_i = 4 },
1214       .help_string = TRS("Order for sinc scaling"),
1215     },
1216     {
1217       .name =        "scale_quality",
1218       .long_name =   TRS("Scale quality"),
1219       .type =        BG_PARAMETER_SLIDER_INT,
1220       .val_min =     { .val_i = GAVL_QUALITY_FASTEST },
1221       .val_max =     { .val_i = GAVL_QUALITY_BEST },
1222       .val_default = { .val_i = GAVL_QUALITY_DEFAULT },
1223       .help_string = TRS("Scale quality"),
1224     },
1225     { /* End of parameters */ },
1226   };
1227 
1228 
bg_x11_window_get_parameters(bg_x11_window_t * win)1229 const bg_parameter_info_t * bg_x11_window_get_parameters(bg_x11_window_t * win)
1230   {
1231   return common_parameters;
1232   }
1233 
1234 void
bg_x11_window_set_parameter(void * data,const char * name,const bg_parameter_value_t * val)1235 bg_x11_window_set_parameter(void * data, const char * name,
1236                             const bg_parameter_value_t * val)
1237   {
1238   gavl_scale_mode_t scale_mode = GAVL_SCALE_AUTO;
1239   gavl_video_options_t * opt;
1240   bg_x11_window_t * win;
1241   if(!name)
1242     return;
1243   win = (bg_x11_window_t *)data;
1244 
1245   if(!strcmp(name, "auto_resize"))
1246     {
1247     win->auto_resize = val->val_i;
1248     }
1249   else if(!strcmp(name, "window_x"))
1250     {
1251     if(win->normal.parent == win->root)
1252       win->window_x = val->val_i;
1253     }
1254   else if(!strcmp(name, "window_y"))
1255     {
1256     if(win->normal.parent == win->root)
1257       win->window_y = val->val_i;
1258     }
1259   else if(!strcmp(name, "window_width"))
1260     {
1261     if(win->normal.parent == win->root)
1262       win->window_width = val->val_i;
1263     }
1264   else if(!strcmp(name, "window_height"))
1265     {
1266     if(win->normal.parent == win->root)
1267       win->window_height = val->val_i;
1268     }
1269   else if(!strcmp(name, "disable_xscreensaver_normal"))
1270     {
1271     win->disable_screensaver_normal = val->val_i;
1272     }
1273   else if(!strcmp(name, "disable_xscreensaver_fullscreen"))
1274     {
1275     win->disable_screensaver_fullscreen = val->val_i;
1276     }
1277 #ifdef HAVE_GLX
1278   else if(!strcmp(name, "background_color"))
1279     {
1280     memcpy(win->background_color, val->val_color, 3 * sizeof(float));
1281     }
1282 #endif
1283 
1284   else if(!strcmp(name, "force_hw_scale"))
1285     {
1286     win->force_hw_scale = val->val_i;
1287     }
1288   else if(!strcmp(name, "scale_mode"))
1289     {
1290     if(!strcmp(val->val_str, "auto"))
1291       scale_mode = GAVL_SCALE_AUTO;
1292     else if(!strcmp(val->val_str, "nearest"))
1293       scale_mode = GAVL_SCALE_NEAREST;
1294     else if(!strcmp(val->val_str, "bilinear"))
1295       scale_mode = GAVL_SCALE_BILINEAR;
1296     else if(!strcmp(val->val_str, "quadratic"))
1297       scale_mode = GAVL_SCALE_QUADRATIC;
1298     else if(!strcmp(val->val_str, "cubic_bspline"))
1299       scale_mode = GAVL_SCALE_CUBIC_BSPLINE;
1300     else if(!strcmp(val->val_str, "cubic_mitchell"))
1301       scale_mode = GAVL_SCALE_CUBIC_MITCHELL;
1302     else if(!strcmp(val->val_str, "cubic_catmull"))
1303       scale_mode = GAVL_SCALE_CUBIC_CATMULL;
1304     else if(!strcmp(val->val_str, "sinc_lanczos"))
1305       scale_mode = GAVL_SCALE_SINC_LANCZOS;
1306 
1307     opt = gavl_video_scaler_get_options(win->scaler);
1308     if(scale_mode != gavl_video_options_get_scale_mode(opt))
1309       {
1310       win->scaler_options_changed = 1;
1311       gavl_video_options_set_scale_mode(opt, scale_mode);
1312       }
1313     }
1314   else if(!strcmp(name, "scale_order"))
1315     {
1316     opt = gavl_video_scaler_get_options(win->scaler);
1317     if(val->val_i != gavl_video_options_get_scale_order(opt))
1318       {
1319       win->scaler_options_changed = 1;
1320       gavl_video_options_set_scale_order(opt, val->val_i);
1321       }
1322     }
1323   else if(!strcmp(name, "scale_quality"))
1324     {
1325     opt = gavl_video_scaler_get_options(win->scaler);
1326     if(val->val_i != gavl_video_options_get_quality(opt))
1327       {
1328       win->scaler_options_changed = 1;
1329       gavl_video_options_set_quality(opt, val->val_i);
1330       }
1331     }
1332 
1333 
1334   }
1335 
1336 int
bg_x11_window_get_parameter(void * data,const char * name,bg_parameter_value_t * val)1337 bg_x11_window_get_parameter(void * data, const char * name,
1338                             bg_parameter_value_t * val)
1339   {
1340   bg_x11_window_t * win;
1341   if(!name)
1342     return 0;
1343   win = (bg_x11_window_t *)data;
1344 
1345   if(!strcmp(name, "window_x"))
1346     {
1347     val->val_i = win->window_x;
1348     return 1;
1349     }
1350   else if(!strcmp(name, "window_y"))
1351     {
1352     val->val_i = win->window_y;
1353     return 1;
1354     }
1355   else if(!strcmp(name, "window_width"))
1356     {
1357     val->val_i = win->window_width;
1358     return 1;
1359     }
1360   else if(!strcmp(name, "window_height"))
1361     {
1362     val->val_i = win->window_height;
1363     return 1;
1364     }
1365   return 0;
1366   }
1367 
1368 
bg_x11_window_set_size(bg_x11_window_t * win,int width,int height)1369 void bg_x11_window_set_size(bg_x11_window_t * win, int width, int height)
1370   {
1371   win->window_width = width;
1372   win->window_height = height;
1373   }
1374 
1375 
bg_x11_window_realize(bg_x11_window_t * win)1376 int bg_x11_window_realize(bg_x11_window_t * win)
1377   {
1378   int ret;
1379 
1380   if(!win->dpy && !open_display(win))
1381     return 0;
1382 
1383 // #ifdef HAVE_GLX
1384 #if 0
1385   win->gl_vi = glXChooseVisual(win->dpy, win->screen, attr_list);
1386 
1387   if(!win->gl_vi)
1388     {
1389     bg_log(BG_LOG_WARNING, LOG_DOMAIN, "Could not get GL Visual");
1390     win->visual = DefaultVisual(win->dpy, win->screen);
1391     win->depth = DefaultDepth(win->dpy, win->screen);
1392     }
1393   else
1394     {
1395     win->visual = win->gl_vi->visual;
1396     win->depth  = win->gl_vi->depth;
1397     }
1398 
1399 #endif
1400 
1401   win->visual = DefaultVisual(win->dpy, win->screen);
1402   win->depth = DefaultDepth(win->dpy, win->screen);
1403 
1404   bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Got Visual 0x%lx depth %d",
1405          win->visual->visualid, win->depth);
1406 
1407   ret = create_window(win, win->window_width, win->window_height);
1408   bg_x11_window_init_gl(win);
1409   return ret;
1410   }
1411 
1412 /* Handle X11 events, callbacks are called from here */
bg_x11_window_set_callbacks(bg_x11_window_t * win,bg_x11_window_callbacks_t * callbacks)1413 void bg_x11_window_set_callbacks(bg_x11_window_t * win,
1414                                  bg_x11_window_callbacks_t * callbacks)
1415   {
1416   win->callbacks = callbacks;
1417   }
1418 
bg_x11_window_set_title(bg_x11_window_t * w,const char * title)1419 void bg_x11_window_set_title(bg_x11_window_t * w, const char * title)
1420   {
1421   if(w->normal.parent == w->root)
1422     XmbSetWMProperties(w->dpy, w->normal.win, title,
1423                        title, NULL, 0, NULL, NULL, NULL);
1424   if(w->fullscreen.parent == w->root)
1425     XmbSetWMProperties(w->dpy, w->fullscreen.win, title,
1426                        title, NULL, 0, NULL, NULL, NULL);
1427   }
1428 
bg_x11_window_set_options(bg_x11_window_t * w,const char * name,const char * klass,const gavl_video_frame_t * icon,const gavl_video_format_t * icon_format)1429 void bg_x11_window_set_options(bg_x11_window_t * w,
1430                                const char * name,
1431                                const char * klass,
1432                                const gavl_video_frame_t * icon,
1433                                const gavl_video_format_t * icon_format)
1434   {
1435   /* Set Class hints */
1436   if(name && klass)
1437     {
1438     XClassHint xclasshint;
1439 
1440     /* const madness */
1441     xclasshint.res_name = bg_strdup(NULL, name);
1442     xclasshint.res_class = bg_strdup(NULL, klass);
1443 
1444     if(w->normal.parent == w->root)
1445       XSetClassHint(w->dpy, w->normal.win, &xclasshint);
1446 
1447     if(w->fullscreen.parent == w->root)
1448       XSetClassHint(w->dpy, w->fullscreen.win, &xclasshint);
1449 
1450     free(xclasshint.res_name);
1451     free(xclasshint.res_class);
1452     }
1453 
1454   /* Set Icon (TODO) */
1455   if(icon && icon_format)
1456     {
1457     XWMHints xwmhints;
1458     memset(&xwmhints, 0, sizeof(xwmhints));
1459 
1460     if((w->normal.parent == w->root) ||
1461        (w->fullscreen.parent == w->root))
1462       {
1463       if(w->icon != None)
1464         {
1465         XFreePixmap(w->dpy, w->icon);
1466         w->icon = None;
1467         }
1468       if(w->icon_mask != None)
1469         {
1470         XFreePixmap(w->dpy, w->icon_mask);
1471         w->icon_mask = None;
1472         }
1473 
1474       bg_x11_window_make_icon(w,
1475                               icon,
1476                               icon_format,
1477                               &w->icon,
1478                               &w->icon_mask);
1479 
1480       xwmhints.icon_pixmap = w->icon;
1481       xwmhints.icon_mask   = w->icon_mask;
1482 
1483       if(xwmhints.icon_pixmap != None)
1484         xwmhints.flags |= IconPixmapHint;
1485 
1486       if(xwmhints.icon_mask != None)
1487         xwmhints.flags |= IconMaskHint;
1488 
1489       if(w->normal.parent == w->root)
1490         XSetWMHints(w->dpy, w->normal.win, &xwmhints);
1491       if(w->fullscreen.parent == w->root)
1492         XSetWMHints(w->dpy, w->fullscreen.win, &xwmhints);
1493       }
1494     }
1495   }
1496 
bg_x11_window_show(bg_x11_window_t * win,int show)1497 void bg_x11_window_show(bg_x11_window_t * win, int show)
1498   {
1499   show_window(win, win->current, show);
1500 
1501   if(show && check_disable_screensaver(win))
1502     bg_x11_screensaver_disable(&win->scr);
1503   else
1504     bg_x11_screensaver_enable(&win->scr);
1505 
1506   if(!show)
1507     {
1508     XSync(win->dpy, False);
1509     bg_x11_window_handle_events(win, 0);
1510     }
1511   }
1512 
bg_x11_window_get_toplevel(bg_x11_window_t * w,Window win)1513 Window bg_x11_window_get_toplevel(bg_x11_window_t * w, Window win)
1514   {
1515   Window *children_return;
1516   Window root_return;
1517   Window parent_return;
1518   Atom     type_ret;
1519   int      format_ret;
1520   unsigned long   nitems_ret;
1521   unsigned long   bytes_after_ret;
1522   unsigned char  *prop_ret;
1523 
1524   unsigned int nchildren_return;
1525   while(1)
1526     {
1527     XGetWindowProperty(w->dpy, win,
1528                        w->WM_CLASS, 0L, 0L, 0,
1529                        w->STRING,
1530                        &type_ret,&format_ret,&nitems_ret,
1531                        &bytes_after_ret,&prop_ret);
1532     if(type_ret!=None)
1533       {
1534       XFree(prop_ret);
1535       return win;
1536       }
1537     XQueryTree(w->dpy, win, &root_return, &parent_return,
1538                &children_return, &nchildren_return);
1539     if(nchildren_return)
1540       XFree(children_return);
1541     if(parent_return == root_return)
1542       break;
1543     win = parent_return;
1544     }
1545   return win;
1546   }
1547 
1548 void
bg_x11_window_send_xembed_message(bg_x11_window_t * w,Window win,long time,int message,int detail,int data1,int data2)1549 bg_x11_window_send_xembed_message(bg_x11_window_t * w, Window win,
1550                                   long time, int message, int detail,
1551                                   int data1, int data2)
1552   {
1553   XClientMessageEvent xclient;
1554 
1555   xclient.window = win;
1556   xclient.type = ClientMessage;
1557   xclient.message_type = w->_XEMBED;
1558   xclient.format = 32;
1559   xclient.data.l[0] = time;
1560   xclient.data.l[1] = message;
1561   xclient.data.l[2] = detail;
1562   xclient.data.l[3] = data1;
1563   xclient.data.l[4] = data2;
1564 
1565   XSendEvent(w->dpy, win,
1566              False, NoEventMask, (XEvent *)&xclient);
1567   XSync(w->dpy, False);
1568   }
1569 
bg_x11_window_check_embed_property(bg_x11_window_t * win,window_t * w)1570 int bg_x11_window_check_embed_property(bg_x11_window_t * win,
1571                                        window_t * w)
1572   {
1573   Atom type;
1574   int format;
1575   unsigned long nitems, bytes_after;
1576   unsigned char *data;
1577   unsigned long *data_long;
1578   int flags;
1579   if(XGetWindowProperty(win->dpy, w->child,
1580                         win->_XEMBED_INFO,
1581                         0, 2, False,
1582                         win->_XEMBED_INFO, &type, &format,
1583                         &nitems, &bytes_after, &data) != Success)
1584     return 0;
1585 
1586   if (type == None)             /* No info property */
1587     return 0;
1588   if (type != win->_XEMBED_INFO)
1589     return 0;
1590 
1591   data_long = (unsigned long *)data;
1592   flags = data_long[1];
1593   XFree(data);
1594 
1595   if(flags & XEMBED_MAPPED)
1596     {
1597     XMapWindow(win->dpy, w->child);
1598     XRaiseWindow(win->dpy, w->focus_child);
1599     }
1600   else
1601     {
1602     /* Window should not be mapped right now */
1603     //    XUnmapWindow(win->dpy, w->child);
1604     }
1605 
1606   if(!w->child_xembed)
1607     {
1608     w->child_xembed = 1;
1609     bg_x11_window_send_xembed_message(win,
1610                                       w->child,
1611                                       CurrentTime,
1612                                       XEMBED_EMBEDDED_NOTIFY,
1613                                       0,
1614                                       w->win, 0);
1615     XFlush(win->dpy);
1616     }
1617   return 1;
1618   }
1619 
1620 gavl_pixelformat_t
bg_x11_window_get_pixelformat(Display * dpy,Visual * visual,int depth)1621 bg_x11_window_get_pixelformat(Display * dpy, Visual * visual, int depth)
1622   {
1623   int bpp;
1624   XPixmapFormatValues * pf;
1625   int i;
1626   int num_pf;
1627   gavl_pixelformat_t ret = GAVL_PIXELFORMAT_NONE;
1628 
1629   bpp = 0;
1630   pf = XListPixmapFormats(dpy, &num_pf);
1631   for(i = 0; i < num_pf; i++)
1632     {
1633     if(pf[i].depth == depth)
1634       bpp = pf[i].bits_per_pixel;
1635     }
1636   XFree(pf);
1637 
1638   ret = GAVL_PIXELFORMAT_NONE;
1639   switch(bpp)
1640     {
1641     case 16:
1642       if((visual->red_mask == 63488) &&
1643          (visual->green_mask == 2016) &&
1644          (visual->blue_mask == 31))
1645         ret = GAVL_RGB_16;
1646       else if((visual->blue_mask == 63488) &&
1647               (visual->green_mask == 2016) &&
1648               (visual->red_mask == 31))
1649         ret = GAVL_BGR_16;
1650       break;
1651     case 24:
1652       if((visual->red_mask == 0xff) &&
1653          (visual->green_mask == 0xff00) &&
1654          (visual->blue_mask == 0xff0000))
1655         ret = GAVL_RGB_24;
1656       else if((visual->red_mask == 0xff0000) &&
1657          (visual->green_mask == 0xff00) &&
1658          (visual->blue_mask == 0xff))
1659         ret = GAVL_BGR_24;
1660       break;
1661     case 32:
1662       if((visual->red_mask == 0xff) &&
1663          (visual->green_mask == 0xff00) &&
1664          (visual->blue_mask == 0xff0000))
1665         ret = GAVL_RGB_32;
1666       else if((visual->red_mask == 0xff0000) &&
1667          (visual->green_mask == 0xff00) &&
1668          (visual->blue_mask == 0xff))
1669         ret = GAVL_BGR_32;
1670       break;
1671     }
1672   return ret;
1673   }
1674 
1675