/* (C)opyright MMIV-MMVI Anselm R. Garbe * (C)opyright MMVI-MMVII Kris Maglione * See LICENSE file for license details. */ #include "wmii.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static Bool check_other_wm; static int (*x_error_handler) (Display *, XErrorEvent *); static char version[] = "wmiiwm - " VERSION ", (C)opyright MMVI-MMVII Kris Maglione\n"; static struct sigaction sa; static void usage() { fatal("usage: wmiiwm -a
[-r ] [-v]\n"); } static void scan_wins() { int i; uint num; Window *wins; XWindowAttributes wa; Window d1, d2; if(XQueryTree(blz.dpy, blz.root, &d1, &d2, &wins, &num)) { for(i = 0; i < num; i++) { if(!XGetWindowAttributes(blz.dpy, wins[i], &wa)) continue; if(wa.override_redirect || XGetTransientForHint(blz.dpy, wins[i], &d1)) continue; if(wa.map_state == IsViewable) manage_client(create_client(wins[i], &wa)); } for(i = 0; i < num; i++) { if(!XGetWindowAttributes(blz.dpy, wins[i], &wa)) continue; if(XGetTransientForHint(blz.dpy, wins[i], &d1) && wa.map_state == IsViewable) manage_client(create_client(wins[i], &wa)); } } if(wins) XFree(wins); } static int win_property(Window w, Atom a, Atom t, long l, uchar **prop) { Atom real; int format; ulong res, extra; int status; status = XGetWindowProperty(blz.dpy, w, a, 0L, l, False, t, &real, &format, &res, &extra, prop); if(status != Success || *prop == 0) { return 0; } if(res == 0) { free((void *) *prop); } return res; } int win_proto(Window w) { Atom *protocols; long res; int protos = 0; int i; res = win_property(w, wm_atom[WMProtocols], XA_ATOM, 20L, ((uchar **) &protocols)); if(res <= 0) { return protos; } for(i = 0; i < res; i++) { if(protocols[i] == wm_atom[WMDelete]) protos |= WM_PROTOCOL_DELWIN; } free((char *) protocols); return protos; } static void init_atoms() { wm_atom[WMState] = XInternAtom(blz.dpy, "WM_STATE", False); wm_atom[WMProtocols] = XInternAtom(blz.dpy, "WM_PROTOCOLS", False); wm_atom[WMDelete] = XInternAtom(blz.dpy, "WM_DELETE_WINDOW", False); net_atom[NetSupported] = XInternAtom(blz.dpy, "_NET_SUPPORTED", False); net_atom[NetWMName] = XInternAtom(blz.dpy, "_NET_WM_NAME", False); tags_atom = XInternAtom(blz.dpy, "_WIN_TAGS", False); XChangeProperty(blz.dpy, blz.root, net_atom[NetSupported], XA_ATOM, 32, PropModeReplace, (uchar *) net_atom, NetLast); } static void init_cursors() { Pixmap pix; XColor black, dummy; XAllocNamedColor(blz.dpy, DefaultColormap(blz.dpy, blz.screen), "black", &black, &dummy); pix = XCreateBitmapFromData(blz.dpy, blz.root, (char[]){0}, 1, 1); cursor[CurNormal] = XCreateFontCursor(blz.dpy, XC_left_ptr); cursor[CurNECorner] = XCreateFontCursor(blz.dpy, XC_top_right_corner); cursor[CurNWCorner] = XCreateFontCursor(blz.dpy, XC_top_left_corner); cursor[CurSECorner] = XCreateFontCursor(blz.dpy, XC_bottom_right_corner); cursor[CurSWCorner] = XCreateFontCursor(blz.dpy, XC_bottom_left_corner); cursor[CurMove] = XCreateFontCursor(blz.dpy, XC_fleur); cursor[CurInput] = XCreateFontCursor(blz.dpy, XC_xterm); cursor[CurInvisible] = XCreatePixmapCursor(blz.dpy, pix, pix, &black, &black, 0, 0); XFreePixmap(blz.dpy, pix); } static void init_screen(WMScreen *screen) { Window w; int ret; unsigned mask; XGCValues gcv; gcv.subwindow_mode = IncludeInferiors; gcv.function = GXxor; gcv.foreground = def.normcolor.bg; gcv.plane_mask = AllPlanes; gcv.graphics_exposures = False; xorgc = XCreateGC(blz.dpy, blz.root, GCForeground | GCGraphicsExposures | GCFunction | GCSubwindowMode | GCPlaneMask, &gcv); screen->rect.x = screen->rect.y = 0; screen->rect.width = DisplayWidth(blz.dpy, blz.screen); screen->rect.height = DisplayHeight(blz.dpy, blz.screen); def.snap = screen->rect.height / 63; sel_screen = XQueryPointer(blz.dpy, blz.root, &w, &w, &ret, &ret, &ret, &ret, &mask); } /* * There's no way to check accesses to destroyed windows, thus * those cases are ignored (especially on UnmapNotify's). * Other types of errors call Xlib's default error handler, which * calls exit(). */ int wmii_error_handler(Display *dpy, XErrorEvent *error) { if(check_other_wm) fatal("wmiiwm: another window manager is already running\n"); if(error->error_code == BadWindow || (error->request_code == X_SetInputFocus && error->error_code == BadMatch) || (error->request_code == X_PolyText8 && error->error_code == BadDrawable) || (error->request_code == X_PolyFillRectangle && error->error_code == BadDrawable) || (error->request_code == X_PolySegment && error->error_code == BadDrawable) || (error->request_code == X_ConfigureWindow && error->error_code == BadMatch) || (error->request_code == X_GrabKey && error->error_code == BadAccess)) return 0; fprintf(stderr, "wmiiwm: fatal error: Xrequest code=%d, Xerror code=%d\n", error->request_code, error->error_code); return x_error_handler(blz.dpy, error); /* calls exit() */ } static void cleanup() { Client *c; for(c=client; c; c=c->next) reparent_client(c, blz.root, c->sel->rect.x, c->sel->rect.y); XSync(blz.dpy, False); } static void cleanup_handler(int signal) { sa.sa_handler = SIG_DFL; sigaction(signal, &sa, nil); switch(signal) { case SIGINT: srv.running = False; break; default: cleanup(); XCloseDisplay(blz.dpy); raise(signal); break; } } static void sigchld_handler(int sig) { int ret; /* We only spawn one child */ wait(&ret); } static void init_traps() { char buf[1]; int fd[2]; if(pipe(fd) != 0) fatal("Can't pipe(): %s\n", strerror(errno)); switch(fork()) { case -1: fatal("Can't fork(): %s\n", strerror(errno)); default: close(fd[0]); sa.sa_flags = 0; sa.sa_handler = cleanup_handler; sigaction(SIGINT, &sa, nil); sigaction(SIGTERM, &sa, nil); sigaction(SIGQUIT, &sa, nil); sigaction(SIGHUP, &sa, nil); break; case 0: close(fd[1]); close(ConnectionNumber(blz.dpy)); setsid(); blz.dpy = XOpenDisplay(0); if(!blz.dpy) fatal("wmiiwm: cannot open display\n"); read(fd[0], buf, 1); XSetInputFocus(blz.dpy, PointerRoot, RevertToPointerRoot, CurrentTime); XCloseDisplay(blz.dpy); exit(0); } } void check_9pcon(IXPConn *c) { serve_9pcon(c); check_x_event(c); } int main(int argc, char *argv[]) { char *wmiirc, *errstr, *namespace, *tmp; char address[1024], ns_path[1024]; WMScreen *s; struct passwd *passwd; int i; XSetWindowAttributes wa; passwd = getpwuid(getuid()); user = estrdup(passwd->pw_name); wmiirc = nil; tmp = getenv("WMII_NS_PATH"); if(tmp) strncpy(ns_path, tmp, sizeof(ns_path)); else snprintf(ns_path, sizeof(ns_path), "/tmp/ns.%s.%s", user, getenv("DISPLAY")); tmp = getenv("WMII_ADDRESS"); if(tmp) strncpy(address, tmp, sizeof(ns_path)); else snprintf(address, sizeof(address), "unix!%s/wmii", ns_path); /* command line args */ for(i = 1; (i < argc) && (argv[i][0] == '-'); i++) { switch (argv[i][1]) { case 'v': fprintf(stdout, "%s", version); exit(0); break; case 'V': verbose = True; break; case 'a': if(i + 1 < argc) strncpy(address, argv[++i], sizeof(address)); else usage(); break; case 'r': if(i + 1 < argc) wmiirc = argv[++i]; else usage(); break; default: usage(); break; } } if(!address) usage(); setenv("WMII_NS_PATH", ns_path, True); setenv("WMII_ADDRESS", address, True); setlocale(LC_CTYPE, ""); starting = True; blz.dpy = XOpenDisplay(0); if(!blz.dpy) fatal("wmiiwm: cannot open dpy\n"); blz.screen = DefaultScreen(blz.dpy); blz.root = RootWindow(blz.dpy, blz.screen); check_other_wm = True; x_error_handler = XSetErrorHandler(wmii_error_handler); XSelectInput(blz.dpy, blz.root, SubstructureRedirectMask | EnterWindowMask); XSync(blz.dpy, False); check_other_wm = False; init_traps(); /* Check namespace permissions */ if(!strncmp(address, "unix!", 5)) { struct stat st; namespace = estrdup(&address[5]); for(i = strlen(namespace) - 1; i >= 0; i--) if(namespace[i] == '/') break; namespace[i+1] = '\0'; if(stat(namespace, &st)) fatal("wmiiwm: can't stat namespace directory \"%s\": %s\n", namespace, strerror(errno)); if(getuid() != st.st_uid) fatal("wmiiwm: namespace directory \"%s\" exists, " "but is not owned by you", namespace); if(st.st_mode & 077) fatal("wmiiwm: namespace directory \"%s\" exists, " "but has group or world permissions", namespace); free(namespace); } errstr = nil; i = ixp_create_sock(address, &errstr); if(i < 0) fatal("wmiiwm: fatal: %s\n", errstr); /* start wmiirc */ if(wmiirc) { int name_len = strlen(wmiirc) + 6; char execstr[name_len]; signal(SIGCHLD, sigchld_handler); switch(fork()) { case 0: if(setsid() == -1) fatal("wmiiwm: can't setsid: %s\n", strerror(errno)); close(i); close(ConnectionNumber(blz.dpy)); snprintf(execstr, name_len, "exec %s", wmiirc); execl("/bin/sh", "sh", "-c", execstr, nil); fatal("wmiiwm: can't exec \"%s\": %s\n", wmiirc, strerror(errno)); case -1: perror("wmiiwm: cannot fork wmiirc"); default: break; } } ixp_server_open_conn(&srv, i, &p9srv, check_9pcon, nil); ixp_server_open_conn(&srv, ConnectionNumber(blz.dpy), nil, check_x_event, nil); view = nil; client = nil; key = nil; memset(&def, 0, sizeof(def)); def.font.fontstr = estrdup(BLITZ_FONT); def.border = 1; def.colmode = Coldefault; def.mod = Mod1Mask; strncpy(def.grabmod, "Mod1", sizeof(def.grabmod)); strncpy(def.focuscolor.colstr, BLITZ_FOCUSCOLORS, sizeof(def.focuscolor.colstr)); strncpy(def.normcolor.colstr, BLITZ_NORMCOLORS, sizeof(def.normcolor.colstr)); loadcolor(&blz, &def.focuscolor); loadcolor(&blz, &def.normcolor); init_atoms(); init_cursors(); loadfont(&blz, &def.font); init_lock_keys(); num_screens = 1; screens = emallocz(num_screens * sizeof(*screens)); for(i = 0; i < num_screens; i++) { s = &screens[i]; s->lbar = nil; s->rbar = nil; s->sel = nil; init_screen(s); pmap = XCreatePixmap( /* display */ blz.dpy, /* drawable */ blz.root, /* width */ s->rect.width, /* height */ s->rect.height, /* depth */ DefaultDepth(blz.dpy, blz.screen) ); wa.event_mask = SubstructureRedirectMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask; wa.cursor = cursor[CurNormal]; XChangeWindowAttributes(blz.dpy, blz.root, CWEventMask | CWCursor, &wa); wa.override_redirect = 1; wa.background_pixmap = ParentRelative; wa.event_mask = ExposureMask | ButtonReleaseMask | FocusChangeMask | SubstructureRedirectMask | SubstructureNotifyMask; s->brect = s->rect; s->brect.height = labelh(&def.font); s->brect.y = s->rect.height - s->brect.height; s->barwin = XCreateWindow( /* display */ blz.dpy, /* parent */ RootWindow(blz.dpy, blz.screen), /* x */ s->brect.x, /* y */ s->brect.y, /* width */ s->brect.width, /* height */ s->brect.height, /*border_width*/0, /* depth */ DefaultDepth(blz.dpy, blz.screen), /* class */ CopyFromParent, /* visual */ DefaultVisual(blz.dpy, blz.screen), /* valuemask */ CWOverrideRedirect | CWBackPixmap | CWEventMask, /* attrubutes */&wa ); XSync(blz.dpy, False); s->bbrush.blitz = &blz; s->bbrush.gc = XCreateGC(blz.dpy, s->barwin, 0, 0); s->bbrush.drawable = pmap; s->bbrush.rect = s->brect; s->bbrush.rect.x = 0; s->bbrush.rect.y = 0; s->bbrush.color = def.normcolor; s->bbrush.font = &def.font; s->bbrush.border = 1; draw_bar(s); XMapRaised(blz.dpy, s->barwin); } screen = &screens[0]; screen->focus = nil; XSetInputFocus(blz.dpy, screen->barwin, RevertToParent, CurrentTime); scan_wins(); starting = False; update_views(); if(view) write_event("FocusTag %s\n", screen->sel->name); check_x_event(nil); errstr = ixp_server_loop(&srv); if(errstr) fprintf(stderr, "wmiiwm: fatal: %s\n", errstr); cleanup(); XCloseDisplay(blz.dpy); ixp_server_close(&srv); if(errstr) return 1; return 0; }