/*- * Copyright (c) 2001 Jordan DeLong * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "wm.h" /* list of screen structures, and count of screens */ int screen_count = 0; screenlist_t screen_list = TAILQ_HEAD_INITIALIZER(screen_list); /* * error handler to use while we're trying to init; if errors * happen here it's probably because someone else (another window * manager, presumably) selected for SubstructureRedirectMask * on the root window. */ static int init_handler(Display *d, XErrorEvent *e) { errx(1, "another window manager may be " "running; exiting now."); } /* * error handler used during normal window manager * operation. it is expected that we get some BadWindows, * BadDrawables and such when windows exit; nonexpected * stuff is logged to stderr. */ static int error_handler(Display *d, XErrorEvent *event) { if (event->error_code != BadWindow && event->error_code != BadMatch && event->request_code != X_GetGeometry && event->error_code != BadDrawable) trace("unexpected Xlib error; error code %d," "request code %d, minor %d", event->error_code, event->request_code, event->minor_code); return 0; } /* this starts us up managing windows that already exist when we start */ static void manage_existing_windows(screen_t *screen) { XWindowAttributes attr; Window wroot, wparent, *children; int nchilds, i; client_t *client; XGrabServer(display); XQueryTree(display, screen->root, &wroot, &wparent, &children, &nchilds); for (i = 0; i < nchilds; i++) { XGetWindowAttributes(display, children[i], &attr); if (attr.override_redirect || attr.map_state != IsViewable) continue; client = client_add(screen, children[i], NULL, NULL); if (!client) continue; client->flags.unmap_from_reparent = 1; } XUngrabServer(display); } /* manage a screen */ static screen_t *screen_manage(int num) { XSetWindowAttributes wattr; XGCValues gcvalues; XColor clr; screen_t *screen; Window dumroot; int dumx, dumy, dumbord, dumdepth; int events; screen = calloc(1, sizeof(screen_t)); screen->num = num; screen->root = RootWindow(display, screen->num); /* get root window dimensions */ XGetGeometry(display, screen->root, &dumroot, &dumx, &dumy, &screen->width, &screen->height, &dumbord, &dumdepth); /* we sync after this so we don't accidently change the root window cursor if a manager is running */ events = SubstructureNotifyMask | SubstructureRedirectMask | ButtonPressMask | ButtonReleaseMask; XSelectInput(display, screen->root, events); XSync(display, 0); XDefineCursor(display, screen->root, cursor_default); XSaveContext(display, screen->root, root_context, (XPointer) screen); /* get a window to focus if none of the clients can get it */ wattr.override_redirect = 1; screen->nofocus = XCreateWindow(display, screen->root, -10, -10, 4, 4, 0, 0, InputOnly, CopyFromParent, CWOverrideRedirect, &wattr); XMapWindow(display, screen->nofocus); /* get colors for the screen */ if (!options.linefgclr || !XParseColor(display, DefaultColormap(display, screen->num), options.linefgclr, &clr)) { screen->linefg = (1 << DefaultDepth(display, screen->num)) - 1; } else { XAllocColor(display, DefaultColormap(display, screen->num), &clr); screen->linefg = clr.pixel; } if (!options.titleclr || !XParseColor(display, DefaultColormap(display, screen->num), options.titleclr, &clr)) clr.pixel = BlackPixel(display, screen->num); else XAllocColor(display, DefaultColormap(display, screen->num), &clr); /* get graphics contexts */ gcvalues.function = GXxor; gcvalues.foreground = screen->linefg; gcvalues.background = BlackPixel(display, screen->num); gcvalues.subwindow_mode = IncludeInferiors; gcvalues.line_width = options.linewidth; screen->xorgc = XCreateGC(display, screen->root, GCForeground | GCBackground | GCSubwindowMode | GCFunction | GCLineWidth, &gcvalues); gcvalues.function = GXcopy; gcvalues.foreground = clr.pixel; gcvalues.background = WhitePixel(display, screen->num); #ifdef I18N screen->titlegc = XCreateGC(display, screen->root, GCForeground | GCBackground | GCFunction, &gcvalues); #else gcvalues.font = titlefont->fid; screen->titlegc = XCreateGC(display, screen->root, GCForeground | GCBackground | GCFont | GCFunction, &gcvalues); #endif return screen; } /* the main initialization routine */ void screen_init() { screen_t *screen; int dum, i; screen_count = ScreenCount(display); XSetErrorHandler(init_handler); /* * get cursors, unique contexts and intern all * the global atoms. */ cursor_default = XCreateFontCursor(display, XC_left_ptr); cursor_move = XCreateFontCursor(display, XC_fleur); cursor_place = XCreateFontCursor(display, XC_bottom_right_corner); client_context = XUniqueContext(); root_context = XUniqueContext(); decor_context = XUniqueContext(); plugin_context = XUniqueContext(); XInternAtoms(display, atom_names, NUM_ATOMS, 0, atoms); /* we need this before screen_manage calls */ decor_init(); /* manage all screens */ for (i = 0; i < screen_count; i++) { screen = screen_manage(i); TAILQ_INSERT_HEAD(&screen_list, screen, s_list); } /* * make sure no errors have happened yet, and * switch to the real error handler used during * normal program operation. */ XSync(display, 0); XSetErrorHandler(error_handler); XSync(display, 0); if (pixmap_getpixmaps() != 0) errx(1, "unable to get pixmaps"); dgroup_learn_space(); /* * create desktops and grab keys for all * the managed screens */ TAILQ_FOREACH_REVERSE(screen, &screen_list, screenlist, s_list) { TAILQ_INIT(&screen->s_desklist); if (desktop_create(screen, options.desktop_width, options.desktop_height, options.desktop_count) == -1) errx(1, "unable to add requested number of " "desktops to screen %d", screen->num); screen->desktop = TAILQ_FIRST(&screen->s_desklist); keys_grab(screen); } /* see if we have the shape extension */ if (XShapeQueryExtension(display, &shape_base, &dum) == 0) errx(1, "shape extension not found; shape is " "required to run this windowmanager"); } /* manage clients that already exist */ void screen_manage_existing() { screen_t *screen; TAILQ_FOREACH(screen, &screen_list, s_list) manage_existing_windows(screen); } /* shutdown routine */ void screen_shutdown() { screen_t *screen, *next; /* kill all screens */ screen = TAILQ_FIRST(&screen_list); while (screen) { /* remove screen data */ XDestroyWindow(display, screen->nofocus); XFreeGC(display, screen->xorgc); XFreeGC(display, screen->titlegc); /* get rid of all desktops */ desktop_remove(screen); /* free the screen_t */ next = TAILQ_NEXT(screen, s_list); free(screen); screen = next; } TAILQ_INIT(&screen_list); /* * make sure input focus is on the root * window before we leave. */ XSetInputFocus(display, PointerRoot, RevertToPointerRoot, CurrentTime); XSync(display, 0); }