/* * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } * For license, see COPYING. */ #include #include #include #ifdef HAVE_IMLIB2 #include #endif /* HAVE_IMLIB2 */ #include "wmfs.h" #include "event.h" #include "ewmh.h" #include "screen.h" #include "infobar.h" #include "util.h" #include "config.h" #include "client.h" #include "fifo.h" int wmfs_error_handler(Display *d, XErrorEvent *event) { char mess[256]; /* Check if there is another WM running */ if(event->error_code == BadAccess && W->root == event->resourceid) errl(EXIT_FAILURE, "Another Window Manager is already running."); /* Ignore focus change error for unmapped client * 42 = X_SetInputFocus * 28 = X_GrabButton */ if(client_gb_win(event->resourceid)) if(event->error_code == BadWindow || event->request_code == 42 || event->request_code == 28) return 0; if(XGetErrorText(d, event->error_code, mess, 128)) warnxl("%s(%d) opcodes %d/%d\n resource #%lx\n", mess, event->error_code, event->request_code, event->minor_code, event->resourceid); return 1; } int wmfs_error_handler_dummy(Display *d, XErrorEvent *event) { (void)d; (void)event; return 0; } void wmfs_numlockmask(void) { int i, j; XModifierKeymap *mm = XGetModifierMapping(W->dpy); for(i = 0; i < 8; i++) for(j = 0; j < mm->max_keypermod; ++j) if(mm->modifiermap[i * mm->max_keypermod + j] == XKeysymToKeycode(W->dpy, XK_Num_Lock)) W->numlockmask = (1 << i); XFreeModifiermap(mm); } void wmfs_init_font(char *font, struct theme *t) { XFontStruct **xfs = NULL; char **misschar, **names, *defstring; int d; if(!(t->font.fontset = XCreateFontSet(W->dpy, font, &misschar, &d, &defstring))) { warnxl("Can't load font '%s'", font); t->font.fontset = XCreateFontSet(W->dpy, "fixed", &misschar, &d, &defstring); } XExtentsOfFontSet(t->font.fontset); XFontsOfFontSet(t->font.fontset, &xfs, &names); t->font.as = xfs[0]->max_bounds.ascent; t->font.de = xfs[0]->max_bounds.descent; t->font.width = xfs[0]->max_bounds.width; t->font.height = t->font.as + t->font.de; if(misschar) XFreeStringList(misschar); } static void wmfs_xinit(void) { XGCValues xgc = { .function = GXinvert, .subwindow_mode = IncludeInferiors, .line_width = 1 }; XSetWindowAttributes at = { .event_mask = (KeyMask | ButtonMask | MouseMask | PropertyChangeMask | SubstructureRedirectMask | SubstructureNotifyMask | StructureNotifyMask), .cursor = XCreateFontCursor(W->dpy, XC_left_ptr) }; /* * X Error handler */ XSetErrorHandler(wmfs_error_handler); /* * X var */ W->xscreen = DefaultScreen(W->dpy); W->xdepth = DefaultDepth(W->dpy, W->xscreen); W->gc = DefaultGC(W->dpy, W->xscreen); W->xmaxw = DisplayWidth(W->dpy, W->xscreen); W->xmaxh = DisplayHeight(W->dpy, W->xscreen); /* * Keys */ wmfs_numlockmask(); /* * Root window/cursor */ W->root = RootWindow(W->dpy, W->xscreen); XChangeWindowAttributes(W->dpy, W->root, CWEventMask | CWCursor, &at); W->rgc = XCreateGC(W->dpy, W->root, GCFunction | GCSubwindowMode | GCLineWidth, &xgc); /* * Locale (font encode) */ setlocale(LC_CTYPE, ""); /* * Barwin linked list */ SLIST_INIT(&W->h.barwin); /* * Optional dep init */ #ifdef HAVE_IMLIB2 imlib_context_set_display(W->dpy); imlib_context_set_visual(DefaultVisual(W->dpy, W->xscreen)); imlib_context_set_colormap(DefaultColormap(W->dpy, W->xscreen)); #endif /* HAVE_IMLIB2 */ W->flags |= WMFS_RUNNING; } void wmfs_grab_keys(void) { KeyCode c; struct keybind *k; wmfs_numlockmask(); XUngrabKey(W->dpy, AnyKey, AnyModifier, W->root); SLIST_FOREACH(k, &W->h.keybind, next) if((c = XKeysymToKeycode(W->dpy, k->keysym))) { XGrabKey(W->dpy, c, k->mod, W->root, True, GrabModeAsync, GrabModeAsync); XGrabKey(W->dpy, c, k->mod | LockMask, W->root, True, GrabModeAsync, GrabModeAsync); XGrabKey(W->dpy, c, k->mod | W->numlockmask, W->root, True, GrabModeAsync, GrabModeAsync); XGrabKey(W->dpy, c, k->mod | LockMask | W->numlockmask, W->root, True, GrabModeAsync, GrabModeAsync); } } /** Scan xprops of previous session to set it back * Check if there are windows on X (previous sessions windows) */ static void wmfs_scan(void) { struct geo g; struct client *c, *cc, *fc; struct screen *s; int i, n, rf, nscreen = 0; int tag = -1, screen = -1, flags = -1; unsigned long ir, il; long *ret = NULL, *tret = NULL; bool getg = false; XWindowAttributes wa; Window usl, usl2, *w = NULL, tm, focus; Atom rt; SLIST_INIT(&W->h.client); W->flags |= WMFS_SCAN; /* Get previous selected tag to apply it at the end */ if(XGetWindowProperty(W->dpy, W->root, W->net_atom[wmfs_current_tag], 0, 32, False, XA_CARDINAL, &rt, &rf, &ir, &il, (unsigned char**)&tret) == Success && tret) { nscreen = (int)ir; } /* Previous focused client before reload */ if(XGetWindowProperty(W->dpy, W->root, W->net_atom[wmfs_focus], 0, 32, False, XA_WINDOW, &rt, &rf, &ir, &il, (unsigned char**)&ret) == Success && ret) { focus = *ret; XFree(ret); } if(XQueryTree(W->dpy, W->root, &usl, &usl2, &w, (unsigned int*)&n)) for(i = n - 1; i != -1; --i) { XGetWindowAttributes(W->dpy, w[i], &wa); if(!wa.override_redirect && wa.map_state == IsViewable) { if(XGetWindowProperty(W->dpy, w[i], ATOM("_WMFS_TAG"), 0, 32, False, XA_CARDINAL, &rt, &rf, &ir, &il, (unsigned char**)&ret) == Success && ret) { tag = *ret; XFree(ret); } if(XGetWindowProperty(W->dpy, w[i], ATOM("_WMFS_SCREEN"), 0, 32, False, XA_CARDINAL, &rt, &rf, &ir, &il, (unsigned char**)&ret) == Success && ret) { screen = *ret; XFree(ret); } if(XGetWindowProperty(W->dpy, w[i], ATOM("_WMFS_FLAGS"), 0, 32, False, XA_CARDINAL, &rt, &rf, &ir, &il, (unsigned char**)&ret) == Success && ret) { flags = *ret; flags &= ~(CLIENT_TABBED | CLIENT_REMOVEALL); XFree(ret); } if(XGetWindowProperty(W->dpy, w[i], ATOM("_WMFS_GEO"), 0, 32, False, XA_CARDINAL, &rt, &rf, &ir, &il, (unsigned char**)&ret) == Success && ret) { g.x = ret[0]; g.y = ret[1]; g.w = ret[2]; g.h = ret[3]; getg = true; XFree(ret); } if(XGetWindowProperty(W->dpy, w[i], ATOM("_WMFS_TABMASTER"), 0, 32, False, XA_WINDOW, &rt, &rf, &ir, &il, (unsigned char**)&ret) == Success && ret) { tm = *ret; XFree(ret); } c = client_new(w[i], &wa, true); if(tm != c->win) c->tmp = tm; tm = 0; if(flags != -1) c->flags |= flags; if(tag != -1 && screen != -1) { c->screen = screen_gb_id(screen); if(getg) c->flags |= CLIENT_IGNORE_LAYOUT; client_map(c); tag_client(tag_gb_id(c->screen, tag), c); if(getg) client_moveresize(c, &g); client_get_name(c); } } } if(!nscreen) { SLIST_FOREACH(s, &W->h.screen, next) if(!TAILQ_EMPTY(&s->tags)) tag_screen(s, TAILQ_FIRST(&s->tags)); } else { /* Set back selected tag */ for(i = 0; i < nscreen; ++i) { s = screen_gb_id(i); tag_screen(s, tag_gb_id(s, tret[i])); } } /* Re-adjust tabbed clients */ SLIST_FOREACH(c, &W->h.client, next) if((cc = client_gb_win(c->tmp)) && cc != c) _client_tab(c, cc); if((fc = client_gb_win(focus)) && fc != W->client) client_focus(fc); W->flags &= ~WMFS_SCAN; if(tret) XFree(tret); XFree(w); XSync(W->dpy, false); } static void wmfs_loop(void) { XEvent ev; int maxfd, fd = ConnectionNumber(W->dpy); fd_set iset; while(W->flags & WMFS_RUNNING) { maxfd = fd + 1; FD_ZERO(&iset); FD_SET(fd, &iset); if(W->fifo.fd > 0) { maxfd += W->fifo.fd; FD_SET(W->fifo.fd, &iset); } if(select(maxfd, &iset, NULL, NULL, NULL) > 0) { if(FD_ISSET(fd, &iset)) { while((W->flags & WMFS_RUNNING) && XPending(W->dpy)) { XNextEvent(W->dpy, &ev); EVENT_HANDLE(&ev); } } else if(W->fifo.fd > 0 && FD_ISSET(W->fifo.fd, &iset)) fifo_read(); } } } static inline void wmfs_init(void) { log_init(); wmfs_xinit(); ewmh_init(); screen_init(); event_init(); config_init(); fifo_init(); } void wmfs_quit(void) { struct keybind *k; struct rule *r; struct theme *t; struct client *c; struct mousebind *m; struct launcher *l; ewmh_update_wmfs_props(); XFreeGC(W->dpy, W->rgc); while(!SLIST_EMPTY(&W->h.client)) { c = SLIST_FIRST(&W->h.client); client_update_props(c, CPROP_LOC | CPROP_FLAG | CPROP_GEO); c->flags |= (CLIENT_IGNORE_LAYOUT | CLIENT_REMOVEALL); client_remove(c); } /* Will free: * * Screens -> tags * -> Infobars -> Elements */ screen_free(); /* Conf stuffs */ while(!SLIST_EMPTY(&W->h.theme)) { t = SLIST_FIRST(&W->h.theme); SLIST_REMOVE_HEAD(&W->h.theme, next); XFreeFontSet(W->dpy, t->font.fontset); free(t); } while(!SLIST_EMPTY(&W->h.rule)) { r = SLIST_FIRST(&W->h.rule); SLIST_REMOVE_HEAD(&W->h.rule, next); status_free_ctx(&t->tags_n_sl); status_free_ctx(&t->tags_s_sl); status_free_ctx(&t->tags_o_sl); status_free_ctx(&t->client_n_sl); status_free_ctx(&t->client_s_sl); free(r->class); free(r->instance); free(r->role); free(r->name); free(r); } while(!SLIST_EMPTY(&W->h.keybind)) { k = SLIST_FIRST(&W->h.keybind); SLIST_REMOVE_HEAD(&W->h.keybind, next); free((void*)k->cmd); free(k); } while(!SLIST_EMPTY(&W->h.launcher)) { l = SLIST_FIRST(&W->h.launcher); SLIST_REMOVE_HEAD(&W->h.launcher, next); free((void*)l->name); free((void*)l->prompt); free((void*)l->command); free(l); } while(!SLIST_EMPTY(&W->h.mousebind)) { m = SLIST_FIRST(&W->h.mousebind); SLIST_REMOVE_HEAD(&W->h.mousebind, globnext); free((void*)m->cmd); free(m); } /* FIFO stuffs */ if(W->fifo.fd > 0) { close(W->fifo.fd); unlink(W->fifo.path); } /* close log */ if(W->log) fclose(W->log), W->log = NULL; W->flags &= ~WMFS_RUNNING; XCloseDisplay(W->dpy); } /** Reload WMFS binary */ void uicb_reload(Uicb cmd) { (void)cmd; W->flags &= ~WMFS_RUNNING; W->flags |= WMFS_RELOAD; } void uicb_quit(Uicb cmd) { (void)cmd; W->flags &= ~WMFS_RUNNING; } int main(int argc, char **argv) { int i; bool r; (void)argc; W = (struct wmfs*)xcalloc(1, sizeof(struct wmfs)); /* Default path ~/.config/wmfs/wmfsrc */ sprintf(W->confpath, "%s/"CONFIG_DEFAULT_PATH, getenv("HOME")); /* Opt */ while((i = getopt(argc, argv, "hvC:")) != -1) { switch(i) { case 'h': printf("usage: %s [-hv] [-C ]\n" " -h Show this page\n" " -v Show WMFS version\n" " -C Launch WMFS with a specified configuration file\n", argv[0]); free(W); exit(EXIT_SUCCESS); break; case 'v': printf("wmfs("WMFS_VERSION") 2 beta\n"); free(W); exit(EXIT_SUCCESS); break; case 'C': strncpy(W->confpath, optarg, sizeof(W->confpath)); break; } } /* Get X display */ if(!(W->dpy = XOpenDisplay(NULL))) { fprintf(stderr, "%s: Can't open X server\n", argv[0]); exit(EXIT_FAILURE); } /* Core */ wmfs_init(); wmfs_scan(); wmfs_loop(); wmfs_quit(); r = (W->flags & WMFS_RELOAD); free(W); if(r) execvp(argv[0], argv); return 1; }