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