+ 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
+drain_enter_notify(void)
+{
+ int i = 0;
+ XEvent cne;
+
+ while (XCheckMaskEvent(display, EnterWindowMask, &cne))
+ i++;
+
+ DNPRINTF(SWM_D_MISC, "drain_enter_notify: drained %d\n", i);
+}
+
+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 Build: %s",
+ SCROTWM_VERSION, buildstr);
+ 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(int ws_idx, 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", 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 (fork() == 0) {
+ if (term_width)
+ setenv("_SWM_XTERM_FONTADJ", "", 1);
+ spawn(r->ws->idx, 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);
+ else {
+ /* use larger hammer since the window was killed somehow */
+ TAILQ_FOREACH(cfw, &win->ws->winlist, entry)
+ if (cfw->ws && cfw->ws->r && cfw->ws->r->s)
+ XSetWindowBorder(display, cfw->id,
+ cfw->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color);
+ }
+
+ 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);
+ 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 ||
+ win->ws->always_raise)
+ 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;
+
+ /* this is needed so that we can click on a window after a restart */
+ unfocus_all();
+
+ 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);
+
+ if (focus_mode == SWM_FOCUS_DEFAULT)
+ drain_enter_notify();
+}
+
+void
+cyclews(struct swm_region *r, union arg *args)
+{
+ union arg a;
+ struct swm_screen *s = r->s;
+ int cycle_all = 0;
+
+ 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_ALL:
+ cycle_all = 1;
+ /* FALLTHROUGH */
+ case SWM_ARG_ID_CYCLEWS_UP:
+ if (a.id < SWM_WS_MAX - 1)
+ a.id++;
+ else
+ a.id = 0;
+ break;
+ case SWM_ARG_ID_CYCLEWS_DOWN_ALL:
+ cycle_all = 1;
+ /* FALLTHROUGH */
+ case SWM_ARG_ID_CYCLEWS_DOWN:
+ if (a.id > 0)
+ a.id--;
+ else
+ a.id = SWM_WS_MAX - 1;