+ r->ws->always_raise = !r->ws->always_raise;
+
+ /* bring floaters back to top */
+ if (r->ws->always_raise == 0)
+ stack();
+}
+
+void
+iconify(struct swm_region *r, union arg *args)
+{
+ union arg a;
+
+ if (r->ws->focus == NULL)
+ return;
+ unmap_window(r->ws->focus);
+ update_iconic(r->ws->focus, 1);
+ stack();
+ if (focus_mode == SWM_FOCUS_DEFAULT)
+ drain_enter_notify();
+ r->ws->focus = NULL;
+ a.id = SWM_ARG_ID_FOCUSCUR;
+ focus(r, &a);
+}
+
+unsigned char *
+get_win_name(Window win)
+{
+ unsigned char *prop = NULL;
+ unsigned long nbytes, nitems;
+
+ /* try _NET_WM_NAME first */
+ if (get_property(win, a_netwmname, 0L, a_utf8_string, NULL, &nbytes,
+ &prop)) {
+ XFree(prop);
+ if (get_property(win, a_netwmname, nbytes, a_utf8_string,
+ &nitems, NULL, &prop))
+ return (prop);
+ }
+
+ /* fallback to WM_NAME */
+ if (!get_property(win, a_wmname, 0L, a_string, NULL, &nbytes, &prop))
+ return (NULL);
+ XFree(prop);
+ if (get_property(win, a_wmname, nbytes, a_string, &nitems, NULL, &prop))
+ return (prop);
+
+ return (NULL);
+}
+
+void
+uniconify(struct swm_region *r, union arg *args)
+{
+ struct ws_win *win;
+ FILE *lfile;
+ unsigned char *name;
+ int count = 0;
+
+ DNPRINTF(SWM_D_MISC, "uniconify\n");
+
+ if (r && r->ws == NULL)
+ return;
+
+ /* make sure we have anything to uniconify */
+ TAILQ_FOREACH(win, &r->ws->winlist, entry) {
+ if (win->ws == NULL)
+ continue; /* should never happen */
+ if (win->iconic == 0)
+ continue;
+ count++;
+ }
+ if (count == 0)
+ return;
+
+ search_r = r;
+ search_resp_action = SWM_SEARCH_UNICONIFY;
+
+ spawn_select(r, args, "search", &searchpid);
+
+ if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
+ return;
+
+ TAILQ_FOREACH(win, &r->ws->winlist, entry) {
+ if (win->ws == NULL)
+ continue; /* should never happen */
+ if (win->iconic == 0)
+ continue;
+
+ name = get_win_name(win->id);
+ if (name == NULL)
+ continue;
+ fprintf(lfile, "%s.%lu\n", name, win->id);
+ XFree(name);
+ }
+
+ fclose(lfile);
+}
+
+void
+name_workspace(struct swm_region *r, union arg *args)
+{
+ FILE *lfile;
+
+ DNPRINTF(SWM_D_MISC, "name_workspace\n");
+
+ if (r == NULL)
+ return;
+
+ search_r = r;
+ search_resp_action = SWM_SEARCH_NAME_WORKSPACE;
+
+ spawn_select(r, args, "name_workspace", &searchpid);
+
+ if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
+ return;
+
+ fprintf(lfile, "%s", "");
+ fclose(lfile);
+}
+
+void
+search_workspace(struct swm_region *r, union arg *args)
+{
+ int i;
+ struct workspace *ws;
+ FILE *lfile;
+
+ DNPRINTF(SWM_D_MISC, "search_workspace\n");
+
+ if (r == NULL)
+ return;
+
+ search_r = r;
+ search_resp_action = SWM_SEARCH_SEARCH_WORKSPACE;
+
+ spawn_select(r, args, "search", &searchpid);
+
+ if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
+ return;
+
+ for (i = 0; i < SWM_WS_MAX; i++) {
+ ws = &r->s->ws[i];
+ if (ws == NULL)
+ continue;
+ fprintf(lfile, "%d%s%s\n", ws->idx + 1,
+ (ws->name ? ":" : ""), (ws->name ? ws->name : ""));
+ }
+
+ fclose(lfile);
+}
+
+void
+search_win_cleanup(void)
+{
+ struct search_window *sw = NULL;
+
+ while ((sw = TAILQ_FIRST(&search_wl)) != NULL) {
+ XDestroyWindow(display, sw->indicator);
+ XFreeGC(display, sw->gc);
+ TAILQ_REMOVE(&search_wl, sw, entry);
+ free(sw);
+ }
+}
+
+void
+search_win(struct swm_region *r, union arg *args)
+{
+ struct ws_win *win = NULL;
+ struct search_window *sw = NULL;
+ Window w;
+ XGCValues gcv;
+ int i;
+ char s[8];
+ FILE *lfile;
+ size_t len;
+ XRectangle ibox, lbox;
+
+ DNPRINTF(SWM_D_MISC, "search_win\n");
+
+ search_r = r;
+ search_resp_action = SWM_SEARCH_SEARCH_WINDOW;
+
+ spawn_select(r, args, "search", &searchpid);
+
+ if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
+ return;
+
+ TAILQ_INIT(&search_wl);
+
+ i = 1;
+ TAILQ_FOREACH(win, &r->ws->winlist, entry) {
+ if (win->iconic == 1)
+ continue;
+
+ sw = calloc(1, sizeof(struct search_window));
+ if (sw == NULL) {
+ warn("search_win: calloc");
+ fclose(lfile);
+ search_win_cleanup();
+ return;
+ }
+ sw->idx = i;
+ sw->win = win;
+
+ snprintf(s, sizeof s, "%d", i);
+ len = strlen(s);
+
+ XmbTextExtents(bar_fs, s, len, &ibox, &lbox);
+
+ w = XCreateSimpleWindow(display,
+ win->id, 0, 0,lbox.width + 4,
+ bar_fs_extents->max_logical_extent.height, 1,
+ r->s->c[SWM_S_COLOR_UNFOCUS].color,
+ r->s->c[SWM_S_COLOR_FOCUS].color);
+
+ sw->indicator = w;
+ TAILQ_INSERT_TAIL(&search_wl, sw, entry);
+
+ sw->gc = XCreateGC(display, w, 0, &gcv);
+ XMapRaised(display, w);
+ XSetForeground(display, sw->gc, r->s->c[SWM_S_COLOR_BAR].color);
+
+ DRAWSTRING(display, w, bar_fs, sw->gc, 2,
+ (bar_fs_extents->max_logical_extent.height -
+ lbox.height) / 2 - lbox.y, s, len);
+
+ fprintf(lfile, "%d\n", i);
+ i++;
+ }
+
+ fclose(lfile);
+}
+
+void
+search_resp_uniconify(char *resp, unsigned long len)
+{
+ unsigned char *name;
+ struct ws_win *win;
+ char *s;
+
+ DNPRINTF(SWM_D_MISC, "search_resp_uniconify: resp: %s\n", resp);
+
+ TAILQ_FOREACH(win, &search_r->ws->winlist, entry) {
+ if (win->iconic == 0)
+ continue;
+ name = get_win_name(win->id);
+ if (name == NULL)
+ continue;
+ if (asprintf(&s, "%s.%lu", name, win->id) == -1) {
+ XFree(name);
+ continue;
+ }
+ XFree(name);
+ if (strncmp(s, resp, len) == 0) {
+ /* XXX this should be a callback to generalize */
+ update_iconic(win, 0);
+ free(s);
+ break;
+ }
+ free(s);
+ }
+}
+
+void
+search_resp_name_workspace(char *resp, unsigned long len)
+{
+ struct workspace *ws;
+
+ DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: resp: %s\n", resp);
+
+ if (search_r->ws == NULL)
+ return;
+ ws = search_r->ws;
+
+ if (ws->name) {
+ free(search_r->ws->name);
+ search_r->ws->name = NULL;
+ }
+
+ if (len > 1) {
+ ws->name = strdup(resp);
+ if (ws->name == NULL) {
+ DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: "
+ "strdup: %s", strerror(errno));
+ return;
+ }
+ }
+}
+
+void
+search_resp_search_workspace(char *resp, unsigned long len)
+{
+ char *p, *q;
+ int ws_idx;
+ const char *errstr;
+ union arg a;
+
+ DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: resp: %s\n", resp);
+
+ q = strdup(resp);
+ if (!q) {
+ DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: strdup: %s",
+ strerror(errno));
+ return;
+ }
+ p = strchr(q, ':');
+ if (p != NULL)
+ *p = '\0';
+ ws_idx = strtonum(q, 1, SWM_WS_MAX, &errstr);
+ if (errstr) {
+ DNPRINTF(SWM_D_MISC, "workspace idx is %s: %s",
+ errstr, q);
+ free(q);
+ return;
+ }
+ free(q);
+ a.id = ws_idx - 1;
+ switchws(search_r, &a);
+}
+
+void
+search_resp_search_window(char *resp, unsigned long len)
+{
+ char *s;
+ int idx;
+ const char *errstr;
+ struct search_window *sw;
+
+ DNPRINTF(SWM_D_MISC, "search_resp_search_window: resp: %s\n", resp);
+
+ s = strdup(resp);
+ if (!s) {
+ DNPRINTF(SWM_D_MISC, "search_resp_search_window: strdup: %s",
+ strerror(errno));
+ return;
+ }
+
+ idx = strtonum(s, 1, INT_MAX, &errstr);
+ if (errstr) {
+ DNPRINTF(SWM_D_MISC, "window idx is %s: %s",
+ errstr, s);
+ free(s);
+ return;
+ }
+ free(s);
+
+ TAILQ_FOREACH(sw, &search_wl, entry)
+ if (idx == sw->idx) {
+ focus_win(sw->win);
+ break;
+ }
+}
+
+#define MAX_RESP_LEN 1024
+
+void
+search_do_resp(void)
+{
+ ssize_t rbytes;
+ char *resp;
+ unsigned long len;
+
+ DNPRINTF(SWM_D_MISC, "search_do_resp:\n");
+
+ search_resp = 0;
+ searchpid = 0;
+
+ if ((resp = calloc(1, MAX_RESP_LEN + 1)) == NULL) {
+ warn("search: calloc");
+ goto done;
+ }
+
+ rbytes = read(select_resp_pipe[0], resp, MAX_RESP_LEN);
+ if (rbytes <= 0) {
+ warn("search: read error");
+ goto done;
+ }
+ resp[rbytes] = '\0';
+
+ /* XXX:
+ * Older versions of dmenu (Atleast pre 4.4.1) do not send a
+ * newline, so work around that by sanitizing the resp now.
+ */
+ resp[strcspn(resp, "\n")] = '\0';
+ len = strlen(resp);
+
+ switch (search_resp_action) {
+ case SWM_SEARCH_UNICONIFY:
+ search_resp_uniconify(resp, len);
+ break;
+ case SWM_SEARCH_NAME_WORKSPACE:
+ search_resp_name_workspace(resp, len);
+ break;
+ case SWM_SEARCH_SEARCH_WORKSPACE:
+ search_resp_search_workspace(resp, len);
+ break;
+ case SWM_SEARCH_SEARCH_WINDOW:
+ search_resp_search_window(resp, len);
+ break;
+ }
+
+done:
+ if (search_resp_action == SWM_SEARCH_SEARCH_WINDOW)
+ search_win_cleanup();
+
+ search_resp_action = SWM_SEARCH_NONE;
+ close(select_resp_pipe[0]);
+ free(resp);
+}
+
+void
+wkill(struct swm_region *r, union arg *args)
+{
+ DNPRINTF(SWM_D_MISC, "wkill: id: %d\n", args->id);
+
+ if (r->ws->focus == NULL)
+ return;
+
+ if (args->id == SWM_ARG_ID_KILLWINDOW)
+ XKillClient(display, r->ws->focus->id);
+ else
+ if (r->ws->focus->can_delete)
+ client_msg(r->ws->focus, adelete);
+}