]> git.decadent.org.uk Git - ion3.git/blob - ioncore/rootwin.c
[svn-inject] Installing original source of ion3
[ion3.git] / ioncore / rootwin.c
1 /*
2  * ion/ioncore/rootwin.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2006. 
5  *
6  * Ion is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  */
11
12 #include <unistd.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <sys/time.h>
16 #include <time.h>
17 #include <signal.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <X11/Xlib.h>
22 #include <X11/Xproto.h>
23 /*#include <X11/Xmu/Error.h>*/
24 #ifdef CF_XINERAMA
25 #include <X11/extensions/Xinerama.h>
26 #elif defined(CF_SUN_XINERAMA)
27 #include <X11/extensions/xinerama.h>
28 #endif
29
30 #include <libtu/objp.h>
31 #include "common.h"
32 #include "rootwin.h"
33 #include "cursor.h"
34 #include "global.h"
35 #include "event.h"
36 #include "gr.h"
37 #include "clientwin.h"
38 #include "property.h"
39 #include "focus.h"
40 #include "regbind.h"
41 #include "screen.h"
42 #include "screen.h"
43 #include "bindmaps.h"
44 #include <libextl/readconfig.h>
45 #include "resize.h"
46 #include "saveload.h"
47 #include "netwm.h"
48 #include "xwindow.h"
49
50
51 /*{{{ Error handling */
52
53
54 static bool redirect_error=FALSE;
55 static bool ignore_badwindow=TRUE;
56
57
58 static int my_redirect_error_handler(Display *dpy, XErrorEvent *ev)
59 {
60     redirect_error=TRUE;
61     return 0;
62 }
63
64
65 static int my_error_handler(Display *dpy, XErrorEvent *ev)
66 {
67     static char msg[128], request[64], num[32];
68     
69     /* Just ignore bad window and similar errors; makes the rest of
70      * the code simpler.
71      */
72     if((ev->error_code==BadWindow ||
73         (ev->error_code==BadMatch && ev->request_code==X_SetInputFocus) ||
74         (ev->error_code==BadDrawable && ev->request_code==X_GetGeometry)) &&
75        ignore_badwindow)
76         return 0;
77
78 #if 0
79     XmuPrintDefaultErrorMessage(dpy, ev, stderr);
80 #else
81     XGetErrorText(dpy, ev->error_code, msg, 128);
82     snprintf(num, 32, "%d", ev->request_code);
83     XGetErrorDatabaseText(dpy, "XRequest", num, "", request, 64);
84
85     if(request[0]=='\0')
86         snprintf(request, 64, "<unknown request>");
87
88     if(ev->minor_code!=0){
89         warn("[%d] %s (%d.%d) %#lx: %s", ev->serial, request,
90              ev->request_code, ev->minor_code, ev->resourceid,msg);
91     }else{
92         warn("[%d] %s (%d) %#lx: %s", ev->serial, request,
93              ev->request_code, ev->resourceid,msg);
94     }
95 #endif
96
97     kill(getpid(), SIGTRAP);
98     
99     return 0;
100 }
101
102
103 /*}}}*/
104
105
106 /*{{{ Init/deinit */
107
108
109 static void scan_initial_windows(WRootWin *rootwin)
110 {
111     Window dummy_root, dummy_parent, *wins=NULL;
112     uint nwins=0, i, j;
113     XWMHints *hints;
114     
115     XQueryTree(ioncore_g.dpy, WROOTWIN_ROOT(rootwin), &dummy_root, &dummy_parent,
116                &wins, &nwins);
117     
118     for(i=0; i<nwins; i++){
119         if(wins[i]==None)
120             continue;
121         hints=XGetWMHints(ioncore_g.dpy, wins[i]);
122         if(hints!=NULL && hints->flags&IconWindowHint){
123             for(j=0; j<nwins; j++){
124                 if(wins[j]==hints->icon_window){
125                     wins[j]=None;
126                     break;
127                 }
128             }
129         }
130         if(hints!=NULL)
131             XFree((void*)hints);
132     }
133     
134     rootwin->tmpwins=wins;
135     rootwin->tmpnwins=nwins;
136 }
137
138
139 void rootwin_manage_initial_windows(WRootWin *rootwin)
140 {
141     Window *wins=rootwin->tmpwins;
142     Window tfor=None;
143     int i, nwins=rootwin->tmpnwins;
144
145     rootwin->tmpwins=NULL;
146     rootwin->tmpnwins=0;
147     
148     for(i=0; i<nwins; i++){
149         if(XWINDOW_REGION_OF(wins[i])!=NULL)
150             wins[i]=None;
151         if(wins[i]==None)
152             continue;
153         if(XGetTransientForHint(ioncore_g.dpy, wins[i], &tfor))
154             continue;
155         ioncore_manage_clientwin(wins[i], FALSE);
156         wins[i]=None;
157     }
158
159     for(i=0; i<nwins; i++){
160         if(wins[i]==None)
161             continue;
162         ioncore_manage_clientwin(wins[i], FALSE);
163     }
164     
165     XFree((void*)wins);
166 }
167
168
169 static void create_wm_windows(WRootWin *rootwin)
170 {
171     rootwin->dummy_win=XCreateWindow(ioncore_g.dpy, WROOTWIN_ROOT(rootwin),
172                                      0, 0, 1, 1, 0,
173                                      CopyFromParent, InputOnly,
174                                      CopyFromParent, 0, NULL);
175
176     XSelectInput(ioncore_g.dpy, rootwin->dummy_win, PropertyChangeMask);
177 }
178
179
180 static void preinit_gr(WRootWin *rootwin)
181 {
182     XGCValues gcv;
183     ulong gcvmask;
184
185     /* Create XOR gc (for resize) */
186     gcv.line_style=LineSolid;
187     gcv.join_style=JoinBevel;
188     gcv.cap_style=CapButt;
189     gcv.fill_style=FillSolid;
190     gcv.line_width=2;
191     gcv.subwindow_mode=IncludeInferiors;
192     gcv.function=GXxor;
193     gcv.foreground=~0L;
194     
195     gcvmask=(GCLineStyle|GCLineWidth|GCFillStyle|
196              GCJoinStyle|GCCapStyle|GCFunction|
197              GCSubwindowMode|GCForeground);
198
199     rootwin->xor_gc=XCreateGC(ioncore_g.dpy, WROOTWIN_ROOT(rootwin), 
200                               gcvmask, &gcv);
201 }
202
203
204 static WRootWin *preinit_rootwin(int xscr)
205 {
206     Display *dpy=ioncore_g.dpy;
207     WRootWin *rootwin;
208     WFitParams fp;
209     Window root;
210     int i;
211     
212     /* Try to select input on the root window */
213     root=RootWindow(dpy, xscr);
214     
215     redirect_error=FALSE;
216
217     XSetErrorHandler(my_redirect_error_handler);
218     XSelectInput(dpy, root, IONCORE_EVENTMASK_ROOT);
219     XSync(dpy, 0);
220     XSetErrorHandler(my_error_handler);
221
222     if(redirect_error){
223         warn(TR("Unable to redirect root window events for screen %d."),
224              xscr);
225         return NULL;
226     }
227     
228     rootwin=ALLOC(WRootWin);
229     
230     if(rootwin==NULL)
231         return NULL;
232     
233     /* Init the struct */
234     OBJ_INIT(rootwin, WRootWin);
235
236     rootwin->xscr=xscr;
237     rootwin->default_cmap=DefaultColormap(dpy, xscr);
238     rootwin->tmpwins=NULL;
239     rootwin->tmpnwins=0;
240     rootwin->dummy_win=None;
241     rootwin->xor_gc=None;
242
243     fp.mode=REGION_FIT_EXACT;
244     fp.g.x=0; fp.g.y=0;
245     fp.g.w=DisplayWidth(dpy, xscr);
246     fp.g.h=DisplayHeight(dpy, xscr);
247     
248     if(!window_do_init((WWindow*)rootwin, NULL, root, &fp)){
249         free(rootwin);
250         return NULL;
251     }
252
253     /* Note: this mask isn't right if some WScreen auses the same window. */
254     ((WWindow*)rootwin)->event_mask=IONCORE_EVENTMASK_ROOT;
255     
256     ((WRegion*)rootwin)->flags|=REGION_BINDINGS_ARE_GRABBED|REGION_PLEASE_WARP;
257     ((WRegion*)rootwin)->rootwin=rootwin;
258     
259     REGION_MARK_MAPPED(rootwin);
260     
261     scan_initial_windows(rootwin);
262
263     create_wm_windows(rootwin);
264     preinit_gr(rootwin);
265     netwm_init_rootwin(rootwin);
266     
267     region_add_bindmap((WRegion*)rootwin, ioncore_rootwin_bindmap);
268     
269     return rootwin;
270 }
271
272
273 static Atom net_virtual_roots=None;
274
275
276 static WScreen *add_screen(WRootWin *rw, int id, const WRectangle *geom, 
277                            bool useroot)
278 {
279     WScreen *scr;
280     CARD32 p[1];
281     WFitParams fp;
282     
283     fp.g=*geom;
284     fp.mode=REGION_FIT_EXACT;
285     
286 #ifdef CF_ALWAYS_VIRTUAL_ROOT
287     useroot=FALSE;
288 #endif
289
290     scr=create_screen(rw, id, &fp, useroot);
291     
292     if(scr==NULL)
293         return NULL;
294     
295     region_set_manager((WRegion*)scr, (WRegion*)rw);
296     
297     region_map((WRegion*)scr);
298
299     if(!useroot){
300         p[0]=region_xwindow((WRegion*)scr);
301         XChangeProperty(ioncore_g.dpy, WROOTWIN_ROOT(rw), net_virtual_roots,
302                         XA_WINDOW, 32, PropModeAppend, (uchar*)&(p[0]), 1);
303     }
304
305     return scr;
306 }
307
308
309 #ifdef CF_XINERAMA
310 static bool xinerama_sanity_check(XineramaScreenInfo *xi, int nxi)
311 {
312     int i, j;
313
314     for(i=0; i<nxi; i++){
315         for(j=0; j<nxi; j++){
316             if(i!=j &&
317                (xi[j].x_org>=xi[i].x_org && xi[j].x_org<xi[i].x_org+xi[i].width) &&
318                (xi[j].y_org>=xi[i].y_org && xi[j].y_org<xi[i].y_org+xi[i].height)){
319                 warn(TR("Xinerama sanity check failed; overlapping "
320                         "screens detected."));
321                 return FALSE;
322             }
323         }
324         
325         if(xi[i].width<=0 || xi[i].height<=0){
326             warn(TR("Xinerama sanity check failed; zero size detected."));
327             return FALSE;
328         }
329     }
330     return TRUE;
331 }
332 #elif defined(CF_SUN_XINERAMA)
333 static bool xinerama_sanity_check(XRectangle *monitors, int nxi)
334 {
335     int i, j;
336
337     for(i=0; i<nxi; i++){
338         for(j=0; j<nxi; j++){
339             if(i!=j &&
340                (monitors[j].x>=monitors[i].x &&
341                 monitors[j].x<monitors[i].x+monitors[i].width) &&
342                (monitors[j].y>=monitors[i].y &&
343                 monitors[j].y<monitors[i].y+monitors[i].height)){
344                 warn(TR("Xinerama sanity check failed; overlapping "
345                         "screens detected."));
346                 return FALSE;
347             }
348         }
349         
350         if(monitors[i].width<=0 || monitors[i].height<=0){
351             warn(TR("Xinerama sanity check failed; zero size detected."));
352             return FALSE;
353         }
354     }
355     return TRUE;
356 }
357 #endif
358
359
360 WRootWin *ioncore_manage_rootwin(int xscr, bool noxinerama)
361 {
362     WRootWin *rootwin;
363     int nxi=0, fail=0;
364 #ifdef CF_XINERAMA
365     XineramaScreenInfo *xi=NULL;
366     int i;
367     int event_base, error_base;
368 #elif defined(CF_SUN_XINERAMA)
369     XRectangle monitors[MAXFRAMEBUFFERS];
370     int i;
371 #endif
372
373     if(!noxinerama){
374 #ifdef CF_XINERAMA
375         if(XineramaQueryExtension(ioncore_g.dpy, &event_base, &error_base)){
376             xi=XineramaQueryScreens(ioncore_g.dpy, &nxi);
377
378             if(xi!=NULL && ioncore_g.rootwins!=NULL){
379                 warn(TR("Don't know how to get Xinerama information for "
380                         "multiple X root windows."));
381                 XFree(xi);
382                 xi=NULL;
383                 nxi=0;
384             }
385         }
386 #elif defined(CF_SUN_XINERAMA)
387         if(XineramaGetState(ioncore_g.dpy, xscr)){
388             unsigned char hints[16];
389             int num;
390
391             if(XineramaGetInfo(ioncore_g.dpy, xscr, monitors, hints,
392                                &nxi)==0){
393                 warn(TR("Error retrieving Xinerama information."));
394                 nxi=0;
395             }else{
396                 if(ioncore_g.rootwins!=NULL){
397                     warn(TR("Don't know how to get Xinerama information for "
398                             "multiple X root windows."));
399                     nxi=0;
400                 }
401             }
402         }
403 #endif
404     }
405
406     rootwin=preinit_rootwin(xscr);
407
408     if(rootwin==NULL){
409 #ifdef CF_XINERAMA
410         if(xi!=NULL)
411             XFree(xi);
412 #endif
413         return NULL;
414     }
415
416     net_virtual_roots=XInternAtom(ioncore_g.dpy, "_NET_VIRTUAL_ROOTS", False);
417     XDeleteProperty(ioncore_g.dpy, WROOTWIN_ROOT(rootwin), net_virtual_roots);
418
419 #ifdef CF_XINERAMA
420     if(xi!=NULL && nxi!=0 && xinerama_sanity_check(xi, nxi)){
421         bool useroot=FALSE;
422         WRectangle geom;
423
424         for(i=0; i<nxi; i++){
425             geom.x=xi[i].x_org;
426             geom.y=xi[i].y_org;
427             geom.w=xi[i].width;
428             geom.h=xi[i].height;
429             /*if(nxi==1)
430                 useroot=(geom.x==0 && geom.y==0);*/
431             if(!add_screen(rootwin, i, &geom, useroot)){
432                 warn(TR("Unable to setup Xinerama screen %d."), i);
433                 fail++;
434             }
435         }
436         XFree(xi);
437     }else
438 #elif defined(CF_SUN_XINERAMA)
439     if(nxi!=0 && xinerama_sanity_check(monitors, nxi)){
440         bool useroot=FALSE;
441         WRectangle geom;
442
443         for(i=0; i<nxi; i++){
444             geom.x=monitors[i].x;
445             geom.y=monitors[i].y;
446             geom.w=monitors[i].width;
447             geom.h=monitors[i].height;
448             /*if(nxi==1)
449                 useroot=(geom.x==0 && geom.y==0);*/
450             if(!add_screen(rootwin, i, &geom, useroot)){
451                 warn(TR("Unable to setup Xinerama screen %d."), i);
452                 fail++;
453             }
454         }
455     }else
456 #endif
457     {
458         nxi=1;
459         if(!add_screen(rootwin, xscr, &REGION_GEOM(rootwin), TRUE))
460             fail++;
461     }
462     
463     if(fail==nxi){
464         warn(TR("Unable to setup X screen %d."), xscr);
465         destroy_obj((Obj*)rootwin);
466         return NULL;
467     }
468     
469     /* */ {
470         /* TODO: typed LINK_ITEM */
471         WRegion *tmp=(WRegion*)ioncore_g.rootwins;
472         LINK_ITEM(tmp, (WRegion*)rootwin, p_next, p_prev);
473         ioncore_g.rootwins=(WRootWin*)tmp;
474     }
475
476     xwindow_set_cursor(WROOTWIN_ROOT(rootwin), IONCORE_CURSOR_DEFAULT);
477     
478     return rootwin;
479 }
480
481
482 void rootwin_deinit(WRootWin *rw)
483 {
484     WScreen *scr, *next;
485
486     FOR_ALL_SCREENS_W_NEXT(scr, next){
487         if(REGION_MANAGER(scr)==(WRegion*)rw)
488             destroy_obj((Obj*)scr);
489     }
490     
491     /* */ {
492         WRegion *tmp=(WRegion*)ioncore_g.rootwins;
493         UNLINK_ITEM(tmp, (WRegion*)rw, p_next, p_prev);
494         ioncore_g.rootwins=(WRootWin*)tmp;
495     }
496     
497     XSelectInput(ioncore_g.dpy, WROOTWIN_ROOT(rw), 0);
498     
499     XFreeGC(ioncore_g.dpy, rw->xor_gc);
500     
501     window_deinit((WWindow*)rw);
502 }
503
504
505 /*}}}*/
506
507
508 /*{{{ region dynfun implementations */
509
510
511 static void rootwin_do_set_focus(WRootWin *rootwin, bool warp)
512 {
513     WRegion *sub;
514     
515     sub=REGION_ACTIVE_SUB(rootwin);
516     
517     if(sub==NULL || !REGION_IS_MAPPED(sub)){
518         WScreen *scr;
519         FOR_ALL_SCREENS(scr){
520             if(REGION_IS_MAPPED(scr)){
521                 sub=(WRegion*)scr;
522                 break;
523             }
524         }
525     }
526
527     if(sub!=NULL)
528         region_do_set_focus(sub, warp);
529     else
530         window_do_set_focus((WWindow*)rootwin, warp);
531 }
532
533
534 static bool rootwin_fitrep(WRootWin *rootwin, WWindow *par, 
535                            const WFitParams *fp)
536 {
537     D(warn("Don't know how to reparent or fit root windows."));
538     return FALSE;
539 }
540
541
542 static void rootwin_map(WRootWin *rootwin)
543 {
544     D(warn("Attempt to map a root window."));
545 }
546
547
548 static void rootwin_unmap(WRootWin *rootwin)
549 {
550     D(warn("Attempt to unmap a root window -- impossible."));
551 }
552
553
554 static void rootwin_managed_remove(WRootWin *rootwin, WRegion *reg)
555 {
556     region_unset_manager(reg, (WRegion*)rootwin);
557 }
558
559
560 static Window rootwin_x_window(WRootWin *rootwin)
561 {
562     return WROOTWIN_ROOT(rootwin);
563 }
564
565
566 /*}}}*/
567
568
569 /*{{{ Misc */
570
571
572 static bool scr_ok(WRegion *r)
573 {
574     return (OBJ_IS(r, WScreen) && REGION_IS_MAPPED(r));
575 }
576
577
578 /*EXTL_DOC
579  * Returns previously active screen on root window \var{rootwin}.
580  */
581 EXTL_SAFE
582 EXTL_EXPORT_MEMBER
583 WScreen *rootwin_current_scr(WRootWin *rootwin)
584 {
585     WRegion *r=REGION_ACTIVE_SUB(rootwin);
586     WScreen *scr;
587     
588     /* There should be no non-WScreen as children or managed by us, but... */
589     
590     if(r!=NULL && scr_ok(r))
591         return (WScreen*)r;
592     
593     FOR_ALL_SCREENS(scr){
594         if(REGION_MANAGER(scr)==(WRegion*)rootwin
595            && REGION_IS_MAPPED(scr)){
596             break;
597         }
598     }
599     
600     return scr;
601 }
602
603
604 /*}}}*/
605
606
607 /*{{{ Dynamic function table and class implementation */
608
609
610 static DynFunTab rootwin_dynfuntab[]={
611     {region_map, rootwin_map},
612     {region_unmap, rootwin_unmap},
613     {region_do_set_focus, rootwin_do_set_focus},
614     {(DynFun*)region_xwindow, (DynFun*)rootwin_x_window},
615     {(DynFun*)region_fitrep, (DynFun*)rootwin_fitrep},
616     {region_managed_remove, rootwin_managed_remove},
617     END_DYNFUNTAB
618 };
619
620
621 EXTL_EXPORT
622 IMPLCLASS(WRootWin, WWindow, rootwin_deinit, rootwin_dynfuntab);
623
624     
625 /*}}}*/