0 /*
1 * calmwm - the calm window manager
2 *
3 * Copyright (c) 2004 Martin Murray <mmurray@monkey.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 *
17 * $OpenBSD$
18 */
19
20 #include <sys/types.h>
21 #include <sys/queue.h>
22 #include <sys/stat.h>
23
24 #include <dirent.h>
25 #include <err.h>
26 #include <errno.h>
27 #include <limits.h>
28 #include <paths.h>
29 #include <signal.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34
35 #include "calmwm.h"
36
37 #define HASH_MARKER "|1|"
38
39 extern sig_atomic_t cwm_status;
40
41 static void kbfunc_amount(int, int, int *, int *);
42 static void kbfunc_client_move_kb(void *, struct cargs *);
43 static void kbfunc_client_move_mb(void *, struct cargs *);
44 static void kbfunc_client_resize_kb(void *, struct cargs *);
45 static void kbfunc_client_resize_mb(void *, struct cargs *);
46
47 void
48 kbfunc_cwm_status(void *ctx, struct cargs *cargs)
49 {
50 cwm_status = cargs->flag;
51 }
52
53 static void
54 kbfunc_amount(int flags, int amt, int *mx, int *my)
55 {
56 #define CWM_FACTOR 10
57
58 if (flags & CWM_BIGAMOUNT)
59 amt *= CWM_FACTOR;
60
61 switch (flags & (CWM_UP | CWM_DOWN | CWM_LEFT | CWM_RIGHT)) {
62 case CWM_UP:
63 *my -= amt;
64 break;
65 case CWM_DOWN:
66 *my += amt;
67 break;
68 case CWM_RIGHT:
69 *mx += amt;
70 break;
71 case CWM_LEFT:
72 *mx -= amt;
73 break;
74 }
75 }
76
77 void
78 kbfunc_ptrmove(void *ctx, struct cargs *cargs)
79 {
80 struct screen_ctx *sc = ctx;
81 int x, y;
82 int mx = 0, my = 0;
83
84 kbfunc_amount(cargs->flag, Conf.mamount, &mx, &my);
85
86 xu_ptr_get(sc->rootwin, &x, &y);
87 xu_ptr_set(sc->rootwin, x + mx, y + my);
88 }
89
90 void
91 kbfunc_client_move(void *ctx, struct cargs *cargs)
92 {
93 if (cargs->xev == CWM_XEV_BTN)
94 kbfunc_client_move_mb(ctx, cargs);
95 else
96 kbfunc_client_move_kb(ctx, cargs);
97 }
98
99 void
100 kbfunc_client_resize(void *ctx, struct cargs *cargs)
101 {
102 if (cargs->xev == CWM_XEV_BTN)
103 kbfunc_client_resize_mb(ctx, cargs);
104 else
105 kbfunc_client_resize_kb(ctx, cargs);
106 }
107
108 static void
109 kbfunc_client_move_kb(void *ctx, struct cargs *cargs)
110 {
111 struct client_ctx *cc = ctx;
112 struct screen_ctx *sc = cc->sc;
113 struct geom area;
114 int mx = 0, my = 0;
115
116 if (cc->flags & CLIENT_FREEZE)
117 return;
118
119 kbfunc_amount(cargs->flag, Conf.mamount, &mx, &my);
120
121 cc->geom.x += mx;
122 if (cc->geom.x < -(cc->geom.w + cc->bwidth - 1))
123 cc->geom.x = -(cc->geom.w + cc->bwidth - 1);
124 if (cc->geom.x > (sc->view.w - cc->bwidth - 1))
125 cc->geom.x = sc->view.w - cc->bwidth - 1;
126 cc->geom.y += my;
127 if (cc->geom.y < -(cc->geom.h + cc->bwidth - 1))
128 cc->geom.y = -(cc->geom.h + cc->bwidth - 1);
129 if (cc->geom.y > (sc->view.h - cc->bwidth - 1))
130 cc->geom.y = sc->view.h - cc->bwidth - 1;
131
132 area = screen_area(sc,
133 cc->geom.x + cc->geom.w / 2,
134 cc->geom.y + cc->geom.h / 2, 1);
135 cc->geom.x += client_snapcalc(cc->geom.x,
136 cc->geom.x + cc->geom.w + (cc->bwidth * 2),
137 area.x, area.x + area.w, sc->snapdist);
138 cc->geom.y += client_snapcalc(cc->geom.y,
139 cc->geom.y + cc->geom.h + (cc->bwidth * 2),
140 area.y, area.y + area.h, sc->snapdist);
141
142 client_move(cc);
143 client_ptr_inbound(cc, 1);
144 XSync(X_Dpy, True);
145 }
146
147 static void
148 kbfunc_client_move_mb(void *ctx, struct cargs *cargs)
149 {
150 struct client_ctx *cc = ctx;
151 XEvent ev;
152 Time ltime = 0;
153 struct screen_ctx *sc = cc->sc;
154 struct geom area;
155 int move = 1;
156
157 client_raise(cc);
158
159 if (cc->flags & CLIENT_FREEZE)
160 return;
161
162 client_ptr_inbound(cc, 1);
163
164 if (XGrabPointer(X_Dpy, cc->win, False, MOUSEMASK,
165 GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_MOVE],
166 CurrentTime) != GrabSuccess)
167 return;
168
169 screen_prop_win_create(sc, cc->win);
170 screen_prop_win_draw(sc, "%+5d%+5d", cc->geom.x, cc->geom.y);
171 while (move) {
172 XMaskEvent(X_Dpy, MOUSEMASK, &ev);
173 switch (ev.type) {
174 case MotionNotify:
175 /* not more than 60 times / second */
176 if ((ev.xmotion.time - ltime) <= (1000 / 60))
177 continue;
178 ltime = ev.xmotion.time;
179
180 cc->geom.x = ev.xmotion.x_root - cc->ptr.x - cc->bwidth;
181 cc->geom.y = ev.xmotion.y_root - cc->ptr.y - cc->bwidth;
182
183 area = screen_area(sc,
184 cc->geom.x + cc->geom.w / 2,
185 cc->geom.y + cc->geom.h / 2, 1);
186 cc->geom.x += client_snapcalc(cc->geom.x,
187 cc->geom.x + cc->geom.w + (cc->bwidth * 2),
188 area.x, area.x + area.w, sc->snapdist);
189 cc->geom.y += client_snapcalc(cc->geom.y,
190 cc->geom.y + cc->geom.h + (cc->bwidth * 2),
191 area.y, area.y + area.h, sc->snapdist);
192 client_move(cc);
193 screen_prop_win_draw(sc,
194 "%+5d%+5d", cc->geom.x, cc->geom.y);
195 break;
196 case ButtonRelease:
197 move = 0;
198 break;
199 }
200 }
201 if (ltime)
202 client_move(cc);
203 screen_prop_win_destroy(sc);
204 XUngrabPointer(X_Dpy, CurrentTime);
205 }
206
207 static void
208 kbfunc_client_resize_kb(void *ctx, struct cargs *cargs)
209 {
210 struct client_ctx *cc = ctx;
211 int mx = 0, my = 0;
212 int amt = 1;
213
214 if (cc->flags & CLIENT_FREEZE)
215 return;
216
217 if (!(cc->hint.flags & PResizeInc))
218 amt = Conf.mamount;
219
220 kbfunc_amount(cargs->flag, amt, &mx, &my);
221
222 if ((cc->geom.w += mx * cc->hint.incw) < cc->hint.minw)
223 cc->geom.w = cc->hint.minw;
224 if ((cc->geom.h += my * cc->hint.inch) < cc->hint.minh)
225 cc->geom.h = cc->hint.minh;
226 if (cc->geom.x + cc->geom.w + cc->bwidth - 1 < 0)
227 cc->geom.x = -(cc->geom.w + cc->bwidth - 1);
228 if (cc->geom.y + cc->geom.h + cc->bwidth - 1 < 0)
229 cc->geom.y = -(cc->geom.h + cc->bwidth - 1);
230
231 client_resize(cc, 1);
232 client_ptr_inbound(cc, 1);
233 XSync(X_Dpy, True);
234 }
235
236 static void
237 kbfunc_client_resize_mb(void *ctx, struct cargs *cargs)
238 {
239 struct client_ctx *cc = ctx;
240 XEvent ev;
241 Time ltime = 0;
242 struct screen_ctx *sc = cc->sc;
243 int resize = 1;
244
245 if (cc->flags & CLIENT_FREEZE)
246 return;
247
248 client_raise(cc);
249 client_ptr_save(cc);
250
251 xu_ptr_set(cc->win, cc->geom.w, cc->geom.h);
252
253 if (XGrabPointer(X_Dpy, cc->win, False, MOUSEMASK,
254 GrabModeAsync, GrabModeAsync, None, Conf.cursor[CF_RESIZE],
255 CurrentTime) != GrabSuccess)
256 return;
257
258 screen_prop_win_create(sc, cc->win);
259 screen_prop_win_draw(sc, "%4d x %-4d", cc->dim.w, cc->dim.h);
260 while (resize) {
261 XMaskEvent(X_Dpy, MOUSEMASK, &ev);
262 switch (ev.type) {
263 case MotionNotify:
264 /* not more than 60 times / second */
265 if ((ev.xmotion.time - ltime) <= (1000 / 60))
266 continue;
267 ltime = ev.xmotion.time;
268
269 cc->geom.w = ev.xmotion.x;
270 cc->geom.h = ev.xmotion.y;
271 client_apply_sizehints(cc);
272 client_resize(cc, 1);
273 screen_prop_win_draw(sc,
274 "%4d x %-4d", cc->dim.w, cc->dim.h);
275 break;
276 case ButtonRelease:
277 resize = 0;
278 break;
279 }
280 }
281 if (ltime)
282 client_resize(cc, 1);
283 screen_prop_win_destroy(sc);
284 XUngrabPointer(X_Dpy, CurrentTime);
285
286 /* Make sure the pointer stays within the window. */
287 client_ptr_inbound(cc, 0);
288 }
289
290 void
291 kbfunc_client_snap(void *ctx, struct cargs *cargs)
292 {
293 struct client_ctx *cc = ctx;
294 struct screen_ctx *sc = cc->sc;
295 struct geom area;
296 int flags;
297
298 area = screen_area(sc,
299 cc->geom.x + cc->geom.w / 2,
300 cc->geom.y + cc->geom.h / 2, 1);
301
302 flags = cargs->flag;
303 while (flags) {
304 if (flags & CWM_UP) {
305 cc->geom.y = area.y;
306 flags &= ~CWM_UP;
307 }
308 if (flags & CWM_LEFT) {
309 cc->geom.x = area.x;
310 flags &= ~CWM_LEFT;
311 }
312 if (flags & CWM_RIGHT) {
313 cc->geom.x = area.x + area.w - cc->geom.w -
314 (cc->bwidth * 2);
315 flags &= ~CWM_RIGHT;
316 }
317 if (flags & CWM_DOWN) {
318 cc->geom.y = area.y + area.h - cc->geom.h -
319 (cc->bwidth * 2);
320 flags &= ~CWM_DOWN;
321 }
322 }
323 client_move(cc);
324 client_ptr_inbound(cc, 1);
325 }
326
327 void
328 kbfunc_client_close(void *ctx, struct cargs *cargs)
329 {
330 client_close(ctx);
331 }
332
333 void
334 kbfunc_client_lower(void *ctx, struct cargs *cargs)
335 {
336 client_ptr_save(ctx);
337 client_lower(ctx);
338 }
339
340 void
341 kbfunc_client_raise(void *ctx, struct cargs *cargs)
342 {
343 client_raise(ctx);
344 }
345
346 void
347 kbfunc_client_hide(void *ctx, struct cargs *cargs)
348 {
349 client_hide(ctx);
350 }
351
352 void
353 kbfunc_client_toggle_freeze(void *ctx, struct cargs *cargs)
354 {
355 client_toggle_freeze(ctx);
356 }
357
358 void
359 kbfunc_client_toggle_sticky(void *ctx, struct cargs *cargs)
360 {
361 client_toggle_sticky(ctx);
362 }
363
364 void
365 kbfunc_client_toggle_fullscreen(void *ctx, struct cargs *cargs)
366 {
367 client_toggle_fullscreen(ctx);
368 }
369
370 void
371 kbfunc_client_toggle_maximize(void *ctx, struct cargs *cargs)
372 {
373 client_toggle_maximize(ctx);
374 }
375
376 void
377 kbfunc_client_toggle_hmaximize(void *ctx, struct cargs *cargs)
378 {
379 client_toggle_hmaximize(ctx);
380 }
381
382 void
383 kbfunc_client_toggle_vmaximize(void *ctx, struct cargs *cargs)
384 {
385 client_toggle_vmaximize(ctx);
386 }
387
388 void
389 kbfunc_client_htile(void *ctx, struct cargs *cargs)
390 {
391 client_htile(ctx);
392 }
393
394 void
395 kbfunc_client_vtile(void *ctx, struct cargs *cargs)
396 {
397 client_vtile(ctx);
398 }
399
400 void
401 kbfunc_client_cycle(void *ctx, struct cargs *cargs)
402 {
403 struct screen_ctx *sc = ctx;
404 struct client_ctx *newcc, *oldcc, *prevcc;
405 int again = 1, flags = cargs->flag;
406
407 /* For X apps that ignore/steal events. */
408 if (cargs->xev == CWM_XEV_KEY)
409 XGrabKeyboard(X_Dpy, sc->rootwin, True,
410 GrabModeAsync, GrabModeAsync, CurrentTime);
411
412 if (TAILQ_EMPTY(&sc->clientq))
413 return;
414
415 prevcc = TAILQ_FIRST(&sc->clientq);
416 oldcc = client_current(sc);
417 if (oldcc == NULL)
418 oldcc = (flags & CWM_CYCLE_REVERSE) ?
419 TAILQ_LAST(&sc->clientq, client_q) :
420 TAILQ_FIRST(&sc->clientq);
421
422 newcc = oldcc;
423 while (again) {
424 again = 0;
425
426 newcc = (flags & CWM_CYCLE_REVERSE) ? client_prev(newcc) :
427 client_next(newcc);
428
429 /* Only cycle visible and non-ignored windows. */
430 if ((newcc->flags & (CLIENT_SKIP_CYCLE)) ||
431 ((flags & CWM_CYCLE_INGROUP) &&
432 (newcc->gc != oldcc->gc)))
433 again = 1;
434
435 /* Is oldcc the only non-hidden window? */
436 if (newcc == oldcc) {
437 if (again)
438 return; /* No windows visible. */
439 break;
440 }
441 }
442
443 /* Reset when cycling mod is released. XXX I hate this hack */
444 sc->cycling = 1;
445 client_ptr_save(oldcc);
446 client_raise(prevcc);
447 client_raise(newcc);
448 if (!client_inbound(newcc, newcc->ptr.x, newcc->ptr.y)) {
449 newcc->ptr.x = newcc->geom.w / 2;
450 newcc->ptr.y = newcc->geom.h / 2;
451 }
452
453 /* When no client is active, warp pointer to last active. */
454 if (oldcc->flags & (CLIENT_ACTIVE))
455 client_ptr_warp(newcc);
456 else if (oldcc->flags & (CLIENT_SKIP_CYCLE))
457 client_ptr_warp(newcc);
458 else {
459 client_raise(oldcc);
460 client_ptr_warp(oldcc);
461 }
462 }
463
464 void
465 kbfunc_client_toggle_group(void *ctx, struct cargs *cargs)
466 {
467 struct client_ctx *cc = ctx;
468
469 /* For X apps that ignore/steal events. */
470 if (cargs->xev == CWM_XEV_KEY)
471 XGrabKeyboard(X_Dpy, cc->win, True,
472 GrabModeAsync, GrabModeAsync, CurrentTime);
473
474 group_toggle_membership(cc);
475 }
476
477 void
478 kbfunc_client_movetogroup(void *ctx, struct cargs *cargs)
479 {
480 group_movetogroup(ctx, cargs->flag);
481 }
482
483 void
484 kbfunc_group_only(void *ctx, struct cargs *cargs)
485 {
486 group_only(ctx, cargs->flag);
487 }
488
489 void
490 kbfunc_group_last(void *ctx, struct cargs *cargs)
491 {
492 struct screen_ctx *sc = ctx;
493
494 group_only(ctx, sc->group_last->num);
495 }
496
497 void
498 kbfunc_group_toggle(void *ctx, struct cargs *cargs)
499 {
500 group_toggle(ctx, cargs->flag);
501 }
502
503 void
504 kbfunc_group_toggle_all(void *ctx, struct cargs *cargs)
505 {
506 group_toggle_all(ctx);
507 }
508
509 void
510 kbfunc_group_close(void *ctx, struct cargs *cargs)
511 {
512 group_close(ctx, cargs->flag);
513 }
514
515 void
516 kbfunc_group_cycle(void *ctx, struct cargs *cargs)
517 {
518 group_cycle(ctx, cargs->flag);
519 }
520
521 void
522 kbfunc_menu_client(void *ctx, struct cargs *cargs)
523 {
524 struct screen_ctx *sc = ctx;
525 struct client_ctx *cc, *old_cc;
526 struct menu *mi;
527 struct menu_q menuq;
528 int mflags = 0;
529
530 if (cargs->xev == CWM_XEV_BTN)
531 mflags |= CWM_MENU_LIST;
532
533 TAILQ_INIT(&menuq);
534 TAILQ_FOREACH(cc, &sc->clientq, entry) {
535 if ((cargs->flag & CWM_MENU_WINDOW_ALL) ||
536 (cc->flags & CLIENT_HIDDEN))
537 menuq_add(&menuq, cc, NULL);
538 }
539
540 if ((mi = menu_filter(sc, &menuq, "window", NULL, mflags,
541 search_match_client, search_print_client)) != NULL) {
542 cc = (struct client_ctx *)mi->ctx;
543 client_show(cc);
544 if ((old_cc = client_current(sc)) != NULL)
545 client_ptr_save(old_cc);
546 client_ptr_warp(cc);
547 }
548
549 menuq_clear(&menuq);
550 }
551
552 void
553 kbfunc_menu_cmd(void *ctx, struct cargs *cargs)
554 {
555 struct screen_ctx *sc = ctx;
556 struct cmd_ctx *cmd;
557 struct menu *mi;
558 struct menu_q menuq;
559 int mflags = 0;
560
561 if (cargs->xev == CWM_XEV_BTN)
562 mflags |= CWM_MENU_LIST;
563
564 TAILQ_INIT(&menuq);
565 TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
566 if ((strcmp(cmd->name, "lock") == 0) ||
567 (strcmp(cmd->name, "term") == 0))
568 continue;
569 menuq_add(&menuq, cmd, NULL);
570 }
571
572 if ((mi = menu_filter(sc, &menuq, "application", NULL, mflags,
573 search_match_cmd, search_print_cmd)) != NULL) {
574 cmd = (struct cmd_ctx *)mi->ctx;
575 u_spawn(cmd->path);
576 }
577
578 menuq_clear(&menuq);
579 }
580
581 void
582 kbfunc_menu_group(void *ctx, struct cargs *cargs)
583 {
584 struct screen_ctx *sc = ctx;
585 struct group_ctx *gc;
586 struct menu *mi;
587 struct menu_q menuq;
588 int mflags = 0;
589
590 if (cargs->xev == CWM_XEV_BTN)
591 mflags |= CWM_MENU_LIST;
592
593 TAILQ_INIT(&menuq);
594 TAILQ_FOREACH(gc, &sc->groupq, entry) {
595 if (group_holds_only_sticky(gc))
596 continue;
597 menuq_add(&menuq, gc, NULL);
598 }
599
600 if ((mi = menu_filter(sc, &menuq, "group", NULL, mflags,
601 search_match_group, search_print_group)) != NULL) {
602 gc = (struct group_ctx *)mi->ctx;
603 (group_holds_only_hidden(gc)) ?
604 group_show(gc) : group_hide(gc);
605 }
606
607 menuq_clear(&menuq);
608 }
609
610 void
611 kbfunc_menu_wm(void *ctx, struct cargs *cargs)
612 {
613 struct screen_ctx *sc = ctx;
614 struct cmd_ctx *wm;
615 struct menu *mi;
616 struct menu_q menuq;
617 int mflags = 0;
618
619 if (cargs->xev == CWM_XEV_BTN)
620 mflags |= CWM_MENU_LIST;
621
622 TAILQ_INIT(&menuq);
623 TAILQ_FOREACH(wm, &Conf.wmq, entry)
624 menuq_add(&menuq, wm, NULL);
625
626 if ((mi = menu_filter(sc, &menuq, "wm", NULL, mflags,
627 search_match_wm, search_print_wm)) != NULL) {
628 wm = (struct cmd_ctx *)mi->ctx;
629 free(Conf.wm_argv);
630 Conf.wm_argv = xstrdup(wm->path);
631 cwm_status = CWM_EXEC_WM;
632 }
633
634 menuq_clear(&menuq);
635 }
636
637 void
638 kbfunc_menu_exec(void *ctx, struct cargs *cargs)
639 {
640 #define NPATHS 256
641 struct screen_ctx *sc = ctx;
642 char **ap, *paths[NPATHS], *path, *pathcpy;
643 char tpath[PATH_MAX];
644 struct stat sb;
645 DIR *dirp;
646 struct dirent *dp;
647 struct menu *mi;
648 struct menu_q menuq;
649 int l, i;
650 int mflags = (CWM_MENU_DUMMY | CWM_MENU_FILE);
651
652 TAILQ_INIT(&menuq);
653
654 if ((path = getenv("PATH")) == NULL)
655 path = _PATH_DEFPATH;
656 pathcpy = path = xstrdup(path);
657
658 for (ap = paths; ap < &paths[NPATHS - 1] &&
659 (*ap = strsep(&pathcpy, ":")) != NULL;) {
660 if (**ap != '\0')
661 ap++;
662 }
663 *ap = NULL;
664 for (i = 0; i < NPATHS && paths[i] != NULL; i++) {
665 if ((dirp = opendir(paths[i])) == NULL)
666 continue;
667
668 while ((dp = readdir(dirp)) != NULL) {
669 (void)memset(tpath, '\0', sizeof(tpath));
670 l = snprintf(tpath, sizeof(tpath), "%s/%s", paths[i],
671 dp->d_name);
672 if (l == -1 || l >= sizeof(tpath))
673 continue;
674 /* Skip everything but regular files and symlinks. */
675 if (dp->d_type != DT_REG && dp->d_type != DT_LNK) {
676 /* lstat(2) in case d_type isn't supported. */
677 if (lstat(tpath, &sb) == -1)
678 continue;
679 if (!S_ISREG(sb.st_mode) &&
680 !S_ISLNK(sb.st_mode))
681 continue;
682 }
683 if (access(tpath, X_OK) == 0)
684 menuq_add(&menuq, NULL, "%s", dp->d_name);
685 }
686 (void)closedir(dirp);
687 }
688 free(path);
689
690 if ((mi = menu_filter(sc, &menuq, "exec", NULL, mflags,
691 search_match_exec, search_print_text)) != NULL) {
692 if (mi->text[0] == '\0')
693 goto out;
694 u_spawn(mi->text);
695 }
696 out:
697 if (mi != NULL && mi->dummy)
698 free(mi);
699 menuq_clear(&menuq);
700 }
701
702 void
703 kbfunc_menu_ssh(void *ctx, struct cargs *cargs)
704 {
705 struct screen_ctx *sc = ctx;
706 struct cmd_ctx *cmd;
707 struct menu *mi;
708 struct menu_q menuq;
709 FILE *fp;
710 char *buf, *lbuf, *p;
711 char hostbuf[HOST_NAME_MAX+1];
712 char path[PATH_MAX];
713 int l;
714 size_t len;
715 ssize_t slen;
716 int mflags = (CWM_MENU_DUMMY);
717
718 TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
719 if (strcmp(cmd->name, "term") == 0)
720 break;
721 }
722 TAILQ_INIT(&menuq);
723
724 if ((fp = fopen(Conf.known_hosts, "r")) == NULL) {
725 warn("%s: %s", __func__, Conf.known_hosts);
726 goto menu;
727 }
728
729 lbuf = NULL;
730 len = 0;
731 while ((slen = getline(&lbuf, &len, fp)) != -1) {
732 buf = lbuf;
733 if (buf[slen - 1] == '\n')
734 buf[slen - 1] = '\0';
735
736 /* skip hashed hosts */
737 if (strncmp(buf, HASH_MARKER, strlen(HASH_MARKER)) == 0)
738 continue;
739 for (p = buf; *p != ',' && *p != ' ' && p != buf + slen; p++)
740 ;
741 /* ignore badness */
742 if (p - buf + 1 > sizeof(hostbuf))
743 continue;
744 (void)strlcpy(hostbuf, buf, p - buf + 1);
745 menuq_add(&menuq, NULL, "%s", hostbuf);
746 }
747 free(lbuf);
748 if (ferror(fp))
749 err(1, "%s", path);
750 (void)fclose(fp);
751 menu:
752 if ((mi = menu_filter(sc, &menuq, "ssh", NULL, mflags,
753 search_match_text, search_print_text)) != NULL) {
754 if (mi->text[0] == '\0')
755 goto out;
756 l = snprintf(path, sizeof(path), "%s -T '[ssh] %s' -e ssh %s",
757 cmd->path, mi->text, mi->text);
758 if (l == -1 || l >= sizeof(path))
759 goto out;
760 u_spawn(path);
761 }
762 out:
763 if (mi != NULL && mi->dummy)
764 free(mi);
765 menuq_clear(&menuq);
766 }
767
768 void
769 kbfunc_client_menu_label(void *ctx, struct cargs *cargs)
770 {
771 struct client_ctx *cc = ctx;
772 struct menu *mi;
773 struct menu_q menuq;
774 int mflags = (CWM_MENU_DUMMY);
775
776 TAILQ_INIT(&menuq);
777
778 /* dummy is set, so this will always return */
779 mi = menu_filter(cc->sc, &menuq, "label", cc->label, mflags,
780 search_match_text, search_print_text);
781
782 if (!mi->abort) {
783 free(cc->label);
784 cc->label = xstrdup(mi->text);
785 }
786 free(mi);
787 }
788
789 void
790 kbfunc_exec_cmd(void *ctx, struct cargs *cargs)
791 {
792 u_spawn(cargs->cmd);
793 }
794
795 void
796 kbfunc_exec_term(void *ctx, struct cargs *cargs)
797 {
798 struct cmd_ctx *cmd;
799
800 TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
801 if (strcmp(cmd->name, "term") == 0)
802 u_spawn(cmd->path);
803 }
804 }
805
806 void
807 kbfunc_exec_lock(void *ctx, struct cargs *cargs)
808 {
809 struct cmd_ctx *cmd;
810
811 TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
812 if (strcmp(cmd->name, "lock") == 0)
813 u_spawn(cmd->path);
814 }
815 }
816