2 * ion/ioncore/rootwin.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
10 #include <sys/types.h>
19 #include <X11/Xproto.h>
21 #include <libtu/objp.h>
28 #include "clientwin.h"
34 #include <libextl/readconfig.h>
41 /*{{{ Error handling */
44 static bool redirect_error
=FALSE
;
45 static bool ignore_badwindow
=TRUE
;
48 static int my_redirect_error_handler(Display
*UNUSED(dpy
), XErrorEvent
*UNUSED(ev
))
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
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
)) &&
73 XmuPrintDefaultErrorMessage(dpy
, ev
, stderr
);
75 XGetErrorText(dpy
, ev
->error_code
, msg
, 128);
76 snprintf(num
, 32, "%d", ev
->request_code
);
77 XGetErrorDatabaseText(dpy
, "XRequest", num
, "", request
, 64);
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
);
86 warn("[%d] %s (%d) %#lx: %s", ev
->serial
, request
,
87 ev
->request_code
, ev
->resourceid
,msg
);
91 kill(getpid(), SIGTRAP
);
103 static void scan_initial_windows(WRootWin
*rootwin
)
105 Window dummy_root
, dummy_parent
, *wins
=NULL
;
109 XQueryTree(ioncore_g
.dpy
, WROOTWIN_ROOT(rootwin
), &dummy_root
, &dummy_parent
,
112 for(i
=0; i
<nwins
; i
++){
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
){
128 rootwin
->tmpwins
=wins
;
129 rootwin
->tmpnwins
=nwins
;
133 void rootwin_manage_initial_windows(WRootWin
*rootwin
)
135 Window
*wins
=rootwin
->tmpwins
;
137 int i
, nwins
=rootwin
->tmpnwins
;
139 rootwin
->tmpwins
=NULL
;
142 for(i
=0; i
<nwins
; i
++){
143 if(XWINDOW_REGION_OF(wins
[i
])!=NULL
)
147 if(XGetTransientForHint(ioncore_g
.dpy
, wins
[i
], &tfor
))
149 ioncore_manage_clientwin(wins
[i
], FALSE
);
153 for(i
=0; i
<nwins
; i
++){
156 ioncore_manage_clientwin(wins
[i
], FALSE
);
163 static void create_wm_windows(WRootWin
*rootwin
)
167 rootwin
->dummy_win
=XCreateWindow(ioncore_g
.dpy
, WROOTWIN_ROOT(rootwin
),
169 CopyFromParent
, InputOnly
,
170 CopyFromParent
, 0, NULL
);
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
)
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
;
190 gcv
.subwindow_mode
=IncludeInferiors
;
194 gcvmask
=(GCLineStyle
|GCLineWidth
|GCFillStyle
|
195 GCJoinStyle
|GCCapStyle
|GCFunction
|
196 GCSubwindowMode
|GCForeground
);
198 rootwin
->xor_gc
=XCreateGC(ioncore_g
.dpy
, WROOTWIN_ROOT(rootwin
),
203 static bool rootwin_init(WRootWin
*rootwin
, int xscr
)
205 Display
*dpy
=ioncore_g
.dpy
;
210 /* Try to select input on the root window */
211 root
=RootWindow(dpy
, xscr
);
213 redirect_error
=FALSE
;
215 XSetErrorHandler(my_redirect_error_handler
);
216 XSelectInput(dpy
, root
, IONCORE_EVENTMASK_ROOT
);
218 XSetErrorHandler(my_error_handler
);
221 warn(TR("Unable to redirect root window events for screen %d."
222 "Maybe another window manager is running?"),
228 rootwin
->default_cmap
=DefaultColormap(dpy
, xscr
);
229 rootwin
->tmpwins
=NULL
;
231 rootwin
->dummy_win
=None
;
232 rootwin
->xor_gc
=None
;
234 fp
.mode
=REGION_FIT_EXACT
;
236 fp
.g
.w
=DisplayWidth(dpy
, xscr
);
237 fp
.g
.h
=DisplayHeight(dpy
, xscr
);
239 if(!window_do_init((WWindow
*)rootwin
, NULL
, &fp
, root
, "WRootWin")){
243 ((WWindow
*)rootwin
)->event_mask
=IONCORE_EVENTMASK_ROOT
;
244 ((WRegion
*)rootwin
)->flags
|=REGION_BINDINGS_ARE_GRABBED
|REGION_PLEASE_WARP
;
245 ((WRegion
*)rootwin
)->rootwin
=rootwin
;
247 REGION_MARK_MAPPED(rootwin
);
249 scan_initial_windows(rootwin
);
251 create_wm_windows(rootwin
);
253 netwm_init_rootwin(rootwin
);
255 region_add_bindmap((WRegion
*)rootwin
, ioncore_screen_bindmap
);
257 scr
=create_screen(rootwin
, &fp
, xscr
);
261 region_set_manager((WRegion
*)scr
, (WRegion
*)rootwin
);
262 region_map((WRegion
*)scr
);
264 LINK_ITEM(*(WRegion
**)&ioncore_g
.rootwins
, (WRegion
*)rootwin
, p_next
, p_prev
);
266 ioncore_screens_updated(rootwin
);
268 xwindow_set_cursor(root
, IONCORE_CURSOR_DEFAULT
);
274 WRootWin
*create_rootwin(int xscr
)
276 CREATEOBJ_IMPL(WRootWin
, rootwin
, (p
, xscr
));
280 void rootwin_deinit(WRootWin
*rw
)
284 FOR_ALL_SCREENS_W_NEXT(scr
, next
){
285 if(REGION_MANAGER(scr
)==(WRegion
*)rw
)
286 destroy_obj((Obj
*)scr
);
289 UNLINK_ITEM(*(WRegion
**)&ioncore_g
.rootwins
, (WRegion
*)rw
, p_next
, p_prev
);
291 XSelectInput(ioncore_g
.dpy
, WROOTWIN_ROOT(rw
), 0);
293 XFreeGC(ioncore_g
.dpy
, rw
->xor_gc
);
295 window_deinit((WWindow
*)rw
);
302 /*{{{ region dynfun implementations */
305 static void rootwin_do_set_focus(WRootWin
*rootwin
, bool warp
)
309 sub
=REGION_ACTIVE_SUB(rootwin
);
311 if(sub
==NULL
|| !REGION_IS_MAPPED(sub
)){
313 FOR_ALL_SCREENS(scr
){
314 if(REGION_IS_MAPPED(scr
)){
322 region_do_set_focus(sub
, warp
);
324 window_do_set_focus((WWindow
*)rootwin
, warp
);
328 static bool rootwin_fitrep(WRootWin
*UNUSED(rootwin
), WWindow
*UNUSED(par
),
329 const WFitParams
*UNUSED(fp
))
331 D(warn("Don't know how to reparent or fit root windows."));
336 static void rootwin_map(WRootWin
*UNUSED(rootwin
))
338 D(warn("Attempt to map a root window."));
342 static void rootwin_unmap(WRootWin
*UNUSED(rootwin
))
344 D(warn("Attempt to unmap a root window -- impossible."));
348 static void rootwin_managed_remove(WRootWin
*rootwin
, WRegion
*reg
)
350 region_unset_manager(reg
, (WRegion
*)rootwin
);
353 static WRegion
*rootwin_managed_disposeroot(WRootWin
*UNUSED(rootwin
), WRegion
*reg
)
355 WScreen
*scr
=OBJ_CAST(reg
, WScreen
);
356 if(scr
!=NULL
&& scr
==scr
->prev_scr
){
357 warn(TR("Only screen may not be destroyed/detached."));
366 static Window
rootwin_x_window(WRootWin
*rootwin
)
368 return WROOTWIN_ROOT(rootwin
);
378 static bool scr_ok(WRegion
*r
)
380 return (OBJ_IS(r
, WScreen
) && REGION_IS_MAPPED(r
));
385 * Returns previously active screen on root window \var{rootwin}.
389 WScreen
*rootwin_current_scr(WRootWin
*rootwin
)
391 WRegion
*r
=REGION_ACTIVE_SUB(rootwin
);
394 /* There should be no non-WScreen as children or managed by us, but... */
396 if(r
!=NULL
&& scr_ok(r
))
399 FOR_ALL_SCREENS(scr
){
400 if(REGION_MANAGER(scr
)==(WRegion
*)rootwin
401 && REGION_IS_MAPPED(scr
)){
410 * Warp the cursor pointer to this location
412 * I'm not *entirely* sure what 'safe' means, but this doesn't change internal
413 * notion state, so I guess it's 'safe'...
417 void rootwin_warp_pointer(WRootWin
*root
, int x
, int y
)
419 XWarpPointer(ioncore_g
.dpy
, None
, WROOTWIN_ROOT(root
), 0, 0, 0, 0, x
, y
);
424 * Returns the first WRootWin
428 WRootWin
*ioncore_rootwin()
430 return ioncore_g
.rootwins
;
437 /*{{{ Dynamic function table and class implementation */
440 static DynFunTab rootwin_dynfuntab
[]={
441 {region_map
, rootwin_map
},
442 {region_unmap
, rootwin_unmap
},
443 {region_do_set_focus
, rootwin_do_set_focus
},
444 {(DynFun
*)region_xwindow
, (DynFun
*)rootwin_x_window
},
445 {(DynFun
*)region_fitrep
, (DynFun
*)rootwin_fitrep
},
446 {region_managed_remove
, rootwin_managed_remove
},
447 {(DynFun
*)region_managed_disposeroot
,
448 (DynFun
*)rootwin_managed_disposeroot
},
454 IMPLCLASS(WRootWin
, WWindow
, rootwin_deinit
, rootwin_dynfuntab
);