+unsigned long
+name_to_color(char *colorname)
+{
+ Colormap cmap;
+ Status status;
+ XColor screen_def, exact_def;
+ unsigned long result = 0;
+ char cname[32] = "#";
+
+ cmap = DefaultColormap(display, screens[0].idx);
+ status = XAllocNamedColor(display, cmap, colorname,
+ &screen_def, &exact_def);
+ if (!status) {
+ strlcat(cname, colorname + 2, sizeof cname - 1);
+ status = XAllocNamedColor(display, cmap, cname, &screen_def,
+ &exact_def);
+ }
+ if (status)
+ result = screen_def.pixel;
+ else
+ fprintf(stderr, "color '%s' not found.\n", colorname);
+
+ return (result);
+}
+
+void
+setscreencolor(char *val, int i, int c)
+{
+ if (i > 0 && i <= ScreenCount(display)) {
+ screens[i - 1].c[c].color = name_to_color(val);
+ if ((screens[i - 1].c[c].name = strdup(val)) == NULL)
+ errx(1, "strdup");
+ } else if (i == -1) {
+ for (i = 0; i < ScreenCount(display); i++)
+ screens[i].c[c].color = name_to_color(val);
+ if ((screens[i - 1].c[c].name = strdup(val)) == NULL)
+ errx(1, "strdup");
+ } else
+ errx(1, "invalid screen index: %d out of bounds (maximum %d)\n",
+ i, ScreenCount(display));
+}
+
+void
+custom_region(char *val)
+{
+ unsigned int sidx, x, y, w, h;
+
+ if (sscanf(val, "screen[%u]:%ux%u+%u+%u", &sidx, &w, &h, &x, &y) != 5)
+ errx(1, "invalid custom region, "
+ "should be 'screen[<n>]:<n>x<n>+<n>+<n>\n");
+ if (sidx < 1 || sidx > ScreenCount(display))
+ errx(1, "invalid screen index: %d out of bounds (maximum %d)\n",
+ sidx, ScreenCount(display));
+ sidx--;
+
+ if (w < 1 || h < 1)
+ errx(1, "region %ux%u+%u+%u too small\n", w, h, x, y);
+
+ if (x < 0 || x > DisplayWidth(display, sidx) ||
+ y < 0 || y > DisplayHeight(display, sidx) ||
+ w + x > DisplayWidth(display, sidx) ||
+ h + y > DisplayHeight(display, sidx))
+ errx(1, "region %ux%u+%u+%u not within screen boundaries "
+ "(%ux%u)\n", w, h, x, y,
+ DisplayWidth(display, sidx), DisplayHeight(display, sidx));
+
+ new_region(&screens[sidx], x, y, w, h);
+}
+
+int
+varmatch(char *var, char *name, int *index)
+{
+ char *p, buf[5];
+ int i;
+
+ i = strncmp(var, name, 255);
+ if (index == NULL)
+ return (i);
+
+ *index = -1;
+ if (i <= 0)
+ return (i);
+ p = var + strlen(name);
+ if (*p++ != '[')
+ return (i);
+ bzero(buf, sizeof buf);
+ i = 0;
+ while (isdigit(*p) && i < sizeof buf)
+ buf[i++] = *p++;
+ if (i == 0 || i >= sizeof buf || *p != ']')
+ return (1);
+ *index = strtonum(buf, 0, 99, NULL);
+ return (0);
+}
+
+/* conf file stuff */
+#define SWM_CONF_WS "\n= \t"
+#define SWM_CONF_FILE "scrotwm.conf"
+int
+conf_load(char *filename)
+{
+ FILE *config;
+ char *line, *cp, *var, *val;
+ size_t len, lineno = 0;
+ int i, sc;
+ unsigned int modkey = MODKEY, modmask;
+ KeySym ks;
+ enum keyfuncid kfid;
+ char class[SWM_QUIRK_LEN];
+ char name[SWM_QUIRK_LEN];
+ unsigned long quirk;
+
+ DNPRINTF(SWM_D_MISC, "conf_load: filename %s\n", filename);
+
+ if (filename == NULL)
+ return (1);
+
+ if ((config = fopen(filename, "r")) == NULL)
+ return (1);
+
+ for (sc = ScreenCount(display);;) {
+ if ((line = fparseln(config, &len, &lineno, NULL, 0)) == NULL)
+ if (feof(config))
+ break;
+ cp = line;
+ cp += (long)strspn(cp, SWM_CONF_WS);
+ if (cp[0] == '\0') {
+ /* empty line */
+ free(line);
+ continue;
+ }
+ if ((var = strsep(&cp, SWM_CONF_WS)) == NULL || cp == NULL)
+ break;
+ cp += (long)strspn(cp, SWM_CONF_WS);
+ if ((val = strsep(&cp, SWM_CONF_WS)) == NULL)
+ break;
+
+ DNPRINTF(SWM_D_MISC, "conf_load: %s=%s\n",var ,val);
+ switch (var[0]) {
+ case 'b':
+ if (!strncmp(var, "bar_enabled", strlen("bar_enabled")))
+ bar_enabled = atoi(val);
+ else if (!varmatch(var, "bar_border", &i))
+ setscreencolor(val, i, SWM_S_COLOR_BAR_BORDER);
+ else if (!varmatch(var, "bar_color", &i))
+ setscreencolor(val, i, SWM_S_COLOR_BAR);
+ else if (!varmatch(var, "bar_font_color", &i))
+ setscreencolor(val, i, SWM_S_COLOR_BAR_FONT);
+ else if (!strncmp(var, "bar_font", strlen("bar_font")))
+ asprintf(&bar_fonts[0], "%s", val);
+ else if (!strncmp(var, "bar_action", strlen("bar_action")))
+ asprintf(&bar_argv[0], "%s", val);
+ else if (!strncmp(var, "bar_delay", strlen("bar_delay")))
+ bar_delay = atoi(val);
+ else if (!bindmatch(var, "bind", modkey, val,
+ &kfid, &modmask, &ks))
+ setkeybinding(modmask, ks, kfid);
+ else
+ goto bad;
+ break;
+
+ case 'c':
+ if (!strncmp(var, "clock_enabled", strlen("clock_enabled")))
+ clock_enabled = atoi(val);
+ else if (!varmatch(var, "color_focus", &i))
+ setscreencolor(val, i, SWM_S_COLOR_FOCUS);
+ else if (!varmatch(var, "color_unfocus", &i))
+ setscreencolor(val, i, SWM_S_COLOR_UNFOCUS);
+ else if (!strncmp(var, "cycle_empty", strlen("cycle_empty")))
+ cycle_visible = atoi(val);
+ else if (!strncmp(var, "cycle_visible", strlen("cycle_visible")))
+ cycle_visible = atoi(val);
+ else
+ goto bad;
+ break;
+
+ case 'd':
+ if (!strncmp(var, "dialog_ratio",
+ strlen("dialog_ratio"))) {
+ dialog_ratio = atof(val);
+ if (dialog_ratio > 1.0 || dialog_ratio <= .3)
+ dialog_ratio = .6;
+ } else
+ goto bad;
+ break;
+
+ case 'm':
+ if (!strncmp(var, "modkey", strlen("modkey"))) {
+ modkey = MODKEY;
+ if (!strncmp(val, "Mod2", strlen("Mod2")))
+ modkey = Mod2Mask;
+ else if (!strncmp(val, "Mod3", strlen("Mod3")))
+ modkey = Mod3Mask;
+ else if (!strncmp(val, "Mod4", strlen("Mod4")))
+ modkey = Mod4Mask;
+ else
+ modkey = Mod1Mask;
+ update_modkey(modkey);
+ } else
+ goto bad;
+ break;
+
+ case 'q':
+ if (!quirkmatch(var, "quirk", val, class, name, &quirk))
+ setquirk(class, name, quirk);
+ else
+ goto bad;
+ break;
+
+ case 'r':
+ if (!strncmp(var, "region", strlen("region")))
+ custom_region(val);
+ else
+ goto bad;
+ break;
+
+ case 's':
+ if (!strncmp(var, "spawn_term", strlen("spawn_term")))
+ asprintf(&spawn_term[0], "%s", val);
+ else if (!strncmp(var, "screenshot_enabled",
+ strlen("screenshot_enabled")))
+ ss_enabled = atoi(val);
+ else if (!strncmp(var, "screenshot_app",
+ strlen("screenshot_app")))
+ asprintf(&spawn_screenshot[0], "%s", val);
+ else
+ goto bad;
+ break;
+
+ case 't':
+ if (!strncmp(var, "term_width", strlen("term_width")))
+ term_width = atoi(val);
+ else if (!strncmp(var, "title_class_enabled",
+ strlen("title_class_enabled")))
+ title_class_enabled = atoi(val);
+ else if (!strncmp(var, "title_name_enabled",
+ strlen("title_name_enabled")))
+ title_name_enabled = atoi(val);
+ else
+ goto bad;
+ break;
+
+ default:
+ goto bad;
+ }
+ free(line);
+ }
+
+ fclose(config);
+ return (0);
+
+bad:
+ errx(1, "invalid conf file entry: %s=%s", var, val);
+}
+
+void
+socket_setnonblock(int fd)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
+ err(1, "fcntl F_GETFL");
+ flags |= O_NONBLOCK;
+ if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
+ err(1, "fcntl F_SETFL");
+}
+
+void
+bar_print(struct swm_region *r, char *s)
+{
+ XClearWindow(display, r->bar_window);
+ XSetForeground(display, bar_gc, r->s->c[SWM_S_COLOR_BAR_FONT].color);
+ XDrawString(display, r->bar_window, bar_gc, 4, bar_fs->ascent, s,
+ strlen(s));
+}
+
+void
+bar_extra_stop(void)
+{
+ if (bar_pipe[0]) {
+ close(bar_pipe[0]);
+ bzero(bar_pipe, sizeof bar_pipe);
+ }
+ if (bar_pid) {
+ kill(bar_pid, SIGTERM);
+ bar_pid = 0;
+ }
+ strlcpy(bar_ext, "", sizeof bar_ext);
+ bar_extra = 0;
+}
+
+void
+bar_update(void)
+{
+ time_t tmt;
+ struct tm tm;
+ struct swm_region *r;
+ int i, x;
+ size_t len;
+ char s[SWM_BAR_MAX];
+ char loc[SWM_BAR_MAX];
+ char *b;
+ XClassHint *xch;
+ Status status;
+
+ if (bar_enabled == 0)
+ return;
+ if (bar_extra && bar_extra_running) {
+ /* ignore short reads; it'll correct itself */
+ while ((b = fgetln(stdin, &len)) != NULL)
+ if (b && b[len - 1] == '\n') {
+ b[len - 1] = '\0';
+ strlcpy(bar_ext, b, sizeof bar_ext);
+ }
+ if (b == NULL && errno != EAGAIN) {
+ fprintf(stderr, "bar_extra failed: errno: %d %s\n",
+ errno, strerror(errno));
+ bar_extra_stop();
+ }
+ } else
+ strlcpy(bar_ext, "", sizeof bar_ext);
+
+ if (clock_enabled == 0)
+ strlcpy(s, "", sizeof s);
+ else {
+ time(&tmt);
+ localtime_r(&tmt, &tm);
+ strftime(s, sizeof s, "%a %b %d %R %Z %Y ", &tm);
+ }
+ xch = NULL;
+ if ((title_name_enabled == 1 || title_class_enabled == 1) &&
+ cur_focus != NULL) {
+ if ((xch = XAllocClassHint()) == NULL)
+ goto out;
+ status = XGetClassHint(display, cur_focus->id, xch);
+ if (status == BadWindow || status == BadAlloc)
+ goto out;
+ if (title_name_enabled)
+ strlcat(s, xch->res_name, sizeof s);
+ if (title_name_enabled && title_class_enabled)
+ strlcat(s, " ", sizeof s);
+ if (title_class_enabled)
+ strlcat(s, xch->res_class, sizeof s);
+ }
+out:
+ if (xch)
+ XFree(xch);
+ for (i = 0; i < ScreenCount(display); i++) {
+ x = 1;
+ TAILQ_FOREACH(r, &screens[i].rl, entry) {
+ snprintf(loc, sizeof loc, "%d:%d %s%s %s",
+ x++, r->ws->idx + 1, s, bar_ext, bar_vertext);
+ bar_print(r, loc);
+ }
+ }
+ XSync(display, False);
+ alarm(bar_delay);
+}
+
+void
+bar_signal(int sig)
+{
+ bar_alarm = 1;
+}
+
+void
+bar_toggle(struct swm_region *r, union arg *args)
+{
+ struct swm_region *tmpr;
+ int i, j, sc = ScreenCount(display);
+
+ DNPRINTF(SWM_D_MISC, "bar_toggle\n");
+
+ if (bar_enabled)
+ for (i = 0; i < sc; i++)
+ TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
+ XUnmapWindow(display, tmpr->bar_window);
+ else
+ for (i = 0; i < sc; i++)
+ TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
+ XMapRaised(display, tmpr->bar_window);
+
+ bar_enabled = !bar_enabled;
+ for (i = 0; i < sc; i++)
+ for (j = 0; j < SWM_WS_MAX; j++)
+ screens[i].ws[j].restack = 1;
+
+ stack();
+ /* must be after stack */
+ bar_update();
+}
+
+void
+bar_refresh(void)
+{
+ XSetWindowAttributes wa;
+ struct swm_region *r;
+ int i;
+
+ /* do this here because the conf file is in memory */
+ if (bar_extra && bar_extra_running == 0 && bar_argv[0]) {
+ /* launch external status app */
+ bar_extra_running = 1;
+ if (pipe(bar_pipe) == -1)
+ err(1, "pipe error");
+ socket_setnonblock(bar_pipe[0]);
+ socket_setnonblock(bar_pipe[1]); /* XXX hmmm, really? */
+ if (dup2(bar_pipe[0], 0) == -1)
+ errx(1, "dup2");
+ if (dup2(bar_pipe[1], 1) == -1)
+ errx(1, "dup2");
+ if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
+ err(1, "could not disable SIGPIPE");
+ switch (bar_pid = fork()) {
+ case -1:
+ err(1, "cannot fork");
+ break;
+ case 0: /* child */
+ close(bar_pipe[0]);
+ execvp(bar_argv[0], bar_argv);
+ err(1, "%s external app failed", bar_argv[0]);
+ break;
+ default: /* parent */
+ close(bar_pipe[1]);
+ break;
+ }
+ }
+
+ bzero(&wa, sizeof wa);
+ for (i = 0; i < ScreenCount(display); i++)
+ TAILQ_FOREACH(r, &screens[i].rl, entry) {
+ wa.border_pixel =
+ screens[i].c[SWM_S_COLOR_BAR_BORDER].color;
+ wa.background_pixel =
+ screens[i].c[SWM_S_COLOR_BAR].color;
+ XChangeWindowAttributes(display, r->bar_window,
+ CWBackPixel | CWBorderPixel, &wa);
+ }
+ bar_update();
+}
+
+void
+bar_setup(struct swm_region *r)
+{
+ int i;
+
+ for (i = 0; bar_fonts[i] != NULL; i++) {
+ bar_fs = XLoadQueryFont(display, bar_fonts[i]);
+ if (bar_fs) {
+ bar_fidx = i;
+ break;
+ }
+ }
+ if (bar_fonts[i] == NULL)
+ errx(1, "couldn't load font");
+ bar_height = bar_fs->ascent + bar_fs->descent + 3;
+
+ r->bar_window = XCreateSimpleWindow(display,
+ r->s->root, X(r), Y(r), WIDTH(r) - 2, bar_height - 2,
+ 1, r->s->c[SWM_S_COLOR_BAR_BORDER].color,
+ r->s->c[SWM_S_COLOR_BAR].color);
+ bar_gc = XCreateGC(display, r->bar_window, 0, &bar_gcv);
+ XSetFont(display, bar_gc, bar_fs->fid);
+ XSelectInput(display, r->bar_window, VisibilityChangeMask);
+ if (bar_enabled)
+ XMapRaised(display, r->bar_window);
+ DNPRINTF(SWM_D_MISC, "bar_setup: bar_window %lu\n", r->bar_window);
+
+ if (signal(SIGALRM, bar_signal) == SIG_ERR)
+ err(1, "could not install bar_signal");
+ bar_refresh();
+}
+
+void
+version(struct swm_region *r, union arg *args)
+{
+ bar_version = !bar_version;
+ if (bar_version)
+ snprintf(bar_vertext, sizeof bar_vertext, "Version: %s CVS: %s",
+ SWM_VERSION, cvstag);
+ else
+ strlcpy(bar_vertext, "", sizeof bar_vertext);
+ bar_update();
+}
+
+void
+client_msg(struct ws_win *win, Atom a)
+{
+ XClientMessageEvent cm;
+
+ bzero(&cm, sizeof cm);
+ cm.type = ClientMessage;
+ cm.window = win->id;
+ cm.message_type = aprot;
+ cm.format = 32;
+ cm.data.l[0] = a;
+ cm.data.l[1] = CurrentTime;
+ XSendEvent(display, win->id, False, 0L, (XEvent *)&cm);
+}
+
+void
+config_win(struct ws_win *win)
+{
+ XConfigureEvent ce;
+
+ DNPRINTF(SWM_D_MISC, "config_win: win %lu x %d y %d w %d h %d\n",
+ win->id, win->g.x, win->g.y, win->g.w, win->g.h);
+ ce.type = ConfigureNotify;
+ ce.display = display;
+ ce.event = win->id;
+ ce.window = win->id;
+ ce.x = win->g.x;
+ ce.y = win->g.y;
+ ce.width = win->g.w;
+ ce.height = win->g.h;
+ ce.border_width = 1; /* XXX store this! */
+ ce.above = None;
+ ce.override_redirect = False;
+ XSendEvent(display, win->id, False, StructureNotifyMask, (XEvent *)&ce);
+}
+
+int
+count_win(struct workspace *ws, int count_transient)
+{
+ struct ws_win *win;
+ int count = 0;
+
+ TAILQ_FOREACH(win, &ws->winlist, entry) {
+ if (count_transient == 0 && win->floating)
+ continue;
+ if (count_transient == 0 && win->transient)
+ continue;
+ count++;
+ }
+ DNPRINTF(SWM_D_MISC, "count_win: %d\n", count);
+
+ return (count);
+}
+