Released version 3-2014010505
[notion.git] / ioncore / rootwin.c
blobaa6ff35a0e3625dc9bcf3251bd850b46408fa885
1 /*
2 * ion/ioncore/rootwin.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <unistd.h>
10 #include <sys/types.h>
11 #include <sys/wait.h>
12 #include <sys/time.h>
13 #include <time.h>
14 #include <signal.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <X11/Xlib.h>
19 #include <X11/Xproto.h>
21 #include <libtu/objp.h>
22 #include "common.h"
23 #include "rootwin.h"
24 #include "cursor.h"
25 #include "global.h"
26 #include "event.h"
27 #include "gr.h"
28 #include "clientwin.h"
29 #include "property.h"
30 #include "focus.h"
31 #include "regbind.h"
32 #include "screen.h"
33 #include "bindmaps.h"
34 #include <libextl/readconfig.h>
35 #include "resize.h"
36 #include "saveload.h"
37 #include "netwm.h"
38 #include "xwindow.h"
41 /*{{{ Error handling */
44 static bool redirect_error=FALSE;
45 static bool ignore_badwindow=TRUE;
48 static int my_redirect_error_handler(Display *dpy, XErrorEvent *ev)
50 redirect_error=TRUE;
51 return 0;
55 static int my_error_handler(Display *dpy, XErrorEvent *ev)
57 static char msg[128], request[64], num[32];
59 /* Just ignore bad window and similar errors; makes the rest of
60 * the code simpler.
62 * Apparently XGetWindowProperty can return BadMatch on a race
63 * condition where the server is already reusing the XID for a
64 * non-window drawable, so let's just ignore BadMatch entirely...
66 if((ev->error_code==BadWindow ||
67 (ev->error_code==BadMatch /*&& ev->request_code==X_SetInputFocus*/) ||
68 (ev->error_code==BadDrawable && ev->request_code==X_GetGeometry)) &&
69 ignore_badwindow)
70 return 0;
72 #if 0
73 XmuPrintDefaultErrorMessage(dpy, ev, stderr);
74 #else
75 XGetErrorText(dpy, ev->error_code, msg, 128);
76 snprintf(num, 32, "%d", ev->request_code);
77 XGetErrorDatabaseText(dpy, "XRequest", num, "", request, 64);
79 if(request[0]=='\0')
80 snprintf(request, 64, "<unknown request>");
82 if(ev->minor_code!=0){
83 warn("[%d] %s (%d.%d) %#lx: %s", ev->serial, request,
84 ev->request_code, ev->minor_code, ev->resourceid,msg);
85 }else{
86 warn("[%d] %s (%d) %#lx: %s", ev->serial, request,
87 ev->request_code, ev->resourceid,msg);
89 #endif
91 kill(getpid(), SIGTRAP);
93 return 0;
97 /*}}}*/
100 /*{{{ Init/deinit */
103 static void scan_initial_windows(WRootWin *rootwin)
105 Window dummy_root, dummy_parent, *wins=NULL;
106 uint nwins=0, i, j;
107 XWMHints *hints;
109 XQueryTree(ioncore_g.dpy, WROOTWIN_ROOT(rootwin), &dummy_root, &dummy_parent,
110 &wins, &nwins);
112 for(i=0; i<nwins; i++){
113 if(wins[i]==None)
114 continue;
115 hints=XGetWMHints(ioncore_g.dpy, wins[i]);
116 if(hints!=NULL && hints->flags&IconWindowHint){
117 for(j=0; j<nwins; j++){
118 if(wins[j]==hints->icon_window){
119 wins[j]=None;
120 break;
124 if(hints!=NULL)
125 XFree((void*)hints);
128 rootwin->tmpwins=wins;
129 rootwin->tmpnwins=nwins;
133 void rootwin_manage_initial_windows(WRootWin *rootwin)
135 Window *wins=rootwin->tmpwins;
136 Window tfor=None;
137 int i, nwins=rootwin->tmpnwins;
139 rootwin->tmpwins=NULL;
140 rootwin->tmpnwins=0;
142 for(i=0; i<nwins; i++){
143 if(XWINDOW_REGION_OF(wins[i])!=NULL)
144 wins[i]=None;
145 if(wins[i]==None)
146 continue;
147 if(XGetTransientForHint(ioncore_g.dpy, wins[i], &tfor))
148 continue;
149 ioncore_manage_clientwin(wins[i], FALSE);
150 wins[i]=None;
153 for(i=0; i<nwins; i++){
154 if(wins[i]==None)
155 continue;
156 ioncore_manage_clientwin(wins[i], FALSE);
159 XFree((void*)wins);
163 static void create_wm_windows(WRootWin *rootwin)
165 const char *p[1];
167 rootwin->dummy_win=XCreateWindow(ioncore_g.dpy, WROOTWIN_ROOT(rootwin),
168 0, 0, 1, 1, 0,
169 CopyFromParent, InputOnly,
170 CopyFromParent, 0, NULL);
172 p[0] = "WRootWin";
173 xwindow_set_text_property(rootwin->dummy_win, XA_WM_NAME, p, 1);
175 XSelectInput(ioncore_g.dpy, rootwin->dummy_win, PropertyChangeMask);
179 static void preinit_gr(WRootWin *rootwin)
181 XGCValues gcv;
182 ulong gcvmask;
184 /* Create XOR gc (for resize) */
185 gcv.line_style=LineSolid;
186 gcv.join_style=JoinBevel;
187 gcv.cap_style=CapButt;
188 gcv.fill_style=FillSolid;
189 gcv.line_width=2;
190 gcv.subwindow_mode=IncludeInferiors;
191 gcv.function=GXxor;
192 gcv.foreground=~0L;
194 gcvmask=(GCLineStyle|GCLineWidth|GCFillStyle|
195 GCJoinStyle|GCCapStyle|GCFunction|
196 GCSubwindowMode|GCForeground);
198 rootwin->xor_gc=XCreateGC(ioncore_g.dpy, WROOTWIN_ROOT(rootwin),
199 gcvmask, &gcv);
203 static Atom net_virtual_roots=None;
206 static bool rootwin_init(WRootWin *rootwin, int xscr)
208 Display *dpy=ioncore_g.dpy;
209 WFitParams fp;
210 Window root;
211 WScreen *scr;
213 /* Try to select input on the root window */
214 root=RootWindow(dpy, xscr);
216 redirect_error=FALSE;
218 XSetErrorHandler(my_redirect_error_handler);
219 XSelectInput(dpy, root, IONCORE_EVENTMASK_ROOT);
220 XSync(dpy, 0);
221 XSetErrorHandler(my_error_handler);
223 if(redirect_error){
224 warn(TR("Unable to redirect root window events for screen %d."
225 "Maybe another window manager is running?"),
226 xscr);
227 return FALSE;
230 rootwin->xscr=xscr;
231 rootwin->default_cmap=DefaultColormap(dpy, xscr);
232 rootwin->tmpwins=NULL;
233 rootwin->tmpnwins=0;
234 rootwin->dummy_win=None;
235 rootwin->xor_gc=None;
237 fp.mode=REGION_FIT_EXACT;
238 fp.g.x=0; fp.g.y=0;
239 fp.g.w=DisplayWidth(dpy, xscr);
240 fp.g.h=DisplayHeight(dpy, xscr);
242 if(!window_do_init((WWindow*)rootwin, NULL, &fp, root, "WRootWin")){
243 free(rootwin);
244 return FALSE;
247 ((WWindow*)rootwin)->event_mask=IONCORE_EVENTMASK_ROOT;
248 ((WRegion*)rootwin)->flags|=REGION_BINDINGS_ARE_GRABBED|REGION_PLEASE_WARP;
249 ((WRegion*)rootwin)->rootwin=rootwin;
251 REGION_MARK_MAPPED(rootwin);
253 scan_initial_windows(rootwin);
255 create_wm_windows(rootwin);
256 preinit_gr(rootwin);
257 netwm_init_rootwin(rootwin);
259 region_add_bindmap((WRegion*)rootwin, ioncore_screen_bindmap);
261 scr=create_screen(rootwin, &fp, xscr);
262 if(scr==NULL){
263 free(rootwin);
264 return FALSE;
266 region_set_manager((WRegion*)scr, (WRegion*)rootwin);
267 region_map((WRegion*)scr);
269 LINK_ITEM(*(WRegion**)&ioncore_g.rootwins, (WRegion*)rootwin, p_next, p_prev);
271 ioncore_screens_updated(rootwin);
273 xwindow_set_cursor(root, IONCORE_CURSOR_DEFAULT);
275 return TRUE;
279 WRootWin *create_rootwin(int xscr)
281 CREATEOBJ_IMPL(WRootWin, rootwin, (p, xscr));
285 void rootwin_deinit(WRootWin *rw)
287 WScreen *scr, *next;
289 FOR_ALL_SCREENS_W_NEXT(scr, next){
290 if(REGION_MANAGER(scr)==(WRegion*)rw)
291 destroy_obj((Obj*)scr);
294 UNLINK_ITEM(*(WRegion**)&ioncore_g.rootwins, (WRegion*)rw, p_next, p_prev);
296 XSelectInput(ioncore_g.dpy, WROOTWIN_ROOT(rw), 0);
298 XFreeGC(ioncore_g.dpy, rw->xor_gc);
300 window_deinit((WWindow*)rw);
304 /*}}}*/
307 /*{{{ region dynfun implementations */
310 static void rootwin_do_set_focus(WRootWin *rootwin, bool warp)
312 WRegion *sub;
314 sub=REGION_ACTIVE_SUB(rootwin);
316 if(sub==NULL || !REGION_IS_MAPPED(sub)){
317 WScreen *scr;
318 FOR_ALL_SCREENS(scr){
319 if(REGION_IS_MAPPED(scr)){
320 sub=(WRegion*)scr;
321 break;
326 if(sub!=NULL)
327 region_do_set_focus(sub, warp);
328 else
329 window_do_set_focus((WWindow*)rootwin, warp);
333 static bool rootwin_fitrep(WRootWin *rootwin, WWindow *par,
334 const WFitParams *fp)
336 D(warn("Don't know how to reparent or fit root windows."));
337 return FALSE;
341 static void rootwin_map(WRootWin *rootwin)
343 D(warn("Attempt to map a root window."));
347 static void rootwin_unmap(WRootWin *rootwin)
349 D(warn("Attempt to unmap a root window -- impossible."));
353 static void rootwin_managed_remove(WRootWin *rootwin, WRegion *reg)
355 region_unset_manager(reg, (WRegion*)rootwin);
358 static WRegion *rootwin_managed_disposeroot(WRootWin *rootwin, WRegion *reg)
360 WScreen *scr=OBJ_CAST(reg, WScreen);
361 if(scr!=NULL && scr==scr->prev_scr){
362 warn(TR("Only screen may not be destroyed/detached."));
363 return NULL;
366 return reg;
371 static Window rootwin_x_window(WRootWin *rootwin)
373 return WROOTWIN_ROOT(rootwin);
377 /*}}}*/
380 /*{{{ Misc */
383 static bool scr_ok(WRegion *r)
385 return (OBJ_IS(r, WScreen) && REGION_IS_MAPPED(r));
389 /*EXTL_DOC
390 * Returns previously active screen on root window \var{rootwin}.
392 EXTL_SAFE
393 EXTL_EXPORT_MEMBER
394 WScreen *rootwin_current_scr(WRootWin *rootwin)
396 WRegion *r=REGION_ACTIVE_SUB(rootwin);
397 WScreen *scr;
399 /* There should be no non-WScreen as children or managed by us, but... */
401 if(r!=NULL && scr_ok(r))
402 return (WScreen*)r;
404 FOR_ALL_SCREENS(scr){
405 if(REGION_MANAGER(scr)==(WRegion*)rootwin
406 && REGION_IS_MAPPED(scr)){
407 break;
411 return scr;
415 /*EXTL_DOC
416 * Returns the first WRootWin
418 EXTL_SAFE
419 EXTL_EXPORT
420 WRootWin *ioncore_rootwin()
422 return ioncore_g.rootwins;
426 /*}}}*/
429 /*{{{ Dynamic function table and class implementation */
432 static DynFunTab rootwin_dynfuntab[]={
433 {region_map, rootwin_map},
434 {region_unmap, rootwin_unmap},
435 {region_do_set_focus, rootwin_do_set_focus},
436 {(DynFun*)region_xwindow, (DynFun*)rootwin_x_window},
437 {(DynFun*)region_fitrep, (DynFun*)rootwin_fitrep},
438 {region_managed_remove, rootwin_managed_remove},
439 {(DynFun*)region_managed_disposeroot,
440 (DynFun*)rootwin_managed_disposeroot},
441 END_DYNFUNTAB
445 EXTL_EXPORT
446 IMPLCLASS(WRootWin, WWindow, rootwin_deinit, rootwin_dynfuntab);
449 /*}}}*/