2 * ion/ioncore/rootwin.c
4 * Copyright (c) Tuomo Valkonen 1999-2006.
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.
13 #include <sys/types.h>
22 #include <X11/Xproto.h>
23 /*#include <X11/Xmu/Error.h>*/
25 #include <X11/extensions/Xinerama.h>
26 #elif defined(CF_SUN_XINERAMA)
27 #include <X11/extensions/xinerama.h>
30 #include <libtu/objp.h>
37 #include "clientwin.h"
44 #include <libextl/readconfig.h>
51 /*{{{ Error handling */
54 static bool redirect_error=FALSE;
55 static bool ignore_badwindow=TRUE;
58 static int my_redirect_error_handler(Display *dpy, XErrorEvent *ev)
65 static int my_error_handler(Display *dpy, XErrorEvent *ev)
67 static char msg[128], request[64], num[32];
69 /* Just ignore bad window and similar errors; makes the rest of
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)) &&
79 XmuPrintDefaultErrorMessage(dpy, ev, stderr);
81 XGetErrorText(dpy, ev->error_code, msg, 128);
82 snprintf(num, 32, "%d", ev->request_code);
83 XGetErrorDatabaseText(dpy, "XRequest", num, "", request, 64);
86 snprintf(request, 64, "<unknown request>");
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);
92 warn("[%d] %s (%d) %#lx: %s", ev->serial, request,
93 ev->request_code, ev->resourceid,msg);
97 kill(getpid(), SIGTRAP);
109 static void scan_initial_windows(WRootWin *rootwin)
111 Window dummy_root, dummy_parent, *wins=NULL;
115 XQueryTree(ioncore_g.dpy, WROOTWIN_ROOT(rootwin), &dummy_root, &dummy_parent,
118 for(i=0; i<nwins; i++){
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){
134 rootwin->tmpwins=wins;
135 rootwin->tmpnwins=nwins;
139 void rootwin_manage_initial_windows(WRootWin *rootwin)
141 Window *wins=rootwin->tmpwins;
143 int i, nwins=rootwin->tmpnwins;
145 rootwin->tmpwins=NULL;
148 for(i=0; i<nwins; i++){
149 if(XWINDOW_REGION_OF(wins[i])!=NULL)
153 if(XGetTransientForHint(ioncore_g.dpy, wins[i], &tfor))
155 ioncore_manage_clientwin(wins[i], FALSE);
159 for(i=0; i<nwins; i++){
162 ioncore_manage_clientwin(wins[i], FALSE);
169 static void create_wm_windows(WRootWin *rootwin)
171 rootwin->dummy_win=XCreateWindow(ioncore_g.dpy, WROOTWIN_ROOT(rootwin),
173 CopyFromParent, InputOnly,
174 CopyFromParent, 0, NULL);
176 XSelectInput(ioncore_g.dpy, rootwin->dummy_win, PropertyChangeMask);
180 static void preinit_gr(WRootWin *rootwin)
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;
191 gcv.subwindow_mode=IncludeInferiors;
195 gcvmask=(GCLineStyle|GCLineWidth|GCFillStyle|
196 GCJoinStyle|GCCapStyle|GCFunction|
197 GCSubwindowMode|GCForeground);
199 rootwin->xor_gc=XCreateGC(ioncore_g.dpy, WROOTWIN_ROOT(rootwin),
204 static WRootWin *preinit_rootwin(int xscr)
206 Display *dpy=ioncore_g.dpy;
212 /* Try to select input on the root window */
213 root=RootWindow(dpy, xscr);
215 redirect_error=FALSE;
217 XSetErrorHandler(my_redirect_error_handler);
218 XSelectInput(dpy, root, IONCORE_EVENTMASK_ROOT);
220 XSetErrorHandler(my_error_handler);
223 warn(TR("Unable to redirect root window events for screen %d."),
228 rootwin=ALLOC(WRootWin);
233 /* Init the struct */
234 OBJ_INIT(rootwin, WRootWin);
237 rootwin->default_cmap=DefaultColormap(dpy, xscr);
238 rootwin->tmpwins=NULL;
240 rootwin->dummy_win=None;
241 rootwin->xor_gc=None;
243 fp.mode=REGION_FIT_EXACT;
245 fp.g.w=DisplayWidth(dpy, xscr);
246 fp.g.h=DisplayHeight(dpy, xscr);
248 if(!window_do_init((WWindow*)rootwin, NULL, root, &fp)){
253 /* Note: this mask isn't right if some WScreen auses the same window. */
254 ((WWindow*)rootwin)->event_mask=IONCORE_EVENTMASK_ROOT;
256 ((WRegion*)rootwin)->flags|=REGION_BINDINGS_ARE_GRABBED|REGION_PLEASE_WARP;
257 ((WRegion*)rootwin)->rootwin=rootwin;
259 REGION_MARK_MAPPED(rootwin);
261 scan_initial_windows(rootwin);
263 create_wm_windows(rootwin);
265 netwm_init_rootwin(rootwin);
267 region_add_bindmap((WRegion*)rootwin, ioncore_rootwin_bindmap);
273 static Atom net_virtual_roots=None;
276 static WScreen *add_screen(WRootWin *rw, int id, const WRectangle *geom,
284 fp.mode=REGION_FIT_EXACT;
286 #ifdef CF_ALWAYS_VIRTUAL_ROOT
290 scr=create_screen(rw, id, &fp, useroot);
295 region_set_manager((WRegion*)scr, (WRegion*)rw);
297 region_map((WRegion*)scr);
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);
310 static bool xinerama_sanity_check(XineramaScreenInfo *xi, int nxi)
314 for(i=0; i<nxi; i++){
315 for(j=0; j<nxi; 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."));
325 if(xi[i].width<=0 || xi[i].height<=0){
326 warn(TR("Xinerama sanity check failed; zero size detected."));
332 #elif defined(CF_SUN_XINERAMA)
333 static bool xinerama_sanity_check(XRectangle *monitors, int nxi)
337 for(i=0; i<nxi; i++){
338 for(j=0; j<nxi; 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."));
350 if(monitors[i].width<=0 || monitors[i].height<=0){
351 warn(TR("Xinerama sanity check failed; zero size detected."));
360 WRootWin *ioncore_manage_rootwin(int xscr, bool noxinerama)
365 XineramaScreenInfo *xi=NULL;
367 int event_base, error_base;
368 #elif defined(CF_SUN_XINERAMA)
369 XRectangle monitors[MAXFRAMEBUFFERS];
375 if(XineramaQueryExtension(ioncore_g.dpy, &event_base, &error_base)){
376 xi=XineramaQueryScreens(ioncore_g.dpy, &nxi);
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."));
386 #elif defined(CF_SUN_XINERAMA)
387 if(XineramaGetState(ioncore_g.dpy, xscr)){
388 unsigned char hints[16];
391 if(XineramaGetInfo(ioncore_g.dpy, xscr, monitors, hints,
393 warn(TR("Error retrieving Xinerama information."));
396 if(ioncore_g.rootwins!=NULL){
397 warn(TR("Don't know how to get Xinerama information for "
398 "multiple X root windows."));
406 rootwin=preinit_rootwin(xscr);
416 net_virtual_roots=XInternAtom(ioncore_g.dpy, "_NET_VIRTUAL_ROOTS", False);
417 XDeleteProperty(ioncore_g.dpy, WROOTWIN_ROOT(rootwin), net_virtual_roots);
420 if(xi!=NULL && nxi!=0 && xinerama_sanity_check(xi, nxi)){
424 for(i=0; i<nxi; i++){
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);
438 #elif defined(CF_SUN_XINERAMA)
439 if(nxi!=0 && xinerama_sanity_check(monitors, nxi)){
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;
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);
459 if(!add_screen(rootwin, xscr, ®ION_GEOM(rootwin), TRUE))
464 warn(TR("Unable to setup X screen %d."), xscr);
465 destroy_obj((Obj*)rootwin);
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;
476 xwindow_set_cursor(WROOTWIN_ROOT(rootwin), IONCORE_CURSOR_DEFAULT);
482 void rootwin_deinit(WRootWin *rw)
486 FOR_ALL_SCREENS_W_NEXT(scr, next){
487 if(REGION_MANAGER(scr)==(WRegion*)rw)
488 destroy_obj((Obj*)scr);
492 WRegion *tmp=(WRegion*)ioncore_g.rootwins;
493 UNLINK_ITEM(tmp, (WRegion*)rw, p_next, p_prev);
494 ioncore_g.rootwins=(WRootWin*)tmp;
497 XSelectInput(ioncore_g.dpy, WROOTWIN_ROOT(rw), 0);
499 XFreeGC(ioncore_g.dpy, rw->xor_gc);
501 window_deinit((WWindow*)rw);
508 /*{{{ region dynfun implementations */
511 static void rootwin_do_set_focus(WRootWin *rootwin, bool warp)
515 sub=REGION_ACTIVE_SUB(rootwin);
517 if(sub==NULL || !REGION_IS_MAPPED(sub)){
519 FOR_ALL_SCREENS(scr){
520 if(REGION_IS_MAPPED(scr)){
528 region_do_set_focus(sub, warp);
530 window_do_set_focus((WWindow*)rootwin, warp);
534 static bool rootwin_fitrep(WRootWin *rootwin, WWindow *par,
535 const WFitParams *fp)
537 D(warn("Don't know how to reparent or fit root windows."));
542 static void rootwin_map(WRootWin *rootwin)
544 D(warn("Attempt to map a root window."));
548 static void rootwin_unmap(WRootWin *rootwin)
550 D(warn("Attempt to unmap a root window -- impossible."));
554 static void rootwin_managed_remove(WRootWin *rootwin, WRegion *reg)
556 region_unset_manager(reg, (WRegion*)rootwin);
560 static Window rootwin_x_window(WRootWin *rootwin)
562 return WROOTWIN_ROOT(rootwin);
572 static bool scr_ok(WRegion *r)
574 return (OBJ_IS(r, WScreen) && REGION_IS_MAPPED(r));
579 * Returns previously active screen on root window \var{rootwin}.
583 WScreen *rootwin_current_scr(WRootWin *rootwin)
585 WRegion *r=REGION_ACTIVE_SUB(rootwin);
588 /* There should be no non-WScreen as children or managed by us, but... */
590 if(r!=NULL && scr_ok(r))
593 FOR_ALL_SCREENS(scr){
594 if(REGION_MANAGER(scr)==(WRegion*)rootwin
595 && REGION_IS_MAPPED(scr)){
607 /*{{{ Dynamic function table and class implementation */
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},
622 IMPLCLASS(WRootWin, WWindow, rootwin_deinit, rootwin_dynfuntab);