+ switch (e->type) {
+ case KeyPress:
+ name = "KeyPress";
+ break;
+ case KeyRelease:
+ name = "KeyRelease";
+ break;
+ case ButtonPress:
+ name = "ButtonPress";
+ break;
+ case ButtonRelease:
+ name = "ButtonRelease";
+ break;
+ case MotionNotify:
+ name = "MotionNotify";
+ break;
+ case EnterNotify:
+ name = "EnterNotify";
+ break;
+ case LeaveNotify:
+ name = "LeaveNotify";
+ break;
+ case FocusIn:
+ name = "FocusIn";
+ break;
+ case FocusOut:
+ name = "FocusOut";
+ break;
+ case KeymapNotify:
+ name = "KeymapNotify";
+ break;
+ case Expose:
+ name = "Expose";
+ break;
+ case GraphicsExpose:
+ name = "GraphicsExpose";
+ break;
+ case NoExpose:
+ name = "NoExpose";
+ break;
+ case VisibilityNotify:
+ name = "VisibilityNotify";
+ break;
+ case CreateNotify:
+ name = "CreateNotify";
+ break;
+ case DestroyNotify:
+ name = "DestroyNotify";
+ break;
+ case UnmapNotify:
+ name = "UnmapNotify";
+ break;
+ case MapNotify:
+ name = "MapNotify";
+ break;
+ case MapRequest:
+ name = "MapRequest";
+ break;
+ case ReparentNotify:
+ name = "ReparentNotify";
+ break;
+ case ConfigureNotify:
+ name = "ConfigureNotify";
+ break;
+ case ConfigureRequest:
+ name = "ConfigureRequest";
+ break;
+ case GravityNotify:
+ name = "GravityNotify";
+ break;
+ case ResizeRequest:
+ name = "ResizeRequest";
+ break;
+ case CirculateNotify:
+ name = "CirculateNotify";
+ break;
+ case CirculateRequest:
+ name = "CirculateRequest";
+ break;
+ case PropertyNotify:
+ name = "PropertyNotify";
+ break;
+ case SelectionClear:
+ name = "SelectionClear";
+ break;
+ case SelectionRequest:
+ name = "SelectionRequest";
+ break;
+ case SelectionNotify:
+ name = "SelectionNotify";
+ break;
+ case ColormapNotify:
+ name = "ColormapNotify";
+ break;
+ case ClientMessage:
+ name = "ClientMessage";
+ break;
+ case MappingNotify:
+ name = "MappingNotify";
+ break;
+ }
+
+ if (name)
+ DNPRINTF(SWM_D_EVENTQ ,"window: %lu event: %s (%d), %d "
+ "remaining\n",
+ e->xany.window, name, e->type, QLength(display));
+ else
+ DNPRINTF(SWM_D_EVENTQ, "window: %lu unknown event %d, %d "
+ "remaining\n",
+ e->xany.window, e->type, QLength(display));
+}
+
+void
+dumpwins(struct swm_region *r, union arg *args)
+{
+ struct ws_win *win;
+ unsigned int state;
+ XWindowAttributes wa;
+
+ if (r->ws == NULL) {
+ fprintf(stderr, "invalid workspace\n");
+ return;
+ }
+
+ fprintf(stderr, "=== managed window list ws %02d ===\n", r->ws->idx);
+
+ TAILQ_FOREACH(win, &r->ws->winlist, entry) {
+ state = getstate(win->id);
+ if (!XGetWindowAttributes(display, win->id, &wa))
+ fprintf(stderr, "window: %lu failed "
+ "XGetWindowAttributes\n", win->id);
+ fprintf(stderr, "window: %lu map_state: %d state: %d\n",
+ win->id, wa.map_state, state);
+ }
+
+ fprintf(stderr, "===== unmanaged window list =====\n");
+ TAILQ_FOREACH(win, &r->ws->unmanagedlist, entry) {
+ state = getstate(win->id);
+ if (!XGetWindowAttributes(display, win->id, &wa))
+ fprintf(stderr, "window: %lu failed "
+ "XGetWindowAttributes\n", win->id);
+ fprintf(stderr, "window: %lu map_state: %d state: %d\n",
+ win->id, wa.map_state, state);
+ }
+
+ fprintf(stderr, "=================================\n");
+}
+#else
+#define dumpevent(e)
+void
+dumpwins(struct swm_region *r, union arg *args)
+{
+}
+#endif /* SWM_DEBUG */
+
+void expose(XEvent *);
+void keypress(XEvent *);
+void buttonpress(XEvent *);
+void configurerequest(XEvent *);
+void configurenotify(XEvent *);
+void destroynotify(XEvent *);
+void enternotify(XEvent *);
+void focusevent(XEvent *);
+void mapnotify(XEvent *);
+void mappingnotify(XEvent *);
+void maprequest(XEvent *);
+void propertynotify(XEvent *);
+void unmapnotify(XEvent *);
+void visibilitynotify(XEvent *);
+void clientmessage(XEvent *);
+
+void (*handler[LASTEvent])(XEvent *) = {
+ [Expose] = expose,
+ [KeyPress] = keypress,
+ [ButtonPress] = buttonpress,
+ [ConfigureRequest] = configurerequest,
+ [ConfigureNotify] = configurenotify,
+ [DestroyNotify] = destroynotify,
+ [EnterNotify] = enternotify,
+ [FocusIn] = focusevent,
+ [FocusOut] = focusevent,
+ [MapNotify] = mapnotify,
+ [MappingNotify] = mappingnotify,
+ [MapRequest] = maprequest,
+ [PropertyNotify] = propertynotify,
+ [UnmapNotify] = unmapnotify,
+ [VisibilityNotify] = visibilitynotify,
+ [ClientMessage] = clientmessage,
+};
+
+void
+sighdlr(int sig)
+{
+ int saved_errno, status;
+ pid_t pid;
+
+ saved_errno = errno;
+
+ switch (sig) {
+ case SIGCHLD:
+ while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) != 0) {
+ if (pid == -1) {
+ if (errno == EINTR)
+ continue;
+#ifdef SWM_DEBUG
+ if (errno != ECHILD)
+ warn("sighdlr: waitpid");
+#endif /* SWM_DEBUG */
+ break;
+ }
+ if (pid == searchpid)
+ search_resp = 1;
+
+#ifdef SWM_DEBUG
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0)
+ warnx("sighdlr: child exit status: %d",
+ WEXITSTATUS(status));
+ } else
+ warnx("sighdlr: child is terminated "
+ "abnormally");
+#endif /* SWM_DEBUG */
+ }
+ break;
+
+ case SIGHUP:
+ restart_wm = 1;
+ break;
+ case SIGINT:
+ case SIGTERM:
+ case SIGQUIT:
+ running = 0;
+ break;
+ }
+
+ errno = saved_errno;
+}
+
+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);
+ free(screens[i - 1].c[c].name);
+ 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);
+ free(screens[i].c[c].name);
+ if ((screens[i].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)) {
+ fprintf(stderr, "ignoring region %ux%u+%u+%u - not within screen boundaries "
+ "(%ux%u)\n", w, h, x, y,
+ DisplayWidth(display, sidx), DisplayHeight(display, sidx));
+ return;
+ }
+
+ new_region(&screens[sidx], x, y, w, h);
+}
+
+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_class_name(char *s, ssize_t sz, struct ws_win *cur_focus)
+{
+ int do_class, do_name;
+ Status status;
+ XClassHint *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;
+ do_class = (title_class_enabled && xch->res_class != NULL);
+ do_name = (title_name_enabled && xch->res_name != NULL);
+ if (do_class)
+ strlcat(s, xch->res_class, sz);
+ if (do_class && do_name)
+ strlcat(s, ":", sz);
+ if (do_name)
+ strlcat(s, xch->res_name, sz);
+ strlcat(s, " ", sz);
+ }
+out:
+ if (xch)
+ XFree(xch);
+}
+
+void
+bar_window_name(char *s, ssize_t sz, struct ws_win *cur_focus)
+{
+ char *title;
+
+ if (window_name_enabled && cur_focus != NULL) {
+ XFetchName(display, cur_focus->id, &title);
+ if (title) {
+ if (cur_focus->floating)
+ strlcat(s, "(f) ", sz);
+ strlcat(s, title, sz);
+ strlcat(s, " ", sz);
+ XFree(title);
+ }
+ }
+}
+
+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 cn[SWM_BAR_MAX];
+ char loc[SWM_BAR_MAX];
+ char *b;
+ char *stack = "";
+
+ 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, clock_format, &tm);
+ strlcat(s, " ", sizeof s);
+ }
+
+ for (i = 0; i < ScreenCount(display); i++) {
+ x = 1;
+ TAILQ_FOREACH(r, &screens[i].rl, entry) {
+ strlcpy(cn, "", sizeof cn);
+ if (r && r->ws) {
+ bar_class_name(cn, sizeof cn, r->ws->focus);
+ bar_window_name(cn, sizeof cn, r->ws->focus);
+ }
+
+ if (stack_enabled)
+ stack = r->ws->cur_layout->name;
+
+ snprintf(loc, sizeof loc, "%d:%d %s %s%s %s %s",
+ x++, r->ws->idx + 1, stack, s, cn, bar_ext,
+ bar_vertext);
+ bar_print(r, loc);
+ }
+ }
+ 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, 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;
+
+ 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, x, y;
+
+ if (bar_fs) {
+ XFreeFont(display, bar_fs);
+ bar_fs = NULL;
+ }
+
+ 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");
+ if (bar_fs == NULL)
+ errx(1, "couldn't create font structure");
+
+ bar_height = bar_fs->ascent + bar_fs->descent + 1 + 2 * bar_border_width;
+ x = X(r);
+ y = bar_at_bottom ? (Y(r) + HEIGHT(r) - bar_height) : Y(r);
+
+ r->bar_window = XCreateSimpleWindow(display,
+ r->s->root, x, y, WIDTH(r) - 2 * bar_border_width, bar_height - 2 * bar_border_width,
+ bar_border_width, 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
+set_win_state(struct ws_win *win, long state)
+{
+ long data[] = {state, None};
+
+ DNPRINTF(SWM_D_EVENT, "set_win_state: window: %lu\n", win->id);
+
+ if (win == NULL)
+ return;
+
+ XChangeProperty(display, win->id, astate, astate, 32, PropModeReplace,
+ (unsigned char *)data, 2);
+}
+
+long
+getstate(Window w)
+{
+ long result = -1;
+ unsigned char *p = NULL;
+ unsigned long n;
+
+ if (!get_property(w, astate, 2L, astate, &n, &p))
+ return (-1);
+ if (n != 0)
+ result = *((long *)p);
+ XFree(p);
+ return (result);
+}
+
+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;
+
+ if (win == NULL)
+ return;
+
+ 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, XConfigureRequestEvent *ev)
+{
+ XConfigureEvent ce;
+
+ if (win == NULL)
+ return;
+
+ if (ev == NULL) {
+ 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 = border_width;
+ ce.above = None;
+ ce.override_redirect = False;
+ } else {
+ DNPRINTF(SWM_D_MISC, "config_win: ev win %lu x %d y %d w %d h %d\n",
+ ev->window, ev->x, ev->y, ev->width, ev->height);
+ ce.type = ConfigureNotify;
+ ce.display = ev->display;
+ ce.event = ev->window;
+ ce.window = ev->window;
+ ce.x = ev->x;
+ ce.y = ev->y;
+ ce.width = ev->width;
+ ce.height = ev->height;
+ ce.border_width = ev->border_width;
+ ce.above = ev->above;
+ 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;
+ if (win->iconic)
+ continue;
+ count++;
+ }
+ DNPRINTF(SWM_D_MISC, "count_win: %d\n", count);
+
+ return (count);
+}
+
+void
+quit(struct swm_region *r, union arg *args)
+{
+ DNPRINTF(SWM_D_MISC, "quit\n");
+ running = 0;
+}
+
+void
+unmap_window(struct ws_win *win)
+{
+ if (win == NULL)
+ return;
+
+ /* don't unmap again */
+ if (getstate(win->id) == IconicState)
+ return;
+
+ set_win_state(win, IconicState);
+
+ XUnmapWindow(display, win->id);
+ XSetWindowBorder(display, win->id,
+ win->s->c[SWM_S_COLOR_UNFOCUS].color);
+}
+
+void
+unmap_all(void)
+{
+ struct ws_win *win;
+ int i, j;
+
+ for (i = 0; i < ScreenCount(display); i++)
+ for (j = 0; j < SWM_WS_MAX; j++)
+ TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
+ unmap_window(win);
+}
+
+void
+fake_keypress(struct ws_win *win, int keysym, int modifiers)
+{
+ XKeyEvent event;
+
+ if (win == NULL)
+ return;
+
+ event.display = display; /* Ignored, but what the hell */
+ event.window = win->id;
+ event.root = win->s->root;
+ event.subwindow = None;
+ event.time = CurrentTime;
+ event.x = win->g.x;
+ event.y = win->g.y;
+ event.x_root = 1;
+ event.y_root = 1;
+ event.same_screen = True;
+ event.keycode = XKeysymToKeycode(display, keysym);
+ event.state = modifiers;
+
+ event.type = KeyPress;
+ XSendEvent(event.display, event.window, True,
+ KeyPressMask, (XEvent *)&event);
+
+ event.type = KeyRelease;
+ XSendEvent(event.display, event.window, True,
+ KeyPressMask, (XEvent *)&event);
+
+}
+
+void
+restart(struct swm_region *r, union arg *args)
+{
+ DNPRINTF(SWM_D_MISC, "restart: %s\n", start_argv[0]);
+
+ /* disable alarm because the following code may not be interrupted */
+ alarm(0);
+ if (signal(SIGALRM, SIG_IGN) == SIG_ERR)
+ errx(1, "can't disable alarm");
+
+ bar_extra_stop();
+ bar_extra = 1;
+ unmap_all();
+ XCloseDisplay(display);
+ execvp(start_argv[0], start_argv);
+ fprintf(stderr, "execvp failed\n");
+ perror(" failed");
+ quit(NULL, NULL);
+}
+
+struct swm_region *
+root_to_region(Window root)
+{
+ struct swm_region *r = NULL;
+ Window rr, cr;
+ int i, x, y, wx, wy;
+ unsigned int mask;
+
+ for (i = 0; i < ScreenCount(display); i++)
+ if (screens[i].root == root)
+ break;
+
+ if (XQueryPointer(display, screens[i].root,
+ &rr, &cr, &x, &y, &wx, &wy, &mask) != False) {
+ /* choose a region based on pointer location */
+ TAILQ_FOREACH(r, &screens[i].rl, entry)
+ if (x >= X(r) && x <= X(r) + WIDTH(r) &&
+ y >= Y(r) && y <= Y(r) + HEIGHT(r))
+ break;
+ }
+
+ if (r == NULL)
+ r = TAILQ_FIRST(&screens[i].rl);
+
+ return (r);
+}
+
+struct ws_win *
+find_unmanaged_window(Window id)
+{
+ struct ws_win *win;
+ int i, j;
+
+ for (i = 0; i < ScreenCount(display); i++)
+ for (j = 0; j < SWM_WS_MAX; j++)
+ TAILQ_FOREACH(win, &screens[i].ws[j].unmanagedlist,
+ entry)
+ if (id == win->id)
+ return (win);
+ return (NULL);
+}
+
+struct ws_win *
+find_window(Window id)
+{
+ struct ws_win *win;
+ Window wrr, wpr, *wcr = NULL;
+ int i, j;
+ unsigned int nc;
+
+ for (i = 0; i < ScreenCount(display); i++)
+ for (j = 0; j < SWM_WS_MAX; j++)
+ TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
+ if (id == win->id)
+ return (win);
+
+ /* if we were looking for the parent return that window instead */
+ if (XQueryTree(display, id, &wrr, &wpr, &wcr, &nc) == 0)
+ return (NULL);
+ if (wcr)
+ XFree(wcr);
+
+ /* ignore not found and root */
+ if (wpr == 0 || wrr == wpr)
+ return (NULL);
+
+ /* look for parent */
+ for (i = 0; i < ScreenCount(display); i++)
+ for (j = 0; j < SWM_WS_MAX; j++)
+ TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
+ if (wpr == win->id)
+ return (win);
+
+ return (NULL);
+}
+
+void
+spawn(struct swm_region *r, union arg *args, int close_fd)
+{
+ int fd;
+ char *ret = NULL;
+
+ DNPRINTF(SWM_D_MISC, "spawn: %s\n", args->argv[0]);
+
+ if (display)
+ close(ConnectionNumber(display));
+
+ setenv("LD_PRELOAD", SWM_LIB, 1);
+
+ if (asprintf(&ret, "%d", r->ws->idx) == -1) {
+ perror("_SWM_WS");
+ _exit(1);
+ }
+ setenv("_SWM_WS", ret, 1);
+ free(ret);
+ ret = NULL;
+
+ if (asprintf(&ret, "%d", getpid()) == -1) {
+ perror("_SWM_PID");
+ _exit(1);
+ }
+ setenv("_SWM_PID", ret, 1);
+ free(ret);
+ ret = NULL;
+
+ if (setsid() == -1) {
+ perror("setsid");
+ _exit(1);
+ }
+
+ if (close_fd) {
+ /*
+ * close stdin and stdout to prevent interaction between apps
+ * and the baraction script
+ * leave stderr open to record errors
+ */
+ if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) {
+ perror("open");
+ _exit(1);
+ }
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ if (fd > 2)
+ close(fd);
+ }
+
+ execvp(args->argv[0], args->argv);
+
+ perror("execvp");
+ _exit(1);
+}
+
+void
+spawnterm(struct swm_region *r, union arg *args)
+{
+ DNPRINTF(SWM_D_MISC, "spawnterm\n");
+
+ if (term_width)
+ setenv("_SWM_XTERM_FONTADJ", "", 1);
+ if (fork() == 0)
+ spawn(r, args, 1);
+}
+
+void
+kill_refs(struct ws_win *win)
+{
+ int i, x;
+ struct swm_region *r;
+ struct workspace *ws;
+
+ if (win == NULL)
+ return;
+
+ for (i = 0; i < ScreenCount(display); i++)
+ TAILQ_FOREACH(r, &screens[i].rl, entry)
+ for (x = 0; x < SWM_WS_MAX; x++) {
+ ws = &r->s->ws[x];
+ if (win == ws->focus)
+ ws->focus = NULL;
+ if (win == ws->focus_prev)
+ ws->focus_prev = NULL;
+ }
+}
+
+int
+validate_win(struct ws_win *testwin)
+{
+ struct ws_win *win;
+ struct workspace *ws;
+ struct swm_region *r;
+ int i, x, foundit = 0;
+
+ if (testwin == NULL)
+ return(0);
+
+ for (i = 0, foundit = 0; i < ScreenCount(display); i++)
+ TAILQ_FOREACH(r, &screens[i].rl, entry)
+ for (x = 0; x < SWM_WS_MAX; x++) {
+ ws = &r->s->ws[x];
+ TAILQ_FOREACH(win, &ws->winlist, entry)
+ if (win == testwin)
+ return (0);
+ }
+ return (1);
+}
+
+int
+validate_ws(struct workspace *testws)
+{
+ struct swm_region *r;
+ struct workspace *ws;
+ int foundit, i, x;
+
+ /* validate all ws */
+ for (i = 0, foundit = 0; i < ScreenCount(display); i++)
+ TAILQ_FOREACH(r, &screens[i].rl, entry)
+ for (x = 0; x < SWM_WS_MAX; x++) {
+ ws = &r->s->ws[x];
+ if (ws == testws)
+ return (0);
+ }
+ return (1);
+}
+
+void
+unfocus_win(struct ws_win *win)
+{
+ XEvent cne;
+ Window none = None;
+
+ DNPRINTF(SWM_D_FOCUS, "unfocus_win: id: %lu\n", WINID(win));
+
+ if (win == NULL)
+ return;
+ if (win->ws == NULL)
+ return;
+
+ if (validate_ws(win->ws))
+ return; /* XXX this gets hit with thunderbird, needs fixing */
+
+ if (win->ws->r == NULL)
+ return;
+
+ if (validate_win(win)) {
+ kill_refs(win);
+ return;
+ }
+
+ if (win->ws->focus == win) {
+ win->ws->focus = NULL;
+ win->ws->focus_prev = win;
+ }
+
+ if (validate_win(win->ws->focus)) {
+ kill_refs(win->ws->focus);
+ win->ws->focus = NULL;
+ }
+ if (validate_win(win->ws->focus_prev)) {
+ kill_refs(win->ws->focus_prev);
+ win->ws->focus_prev = NULL;
+ }
+
+ /* drain all previous unfocus events */
+ while (XCheckTypedEvent(display, FocusOut, &cne) == True)
+ ;
+
+ grabbuttons(win, 0);
+ XSetWindowBorder(display, win->id,
+ win->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color);
+
+ XChangeProperty(display, win->s->root,
+ ewmh[_NET_ACTIVE_WINDOW].atom, XA_WINDOW, 32,
+ PropModeReplace, (unsigned char *)&none,1);
+}
+
+void
+unfocus_all(void)
+{
+ struct ws_win *win;
+ int i, j;
+
+ DNPRINTF(SWM_D_FOCUS, "unfocus_all\n");
+
+ for (i = 0; i < ScreenCount(display); i++)
+ for (j = 0; j < SWM_WS_MAX; j++)
+ TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
+ unfocus_win(win);
+}
+
+void
+focus_win(struct ws_win *win)
+{
+ XEvent cne;
+ Window cur_focus;
+ int rr;
+ struct ws_win *cfw = NULL;
+
+
+ DNPRINTF(SWM_D_FOCUS, "focus_win: id: %lu\n", win ? win->id : 0);
+
+ if (win == NULL)
+ return;
+ if (win->ws == NULL)
+ return;
+
+ if (validate_ws(win->ws))
+ return; /* XXX this gets hit with thunderbird, needs fixing */
+
+ if (validate_win(win)) {
+ kill_refs(win);
+ return;
+ }
+
+ if (validate_win(win)) {
+ kill_refs(win);
+ return;
+ }
+
+ XGetInputFocus(display, &cur_focus, &rr);
+ if ((cfw = find_window(cur_focus)) != NULL)
+ unfocus_win(cfw);
+
+ win->ws->focus = win;
+
+ if (win->ws->r != NULL) {
+ /* drain all previous focus events */
+ while (XCheckTypedEvent(display, FocusIn, &cne) == True)
+ ;
+
+ if (win->java == 0)
+ XSetInputFocus(display, win->id,
+ RevertToParent, CurrentTime);
+ XMapRaised(display, win->id);
+ grabbuttons(win, 1);
+ XSetWindowBorder(display, win->id,
+ win->ws->r->s->c[SWM_S_COLOR_FOCUS].color);
+ if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS)
+ XMapRaised(display, win->id);
+
+ XChangeProperty(display, win->s->root,
+ ewmh[_NET_ACTIVE_WINDOW].atom, XA_WINDOW, 32,
+ PropModeReplace, (unsigned char *)&win->id,1);
+ }
+
+ if (window_name_enabled)
+ bar_update();
+}
+
+void
+switchws(struct swm_region *r, union arg *args)
+{
+ int wsid = args->id, unmap_old = 0;
+ struct swm_region *this_r, *other_r;
+ struct ws_win *win;
+ struct workspace *new_ws, *old_ws;
+ union arg a;
+
+ if (!(r && r->s))
+ return;
+
+ this_r = r;
+ old_ws = this_r->ws;
+ new_ws = &this_r->s->ws[wsid];
+
+ DNPRINTF(SWM_D_WS, "switchws screen[%d]:%dx%d+%d+%d: "
+ "%d -> %d\n", r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r),
+ old_ws->idx, wsid);
+
+ if (new_ws == NULL || old_ws == NULL)
+ return;
+ if (new_ws == old_ws)
+ return;
+
+ other_r = new_ws->r;
+ if (other_r == NULL) {
+ /* the other workspace is hidden, hide this one */
+ old_ws->r = NULL;
+ unmap_old = 1;
+ } else {
+ /* the other ws is visible in another region, exchange them */
+ other_r->ws_prior = new_ws;
+ other_r->ws = old_ws;
+ old_ws->r = other_r;
+ }
+ this_r->ws_prior = old_ws;
+ this_r->ws = new_ws;
+ new_ws->r = this_r;
+
+ stack();
+ a.id = SWM_ARG_ID_FOCUSCUR;
+ focus(new_ws->r, &a);
+ bar_update();
+
+ /* unmap old windows */
+ if (unmap_old)
+ TAILQ_FOREACH(win, &old_ws->winlist, entry)
+ unmap_window(win);
+}
+
+void
+cyclews(struct swm_region *r, union arg *args)
+{
+ union arg a;
+ struct swm_screen *s = r->s;
+
+ DNPRINTF(SWM_D_WS, "cyclews id %d "
+ "in screen[%d]:%dx%d+%d+%d ws %d\n", args->id,
+ r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
+
+ a.id = r->ws->idx;
+ do {
+ switch (args->id) {
+ case SWM_ARG_ID_CYCLEWS_UP:
+ if (a.id < SWM_WS_MAX - 1)
+ a.id++;
+ else
+ a.id = 0;