Use for instead of while loop
[dvtm.git] / dvtm.c
blobab82d5d9535bcf40a1bf11d78c5f797164f597a1
1 /*
2 * The initial "port" of dwm to curses was done by
4 * © 2007-2014 Marc André Tanner <mat at brain-dump dot org>
6 * It is highly inspired by the original X11 dwm and
7 * reuses some code of it which is mostly
9 * © 2006-2007 Anselm R. Garbe <garbeam at gmail dot com>
11 * See LICENSE for details.
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <stdint.h>
16 #include <wchar.h>
17 #include <sys/select.h>
18 #include <sys/stat.h>
19 #include <sys/ioctl.h>
20 #include <sys/wait.h>
21 #include <sys/time.h>
22 #include <sys/types.h>
23 #include <fcntl.h>
24 #include <curses.h>
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <signal.h>
28 #include <locale.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <stdbool.h>
32 #include <errno.h>
33 #include <pwd.h>
34 #if defined __CYGWIN__ || defined __sun
35 # include <termios.h>
36 #endif
37 #include "vt.h"
39 #ifdef PDCURSES
40 int ESCDELAY;
41 #endif
43 #ifndef NCURSES_REENTRANT
44 # define set_escdelay(d) (ESCDELAY = (d))
45 #endif
47 typedef struct {
48 float mfact;
49 int history;
50 int w;
51 int h;
52 volatile sig_atomic_t need_resize;
53 } Screen;
55 typedef struct {
56 const char *symbol;
57 void (*arrange)(void);
58 } Layout;
60 typedef struct Client Client;
61 struct Client {
62 WINDOW *window;
63 Vt *term;
64 Vt *editor, *app;
65 int editor_fds[2];
66 volatile sig_atomic_t editor_died;
67 const char *cmd;
68 char title[255];
69 int order;
70 pid_t pid;
71 unsigned short int id;
72 unsigned short int x;
73 unsigned short int y;
74 unsigned short int w;
75 unsigned short int h;
76 bool has_title_line;
77 bool minimized;
78 volatile sig_atomic_t died;
79 Client *next;
80 Client *prev;
81 Client *snext;
82 bool tags[1];
85 typedef struct {
86 short fg;
87 short bg;
88 short fg256;
89 short bg256;
90 short pair;
91 } Color;
93 typedef struct {
94 const char *title;
95 attr_t attrs;
96 Color *color;
97 } ColorRule;
99 #define ALT(k) ((k) + (161 - 'a'))
100 #if defined CTRL && defined _AIX
101 #undef CTRL
102 #endif
103 #ifndef CTRL
104 #define CTRL(k) ((k) & 0x1F)
105 #endif
106 #define CTRL_ALT(k) ((k) + (129 - 'a'))
108 #define MAX_ARGS 3
110 typedef struct {
111 void (*cmd)(const char *args[]);
112 const char *args[MAX_ARGS];
113 } Action;
115 #define MAX_KEYS 3
117 typedef unsigned int KeyCombo[MAX_KEYS];
119 typedef struct {
120 KeyCombo keys;
121 Action action;
122 } KeyBinding;
124 typedef struct {
125 mmask_t mask;
126 Action action;
127 } Button;
129 typedef struct {
130 const char *name;
131 Action action;
132 } Cmd;
134 enum { BAR_TOP, BAR_BOTTOM, BAR_OFF };
135 enum { ALIGN_LEFT, ALIGN_RIGHT };
137 typedef struct {
138 int fd;
139 int pos;
140 bool autohide;
141 unsigned short int h;
142 unsigned short int y;
143 char text[512];
144 const char *file;
145 } StatusBar;
147 typedef struct {
148 int fd;
149 const char *file;
150 unsigned short int id;
151 } CmdFifo;
153 typedef struct {
154 char *data;
155 size_t len;
156 size_t size;
157 } Register;
159 typedef struct {
160 char *name;
161 const char *argv[4];
162 bool filter;
163 } Editor;
165 #define countof(arr) (sizeof(arr) / sizeof((arr)[0]))
166 #define sstrlen(str) (sizeof(str) - 1)
167 #define max(x, y) ((x) > (y) ? (x) : (y))
169 #ifdef NDEBUG
170 #define debug(format, args...)
171 #else
172 #define debug eprint
173 #endif
175 /* commands for use by keybindings */
176 static void create(const char *args[]);
177 static void copymode(const char *args[]);
178 static void focusn(const char *args[]);
179 static void focusnext(const char *args[]);
180 static void focusnextnm(const char *args[]);
181 static void focusprev(const char *args[]);
182 static void focusprevnm(const char *args[]);
183 static void focuslast(const char *args[]);
184 static void killclient(const char *args[]);
185 static void paste(const char *args[]);
186 static void quit(const char *args[]);
187 static void redraw(const char *args[]);
188 static void scrollback(const char *args[]);
189 static void send(const char *args[]);
190 static void setlayout(const char *args[]);
191 static void setmfact(const char *args[]);
192 static void startup(const char *args[]);
193 static void tag(const char *args[]);
194 static void togglebar(const char *args[]);
195 static void togglebell(const char *key[]);
196 static void toggleminimize(const char *args[]);
197 static void togglemouse(const char *args[]);
198 static void togglerunall(const char *args[]);
199 static void toggletag(const char *args[]);
200 static void toggleview(const char *args[]);
201 static void viewprevtag(const char *args[]);
202 static void view(const char *args[]);
203 static void zoom(const char *args[]);
205 /* commands for use by mouse bindings */
206 static void mouse_focus(const char *args[]);
207 static void mouse_fullscreen(const char *args[]);
208 static void mouse_minimize(const char *args[]);
209 static void mouse_zoom(const char *args[]);
211 /* functions and variables available to layouts via config.h */
212 static Client* nextvisible(Client *c);
213 static void focus(Client *c);
214 static void resize(Client *c, int x, int y, int w, int h);
215 extern Screen screen;
216 static unsigned int waw, wah, wax, way;
217 static Client *clients = NULL;
218 static char *title;
220 #include "config.h"
222 /* global variables */
223 static const char *dvtm_name = "dvtm";
224 Screen screen = { .mfact = MFACT, .history = SCROLL_HISTORY };
225 static Client *stack = NULL;
226 static Client *sel = NULL;
227 static Client *lastsel = NULL;
228 static Client *msel = NULL;
229 static bool seltags[countof(tags)] = {[0] = true};
230 static bool prevtags[countof(tags)];
231 static bool mouse_events_enabled = ENABLE_MOUSE;
232 static Layout *layout = layouts;
233 static StatusBar bar = { .fd = -1, .pos = BAR_POS, .autohide = BAR_AUTOHIDE, .h = 1 };
234 static CmdFifo cmdfifo = { .fd = -1 };
235 static const char *shell;
236 static Register copyreg;
237 static volatile sig_atomic_t running = true;
238 static bool runinall = false;
240 static void
241 eprint(const char *errstr, ...) {
242 va_list ap;
243 va_start(ap, errstr);
244 vfprintf(stderr, errstr, ap);
245 va_end(ap);
248 static void
249 error(const char *errstr, ...) {
250 va_list ap;
251 va_start(ap, errstr);
252 vfprintf(stderr, errstr, ap);
253 va_end(ap);
254 exit(EXIT_FAILURE);
257 static bool
258 isarrange(void (*func)()) {
259 return func == layout->arrange;
262 static bool
263 isvisible(Client *c) {
264 for (unsigned int i = 0; i < countof(tags); i++)
265 if (c->tags[i] && seltags[i])
266 return true;
267 return false;
270 static bool
271 is_content_visible(Client *c) {
272 if (!c)
273 return false;
274 if (isarrange(fullscreen))
275 return sel == c;
276 return isvisible(c) && !c->minimized;
279 static Client*
280 nextvisible(Client *c) {
281 for (; c && !isvisible(c); c = c->next);
282 return c;
285 static Client*
286 nextbytag(Client *c, int tag) {
287 for (; c && !c->tags[tag]; c = c->next);
288 return c;
291 bool
292 isoccupied(unsigned int t) {
293 for (Client *c = clients; c; c = c->next)
294 if (c->tags[t])
295 return true;
296 return false;
299 static void
300 reorder(int tag) {
301 Client *c;
302 uint8_t order = 0;
303 if (tag < 0)
304 for (c = nextvisible(clients); c; c = nextvisible(c->next))
305 c->order = ++order;
306 else
307 for (c = nextbytag(clients, tag); c; c = nextbytag(c->next, tag))
308 c->order = ++order;
311 static void
312 updatebarpos(void) {
313 bar.y = 0;
314 wax = 0;
315 way = 0;
316 wah = screen.h;
317 waw = screen.w;
318 if (bar.pos == BAR_TOP) {
319 wah -= bar.h;
320 way += bar.h;
321 } else if (bar.pos == BAR_BOTTOM) {
322 wah -= bar.h;
323 bar.y = wah;
327 static void
328 drawbar(void) {
329 int sx, sy, x = 0;
330 if (bar.pos == BAR_OFF)
331 return;
332 getyx(stdscr, sy, sx);
333 attrset(BAR_ATTR);
334 move(bar.y, 0);
335 for (unsigned int i = 0; i < countof(tags); i++){
336 if (seltags[i])
337 attrset(TAG_SEL);
338 else if (isoccupied(i))
339 attrset(TAG_OCCUPIED);
340 else
341 attrset(TAG_NORMAL);
342 printw(TAG_SYMBOL, tags[i]);
343 /* -2 because we assume %s is contained in TAG_SYMBOL */
344 x += sstrlen(TAG_SYMBOL) - 2 + strlen(tags[i]);
346 attrset(TAG_NORMAL);
347 addch('[');
348 attrset(BAR_ATTR);
350 wchar_t wbuf[sizeof bar.text];
351 int w, maxwidth = screen.w - x - 2;
353 if (mbstowcs(wbuf, bar.text, sizeof bar.text) == (size_t)-1)
354 return;
355 if ((w = wcswidth(wbuf, maxwidth)) == -1)
356 return;
357 if (BAR_ALIGN == ALIGN_RIGHT) {
358 for (int i = 0; i + w < maxwidth; i++)
359 addch(' ');
361 addstr(bar.text);
362 if (BAR_ALIGN == ALIGN_LEFT) {
363 for (; w < maxwidth; w++)
364 addch(' ');
366 attrset(TAG_NORMAL);
367 mvaddch(bar.y, screen.w - 1, ']');
368 attrset(NORMAL_ATTR);
369 move(sy, sx);
370 wnoutrefresh(stdscr);
373 static int
374 show_border(void) {
375 return (bar.pos != BAR_OFF) || (clients && clients->next);
378 static void
379 draw_border(Client *c) {
380 if (!show_border())
381 return;
382 char t = '\0';
383 int x, y, maxlen;
385 wattrset(c->window, (sel == c || (runinall && !c->minimized)) ? SELECTED_ATTR : NORMAL_ATTR);
386 getyx(c->window, y, x);
387 mvwhline(c->window, 0, 0, ACS_HLINE, c->w);
388 maxlen = c->w - (2 + sstrlen(TITLE) - sstrlen("%s%sd") + sstrlen(SEPARATOR) + 2);
389 if (maxlen < 0)
390 maxlen = 0;
391 if ((size_t)maxlen < sizeof(c->title)) {
392 t = c->title[maxlen];
393 c->title[maxlen] = '\0';
396 mvwprintw(c->window, 0, 2, TITLE,
397 *c->title ? c->title : "",
398 *c->title ? SEPARATOR : "",
399 c->order);
400 if (t)
401 c->title[maxlen] = t;
402 wmove(c->window, y, x);
405 static void
406 draw_content(Client *c) {
407 vt_draw(c->term, c->window, c->has_title_line, 0);
410 static void
411 draw(Client *c) {
412 if (is_content_visible(c)) {
413 redrawwin(c->window);
414 draw_content(c);
416 if (!isarrange(fullscreen) || sel == c)
417 draw_border(c);
418 wnoutrefresh(c->window);
421 static void
422 draw_all(void) {
423 if (!nextvisible(clients)) {
424 sel = NULL;
425 curs_set(0);
426 erase();
427 drawbar();
428 doupdate();
429 return;
432 if (!isarrange(fullscreen)) {
433 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
434 if (c == sel)
435 continue;
436 draw(c);
439 /* as a last step the selected window is redrawn,
440 * this has the effect that the cursor position is
441 * accurate
443 if (sel)
444 draw(sel);
447 static void
448 arrange(void) {
449 int m = 0;
450 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next))
451 if (c->minimized)
452 m++;
453 erase();
454 attrset(NORMAL_ATTR);
455 if (bar.fd == -1 && bar.autohide) {
456 int n = 0;
457 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next), n++);
458 if ((!clients || !clients->next) && n == 1)
459 bar.pos = BAR_OFF;
460 else
461 bar.pos = BAR_POS;
462 updatebarpos();
464 if (m && !isarrange(fullscreen))
465 wah--;
466 layout->arrange();
467 if (m && !isarrange(fullscreen)) {
468 int nw = waw / m, nx = wax;
469 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
470 if (c->minimized) {
471 resize(c, nx, way+wah, nw, 1);
472 nx += nw;
475 wah++;
477 reorder(-1);
478 focus(NULL);
479 wnoutrefresh(stdscr);
480 drawbar();
481 draw_all();
484 static void
485 attach(Client *c) {
486 if (clients)
487 clients->prev = c;
488 c->next = clients;
489 c->prev = NULL;
490 clients = c;
491 for (int o = 1; c; c = nextvisible(c->next), o++)
492 c->order = o;
495 static void
496 attachafter(Client *c, Client *a) { /* attach c after a */
497 if (c == a)
498 return;
499 if (!a)
500 for (a = clients; a && a->next; a = a->next);
502 if (a) {
503 if (a->next)
504 a->next->prev = c;
505 c->next = a->next;
506 c->prev = a;
507 a->next = c;
508 for (int o = a->order; c; c = nextvisible(c->next))
509 c->order = ++o;
513 static void
514 attachstack(Client *c) {
515 c->snext = stack;
516 stack = c;
519 static void
520 detach(Client *c) {
521 Client *d;
522 if (c->prev)
523 c->prev->next = c->next;
524 if (c->next) {
525 c->next->prev = c->prev;
526 for (d = nextvisible(c->next); d; d = nextvisible(d->next))
527 --d->order;
529 if (c == clients)
530 clients = c->next;
531 c->next = c->prev = NULL;
534 static void
535 settitle(Client *c) {
536 char *term, *t = title;
537 if (!t && sel == c && *c->title)
538 t = c->title;
539 if (t && (term = getenv("TERM")) && !strstr(term, "linux"))
540 printf("\033]0;%s\007", t);
543 static void
544 detachstack(Client *c) {
545 Client **tc;
546 for (tc=&stack; *tc && *tc != c; tc=&(*tc)->snext);
547 *tc = c->snext;
550 static void
551 focus(Client *c) {
552 Client *tmp = sel;
553 if (!c)
554 for (c = stack; c && !isvisible(c); c = c->snext);
555 if (sel == c)
556 return;
557 lastsel = sel;
558 sel = c;
559 if (tmp && !isarrange(fullscreen)) {
560 draw_border(tmp);
561 wnoutrefresh(tmp->window);
564 if (c) {
565 detachstack(c);
566 attachstack(c);
567 settitle(c);
568 if (isarrange(fullscreen)) {
569 draw(c);
570 } else {
571 draw_border(c);
572 wnoutrefresh(c->window);
575 curs_set(c && !c->minimized && vt_cursor_visible(c->term));
578 static void
579 applycolorrules(Client *c) {
580 const ColorRule *r = colorrules;
581 short fg = r->color->fg, bg = r->color->bg;
582 attr_t attrs = r->attrs;
584 for (unsigned int i = 1; i < countof(colorrules); i++) {
585 r = &colorrules[i];
586 if (strstr(c->title, r->title)) {
587 attrs = r->attrs;
588 fg = r->color->fg;
589 bg = r->color->bg;
590 break;
594 vt_default_colors_set(c->term, attrs, fg, bg);
597 static void
598 term_title_handler(Vt *term, const char *title) {
599 Client *c = (Client *)vt_data_get(term);
600 if (title)
601 strncpy(c->title, title, sizeof(c->title) - 1);
602 c->title[title ? sizeof(c->title) - 1 : 0] = '\0';
603 settitle(c);
604 if (!isarrange(fullscreen) || sel == c)
605 draw_border(c);
606 applycolorrules(c);
609 static void
610 move_client(Client *c, int x, int y) {
611 if (c->x == x && c->y == y)
612 return;
613 debug("moving, x: %d y: %d\n", x, y);
614 if (mvwin(c->window, y, x) == ERR) {
615 eprint("error moving, x: %d y: %d\n", x, y);
616 } else {
617 c->x = x;
618 c->y = y;
622 static void
623 resize_client(Client *c, int w, int h) {
624 bool has_title_line = show_border();
625 bool resize_window = c->w != w || c->h != h;
626 if (resize_window) {
627 debug("resizing, w: %d h: %d\n", w, h);
628 if (wresize(c->window, h, w) == ERR) {
629 eprint("error resizing, w: %d h: %d\n", w, h);
630 } else {
631 c->w = w;
632 c->h = h;
635 if (resize_window || c->has_title_line != has_title_line) {
636 c->has_title_line = has_title_line;
637 vt_resize(c->app, h - has_title_line, w);
638 if (c->editor)
639 vt_resize(c->editor, h - has_title_line, w);
643 static void
644 resize(Client *c, int x, int y, int w, int h) {
645 resize_client(c, w, h);
646 move_client(c, x, y);
649 static Client*
650 get_client_by_coord(unsigned int x, unsigned int y) {
651 if (y < way || y >= wah)
652 return NULL;
653 if (isarrange(fullscreen))
654 return sel;
655 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
656 if (x >= c->x && x < c->x + c->w && y >= c->y && y < c->y + c->h) {
657 debug("mouse event, x: %d y: %d client: %d\n", x, y, c->order);
658 return c;
661 return NULL;
664 static void
665 sigchld_handler(int sig) {
666 int errsv = errno;
667 int status;
668 pid_t pid;
670 while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
671 if (pid == -1) {
672 if (errno == ECHILD) {
673 /* no more child processes */
674 break;
676 eprint("waitpid: %s\n", strerror(errno));
677 break;
680 debug("child with pid %d died\n", pid);
682 for (Client *c = clients; c; c = c->next) {
683 if (c->pid == pid) {
684 c->died = true;
685 break;
687 if (c->editor && vt_pid_get(c->editor) == pid) {
688 c->editor_died = true;
689 break;
694 errno = errsv;
697 static void
698 sigwinch_handler(int sig) {
699 screen.need_resize = true;
702 static void
703 sigterm_handler(int sig) {
704 running = false;
707 static void
708 resize_screen(void) {
709 struct winsize ws;
711 if (ioctl(0, TIOCGWINSZ, &ws) == -1) {
712 getmaxyx(stdscr, screen.h, screen.w);
713 } else {
714 screen.w = ws.ws_col;
715 screen.h = ws.ws_row;
718 debug("resize_screen(), w: %d h: %d\n", screen.w, screen.h);
720 resizeterm(screen.h, screen.w);
721 wresize(stdscr, screen.h, screen.w);
722 updatebarpos();
723 clear();
724 arrange();
727 static KeyBinding*
728 keybinding(KeyCombo keys) {
729 unsigned int keycount = 0;
730 while (keycount < MAX_KEYS && keys[keycount])
731 keycount++;
732 for (unsigned int b = 0; b < countof(bindings); b++) {
733 for (unsigned int k = 0; k < keycount; k++) {
734 if (keys[k] != bindings[b].keys[k])
735 break;
736 if (k == keycount - 1)
737 return &bindings[b];
740 return NULL;
743 static unsigned int
744 idxoftag(const char *tag) {
745 unsigned int i;
746 for (i = 0; (i < countof(tags)) && (tags[i] != tag); i++);
747 return (i < countof(tags)) ? i : 0;
750 static void
751 tagschanged() {
752 bool nm = false;
753 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
754 if (!c->minimized) {
755 nm = true;
756 break;
759 if (!nm && nextvisible(clients)) {
760 focus(NULL);
761 toggleminimize(NULL);
762 } else {
763 arrange();
767 static void
768 tag(const char *args[]) {
769 unsigned int i;
771 if (!sel)
772 return;
773 for (i = 0; i < countof(tags); i++)
774 sel->tags[i] = (NULL == args[0]);
775 i = idxoftag(args[0]);
776 sel->tags[i] = true;
777 reorder(i);
778 tagschanged();
781 static void
782 toggletag(const char *args[]) {
783 unsigned int i, j;
785 if (!sel)
786 return;
787 i = idxoftag(args[0]);
788 sel->tags[i] = !sel->tags[i];
789 for (j = 0; j < countof(tags) && !sel->tags[j]; j++);
790 if (j == countof(tags))
791 sel->tags[i] = true; /* at least one tag must be enabled */
792 tagschanged();
795 static void
796 toggleview(const char *args[]) {
797 unsigned int i, j;
799 i = idxoftag(args[0]);
800 seltags[i] = !seltags[i];
801 for (j = 0; j < countof(tags) && !seltags[j]; j++);
802 if (j == countof(tags))
803 seltags[i] = true; /* at least one tag must be viewed */
804 tagschanged();
807 static void
808 view(const char *args[]) {
809 memcpy(prevtags, seltags, sizeof seltags);
810 for (unsigned int i = 0; i < countof(tags); i++)
811 seltags[i] = (NULL == args[0]);
812 seltags[idxoftag(args[0])] = true;
813 tagschanged();
816 static void
817 viewprevtag(const char *args[]) {
818 static bool tmp[countof(tags)];
819 memcpy(tmp, seltags, sizeof seltags);
820 memcpy(seltags, prevtags, sizeof seltags);
821 memcpy(prevtags, tmp, sizeof seltags);
822 tagschanged();
825 static void
826 keypress(int code) {
827 unsigned int len = 1;
828 char buf[8] = { '\e' };
830 if (code == '\e') {
831 /* pass characters following escape to the underlying app */
832 nodelay(stdscr, TRUE);
833 for (int t; len < sizeof(buf) && (t = getch()) != ERR; len++)
834 buf[len] = t;
835 nodelay(stdscr, FALSE);
838 for (Client *c = runinall ? nextvisible(clients) : sel; c; c = nextvisible(c->next)) {
839 if (is_content_visible(c)) {
840 if (code == '\e')
841 vt_write(c->term, buf, len);
842 else
843 vt_keypress(c->term, code);
845 if (!runinall)
846 break;
850 static void
851 mouse_setup(void) {
852 #ifdef CONFIG_MOUSE
853 mmask_t mask = 0;
855 if (mouse_events_enabled) {
856 mask = BUTTON1_CLICKED | BUTTON2_CLICKED;
857 for (unsigned int i = 0; i < countof(buttons); i++)
858 mask |= buttons[i].mask;
860 mousemask(mask, NULL);
861 #endif /* CONFIG_MOUSE */
864 static bool
865 checkshell(const char *shell) {
866 if (shell == NULL || *shell == '\0' || *shell != '/')
867 return false;
868 if (!strcmp(strrchr(shell, '/')+1, dvtm_name))
869 return false;
870 if (access(shell, X_OK))
871 return false;
872 return true;
875 static const char *
876 getshell(void) {
877 const char *shell = getenv("SHELL");
878 struct passwd *pw;
880 if (checkshell(shell))
881 return shell;
882 if ((pw = getpwuid(getuid())) && checkshell(pw->pw_shell))
883 return pw->pw_shell;
884 return "/bin/sh";
887 static void
888 setup(void) {
889 shell = getshell();
890 setlocale(LC_CTYPE, "");
891 initscr();
892 start_color();
893 noecho();
894 keypad(stdscr, TRUE);
895 mouse_setup();
896 raw();
897 vt_init();
898 vt_keytable_set(keytable, countof(keytable));
899 for (unsigned int i = 0; i < countof(colors); i++) {
900 if (COLORS == 256) {
901 if (colors[i].fg256)
902 colors[i].fg = colors[i].fg256;
903 if (colors[i].bg256)
904 colors[i].bg = colors[i].bg256;
906 colors[i].pair = vt_color_reserve(colors[i].fg, colors[i].bg);
908 resize_screen();
909 struct sigaction sa;
910 sa.sa_flags = 0;
911 sigemptyset(&sa.sa_mask);
912 sa.sa_handler = sigwinch_handler;
913 sigaction(SIGWINCH, &sa, NULL);
914 sa.sa_handler = sigchld_handler;
915 sigaction(SIGCHLD, &sa, NULL);
916 sa.sa_handler = sigterm_handler;
917 sigaction(SIGTERM, &sa, NULL);
918 sa.sa_handler = SIG_IGN;
919 sigaction(SIGPIPE, &sa, NULL);
922 static void
923 destroy(Client *c) {
924 Client *t;
925 if (sel == c)
926 focusnextnm(NULL);
927 detach(c);
928 detachstack(c);
929 if (sel == c) {
930 if ((t = nextvisible(clients))) {
931 focus(t);
932 toggleminimize(NULL);
933 } else {
934 sel = NULL;
937 if (lastsel == c)
938 lastsel = NULL;
939 werase(c->window);
940 wnoutrefresh(c->window);
941 vt_destroy(c->term);
942 delwin(c->window);
943 if (!clients && countof(actions)) {
944 if (!strcmp(c->cmd, shell))
945 quit(NULL);
946 else
947 create(NULL);
949 free(c);
950 arrange();
953 static void
954 cleanup(void) {
955 while (clients)
956 destroy(clients);
957 vt_shutdown();
958 endwin();
959 free(copyreg.data);
960 if (bar.fd > 0)
961 close(bar.fd);
962 if (bar.file)
963 unlink(bar.file);
964 if (cmdfifo.fd > 0)
965 close(cmdfifo.fd);
966 if (cmdfifo.file)
967 unlink(cmdfifo.file);
970 static char *getcwd_by_pid(Client *c) {
971 if (!c)
972 return NULL;
973 char buf[32];
974 snprintf(buf, sizeof buf, "/proc/%d/cwd", c->pid);
975 return realpath(buf, NULL);
978 /* commands for use by keybindings */
979 static void
980 create(const char *args[]) {
981 Client *c = calloc(1, sizeof(Client) + sizeof(seltags));
982 if (!c)
983 return;
984 memcpy(c->tags, seltags, sizeof seltags);
985 const char *cmd = (args && args[0]) ? args[0] : shell;
986 const char *pargs[] = { "/bin/sh", "-c", cmd, NULL };
987 c->id = ++cmdfifo.id;
988 char buf[8], *cwd = NULL;
989 snprintf(buf, sizeof buf, "%d", c->id);
990 const char *env[] = {
991 "DVTM_WINDOW_ID", buf,
992 NULL
995 if (!(c->window = newwin(wah, waw, way, wax))) {
996 free(c);
997 return;
1000 c->term = c->app = vt_create(screen.h, screen.w, screen.history);
1001 if (!c->term) {
1002 delwin(c->window);
1003 free(c);
1004 return;
1007 c->cmd = cmd;
1008 if (args && args[1]) {
1009 strncpy(c->title, args[1], sizeof(c->title) - 1);
1010 c->title[sizeof(c->title) - 1] = '\0';
1012 if (args && args[2])
1013 cwd = !strcmp(args[2], "$CWD") ? getcwd_by_pid(sel) : (char*)args[2];
1014 c->pid = vt_forkpty(c->term, "/bin/sh", pargs, cwd, env, NULL, NULL);
1015 if (args && args[2] && !strcmp(args[2], "$CWD"))
1016 free(cwd);
1017 vt_data_set(c->term, c);
1018 vt_title_handler_set(c->term, term_title_handler);
1019 c->x = wax;
1020 c->y = way;
1021 debug("client with pid %d forked\n", c->pid);
1022 attach(c);
1023 focus(c);
1024 arrange();
1027 static void
1028 copymode(const char *args[]) {
1029 if (!sel || sel->editor)
1030 return;
1031 if (!(sel->editor = vt_create(sel->h - sel->has_title_line, sel->w, 0)))
1032 return;
1034 char *ed = getenv("DVTM_EDITOR");
1035 int *to = &sel->editor_fds[0], *from = NULL;
1036 sel->editor_fds[0] = sel->editor_fds[1] = -1;
1038 if (!ed)
1039 ed = getenv("EDITOR");
1040 if (!ed)
1041 ed = getenv("PAGER");
1042 if (!ed)
1043 ed = editors[0].name;
1045 const char **argv = (const char*[]){ ed, "-", NULL };
1047 for (unsigned int i = 0; i < countof(editors); i++) {
1048 if (!strcmp(editors[i].name, ed)) {
1049 argv = (const char*[]){ ed, NULL, NULL, NULL, NULL, NULL, NULL };
1050 for (int j = 1; editors[i].argv[j]; j++) {
1051 if (strstr(editors[i].argv[j], "%d")) {
1052 char argline[32];
1053 int line = vt_content_start(sel->app);
1054 snprintf(argline, sizeof(argline), "+%d", line);
1055 argv[j] = argline;
1056 } else {
1057 argv[j] = editors[i].argv[j];
1060 if (editors[i].filter)
1061 from = &sel->editor_fds[1];
1062 break;
1066 if (vt_forkpty(sel->editor, ed, argv, NULL, NULL, to, from) < 0) {
1067 vt_destroy(sel->editor);
1068 sel->editor = NULL;
1069 return;
1072 sel->term = sel->editor;
1074 if (sel->editor_fds[0] != -1) {
1075 char *buf = NULL;
1076 size_t len = vt_content_get(sel->app, &buf);
1077 char *cur = buf;
1078 while (len > 0) {
1079 ssize_t res = write(sel->editor_fds[0], cur, len);
1080 if (res < 0) {
1081 if (errno == EAGAIN || errno == EINTR)
1082 continue;
1083 break;
1085 cur += res;
1086 len -= res;
1088 free(buf);
1089 close(sel->editor_fds[0]);
1090 sel->editor_fds[0] = -1;
1093 if (args[0])
1094 vt_write(sel->editor, args[0], strlen(args[0]));
1097 static void
1098 focusn(const char *args[]) {
1099 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
1100 if (c->order == atoi(args[0])) {
1101 focus(c);
1102 if (c->minimized)
1103 toggleminimize(NULL);
1104 return;
1109 static void
1110 focusnext(const char *args[]) {
1111 Client *c;
1112 if (!sel)
1113 return;
1114 for (c = sel->next; c && !isvisible(c); c = c->next);
1115 if (!c)
1116 for (c = clients; c && !isvisible(c); c = c->next);
1117 if (c)
1118 focus(c);
1121 static void
1122 focusnextnm(const char *args[]) {
1123 if (!sel)
1124 return;
1125 Client *c = sel;
1126 do {
1127 c = nextvisible(c->next);
1128 if (!c)
1129 c = nextvisible(clients);
1130 } while (c->minimized && c != sel);
1131 focus(c);
1134 static void
1135 focusprev(const char *args[]) {
1136 Client *c;
1137 if (!sel)
1138 return;
1139 for (c = sel->prev; c && !isvisible(c); c = c->prev);
1140 if (!c) {
1141 for (c = clients; c && c->next; c = c->next);
1142 for (; c && !isvisible(c); c = c->prev);
1144 if (c)
1145 focus(c);
1148 static void
1149 focusprevnm(const char *args[]) {
1150 if (!sel)
1151 return;
1152 Client *c = sel;
1153 do {
1154 for (c = c->prev; c && !isvisible(c); c = c->prev);
1155 if (!c) {
1156 for (c = clients; c && c->next; c = c->next);
1157 for (; c && !isvisible(c); c = c->prev);
1159 } while (c && c != sel && c->minimized);
1160 focus(c);
1163 static void
1164 focuslast(const char *args[]) {
1165 if (lastsel)
1166 focus(lastsel);
1169 static void
1170 killclient(const char *args[]) {
1171 if (!sel)
1172 return;
1173 debug("killing client with pid: %d\n", sel->pid);
1174 kill(-sel->pid, SIGKILL);
1177 static void
1178 paste(const char *args[]) {
1179 if (sel && copyreg.data)
1180 vt_write(sel->term, copyreg.data, copyreg.len);
1183 static void
1184 quit(const char *args[]) {
1185 cleanup();
1186 exit(EXIT_SUCCESS);
1189 static void
1190 redraw(const char *args[]) {
1191 for (Client *c = clients; c; c = c->next) {
1192 if (!c->minimized) {
1193 vt_dirty(c->term);
1194 wclear(c->window);
1195 wnoutrefresh(c->window);
1198 resize_screen();
1201 static void
1202 scrollback(const char *args[]) {
1203 if (!is_content_visible(sel))
1204 return;
1206 if (!args[0] || atoi(args[0]) < 0)
1207 vt_scroll(sel->term, -sel->h/2);
1208 else
1209 vt_scroll(sel->term, sel->h/2);
1211 draw(sel);
1212 curs_set(vt_cursor_visible(sel->term));
1215 static void
1216 send(const char *args[]) {
1217 if (sel && args && args[0])
1218 vt_write(sel->term, args[0], strlen(args[0]));
1221 static void
1222 setlayout(const char *args[]) {
1223 unsigned int i;
1225 if (!args || !args[0]) {
1226 if (++layout == &layouts[countof(layouts)])
1227 layout = &layouts[0];
1228 } else {
1229 for (i = 0; i < countof(layouts); i++)
1230 if (!strcmp(args[0], layouts[i].symbol))
1231 break;
1232 if (i == countof(layouts))
1233 return;
1234 layout = &layouts[i];
1236 arrange();
1239 static void
1240 setmfact(const char *args[]) {
1241 float delta;
1243 if (isarrange(fullscreen) || isarrange(grid))
1244 return;
1245 /* arg handling, manipulate mfact */
1246 if (args[0] == NULL) {
1247 screen.mfact = MFACT;
1248 } else if (1 == sscanf(args[0], "%f", &delta)) {
1249 if (args[0][0] == '+' || args[0][0] == '-')
1250 screen.mfact += delta;
1251 else
1252 screen.mfact = delta;
1253 if (screen.mfact < 0.1)
1254 screen.mfact = 0.1;
1255 else if (screen.mfact > 0.9)
1256 screen.mfact = 0.9;
1258 arrange();
1261 static void
1262 startup(const char *args[]) {
1263 for (unsigned int i = 0; i < countof(actions); i++)
1264 actions[i].cmd(actions[i].args);
1267 static void
1268 togglebar(const char *args[]) {
1269 if (bar.pos == BAR_OFF)
1270 bar.pos = (BAR_POS == BAR_OFF) ? BAR_TOP : BAR_POS;
1271 else
1272 bar.pos = BAR_OFF;
1273 bar.autohide = false;
1274 updatebarpos();
1275 redraw(NULL);
1278 static void
1279 togglebell(const char *args[]) {
1280 vt_togglebell(sel->term);
1283 static void
1284 toggleminimize(const char *args[]) {
1285 Client *c, *m, *t;
1286 unsigned int n;
1287 if (!sel)
1288 return;
1289 /* the last window can't be minimized */
1290 if (!sel->minimized) {
1291 for (n = 0, c = nextvisible(clients); c; c = nextvisible(c->next))
1292 if (!c->minimized)
1293 n++;
1294 if (n == 1)
1295 return;
1297 sel->minimized = !sel->minimized;
1298 m = sel;
1299 /* check whether the master client was minimized */
1300 if (sel == nextvisible(clients) && sel->minimized) {
1301 c = nextvisible(sel->next);
1302 detach(c);
1303 attach(c);
1304 focus(c);
1305 detach(m);
1306 for (; c && (t = nextvisible(c->next)) && !t->minimized; c = t);
1307 attachafter(m, c);
1308 } else if (m->minimized) {
1309 /* non master window got minimized move it above all other
1310 * minimized ones */
1311 focusnextnm(NULL);
1312 detach(m);
1313 for (c = nextvisible(clients); c && (t = nextvisible(c->next)) && !t->minimized; c = t);
1314 attachafter(m, c);
1315 } else { /* window is no longer minimized, move it to the master area */
1316 vt_dirty(m->term);
1317 detach(m);
1318 attach(m);
1320 arrange();
1323 static void
1324 togglemouse(const char *args[]) {
1325 mouse_events_enabled = !mouse_events_enabled;
1326 mouse_setup();
1329 static void
1330 togglerunall(const char *args[]) {
1331 runinall = !runinall;
1332 draw_all();
1335 static void
1336 zoom(const char *args[]) {
1337 Client *c;
1339 if (!sel)
1340 return;
1341 if (args && args[0])
1342 focusn(args);
1343 if ((c = sel) == nextvisible(clients))
1344 if (!(c = nextvisible(c->next)))
1345 return;
1346 detach(c);
1347 attach(c);
1348 focus(c);
1349 if (c->minimized)
1350 toggleminimize(NULL);
1351 arrange();
1354 /* commands for use by mouse bindings */
1355 static void
1356 mouse_focus(const char *args[]) {
1357 focus(msel);
1358 if (msel->minimized)
1359 toggleminimize(NULL);
1362 static void
1363 mouse_fullscreen(const char *args[]) {
1364 mouse_focus(NULL);
1365 if (isarrange(fullscreen))
1366 setlayout(NULL);
1367 else
1368 setlayout(args);
1371 static void
1372 mouse_minimize(const char *args[]) {
1373 focus(msel);
1374 toggleminimize(NULL);
1377 static void
1378 mouse_zoom(const char *args[]) {
1379 focus(msel);
1380 zoom(NULL);
1383 static Cmd *
1384 get_cmd_by_name(const char *name) {
1385 for (unsigned int i = 0; i < countof(commands); i++) {
1386 if (!strcmp(name, commands[i].name))
1387 return &commands[i];
1389 return NULL;
1392 static void
1393 handle_cmdfifo(void) {
1394 int r;
1395 char *p, *s, cmdbuf[512], c;
1396 Cmd *cmd;
1397 switch (r = read(cmdfifo.fd, cmdbuf, sizeof cmdbuf - 1)) {
1398 case -1:
1399 case 0:
1400 cmdfifo.fd = -1;
1401 break;
1402 default:
1403 cmdbuf[r] = '\0';
1404 p = cmdbuf;
1405 while (*p) {
1406 /* find the command name */
1407 for (; *p == ' ' || *p == '\n'; p++);
1408 for (s = p; *p && *p != ' ' && *p != '\n'; p++);
1409 if ((c = *p))
1410 *p++ = '\0';
1411 if (*s && (cmd = get_cmd_by_name(s)) != NULL) {
1412 bool quote = false;
1413 int argc = 0;
1414 const char *args[MAX_ARGS], *arg;
1415 memset(args, 0, sizeof(args));
1416 /* if arguments were specified in config.h ignore the one given via
1417 * the named pipe and thus skip everything until we find a new line
1419 if (cmd->action.args[0] || c == '\n') {
1420 debug("execute %s", s);
1421 cmd->action.cmd(cmd->action.args);
1422 while (*p && *p != '\n')
1423 p++;
1424 continue;
1426 /* no arguments were given in config.h so we parse the command line */
1427 while (*p == ' ')
1428 p++;
1429 arg = p;
1430 for (; (c = *p); p++) {
1431 switch (*p) {
1432 case '\\':
1433 /* remove the escape character '\\' move every
1434 * following character to the left by one position
1436 switch (p[1]) {
1437 case '\\':
1438 case '\'':
1439 case '\"': {
1440 char *t = p+1;
1441 do {
1442 t[-1] = *t;
1443 } while (*t++);
1446 break;
1447 case '\'':
1448 case '\"':
1449 quote = !quote;
1450 break;
1451 case ' ':
1452 if (!quote) {
1453 case '\n':
1454 /* remove trailing quote if there is one */
1455 if (*(p - 1) == '\'' || *(p - 1) == '\"')
1456 *(p - 1) = '\0';
1457 *p++ = '\0';
1458 /* remove leading quote if there is one */
1459 if (*arg == '\'' || *arg == '\"')
1460 arg++;
1461 if (argc < MAX_ARGS)
1462 args[argc++] = arg;
1464 while (*p == ' ')
1465 ++p;
1466 arg = p--;
1468 break;
1471 if (c == '\n' || *p == '\n') {
1472 if (!*p)
1473 p++;
1474 debug("execute %s", s);
1475 for(int i = 0; i < argc; i++)
1476 debug(" %s", args[i]);
1477 debug("\n");
1478 cmd->action.cmd(args);
1479 break;
1487 static void
1488 handle_mouse(void) {
1489 #ifdef CONFIG_MOUSE
1490 MEVENT event;
1491 unsigned int i;
1492 if (getmouse(&event) != OK)
1493 return;
1494 msel = get_client_by_coord(event.x, event.y);
1496 if (!msel)
1497 return;
1499 debug("mouse x:%d y:%d cx:%d cy:%d mask:%d\n", event.x, event.y, event.x - msel->x, event.y - msel->y, event.bstate);
1501 vt_mouse(msel->term, event.x - msel->x, event.y - msel->y, event.bstate);
1503 for (i = 0; i < countof(buttons); i++) {
1504 if (event.bstate & buttons[i].mask)
1505 buttons[i].action.cmd(buttons[i].action.args);
1508 msel = NULL;
1509 #endif /* CONFIG_MOUSE */
1512 static void
1513 handle_statusbar(void) {
1514 char *p;
1515 int r;
1516 switch (r = read(bar.fd, bar.text, sizeof bar.text - 1)) {
1517 case -1:
1518 strncpy(bar.text, strerror(errno), sizeof bar.text - 1);
1519 bar.text[sizeof bar.text - 1] = '\0';
1520 bar.fd = -1;
1521 break;
1522 case 0:
1523 bar.fd = -1;
1524 break;
1525 default:
1526 bar.text[r] = '\0';
1527 p = bar.text + r - 1;
1528 for (; p >= bar.text && *p == '\n'; *p-- = '\0');
1529 for (; p >= bar.text && *p != '\n'; --p);
1530 if (p >= bar.text)
1531 memmove(bar.text, p + 1, strlen(p));
1532 drawbar();
1536 static void
1537 handle_editor(Client *c) {
1538 if (!copyreg.data && (copyreg.data = malloc(screen.history)))
1539 copyreg.size = screen.history;
1540 copyreg.len = 0;
1541 while (c->editor_fds[1] != -1 && copyreg.len < copyreg.size) {
1542 ssize_t len = read(c->editor_fds[1], copyreg.data + copyreg.len, copyreg.size - copyreg.len);
1543 if (len == -1) {
1544 if (errno == EINTR)
1545 continue;
1546 break;
1548 if (len == 0)
1549 break;
1550 copyreg.len += len;
1551 if (copyreg.len == copyreg.size) {
1552 copyreg.size *= 2;
1553 if (!(copyreg.data = realloc(copyreg.data, copyreg.size))) {
1554 copyreg.size = 0;
1555 copyreg.len = 0;
1559 c->editor_died = false;
1560 c->editor_fds[1] = -1;
1561 vt_destroy(c->editor);
1562 c->editor = NULL;
1563 c->term = c->app;
1564 vt_dirty(c->term);
1565 draw_content(c);
1566 wnoutrefresh(c->window);
1569 static int
1570 open_or_create_fifo(const char *name, const char **name_created) {
1571 struct stat info;
1572 int fd;
1574 do {
1575 if ((fd = open(name, O_RDWR|O_NONBLOCK)) == -1) {
1576 if (errno == ENOENT && !mkfifo(name, S_IRUSR|S_IWUSR)) {
1577 *name_created = name;
1578 continue;
1580 error("%s\n", strerror(errno));
1582 } while (fd == -1);
1584 if (fstat(fd, &info) == -1)
1585 error("%s\n", strerror(errno));
1586 if (!S_ISFIFO(info.st_mode))
1587 error("%s is not a named pipe\n", name);
1588 return fd;
1591 static void
1592 usage(void) {
1593 cleanup();
1594 eprint("usage: dvtm [-v] [-M] [-m mod] [-d delay] [-h lines] [-t title] "
1595 "[-s status-fifo] [-c cmd-fifo] [cmd...]\n");
1596 exit(EXIT_FAILURE);
1599 static bool
1600 parse_args(int argc, char *argv[]) {
1601 bool init = false;
1602 const char *name = argv[0];
1604 if (name && (name = strrchr(name, '/')))
1605 dvtm_name = name + 1;
1606 if (!getenv("ESCDELAY"))
1607 set_escdelay(100);
1608 for (int arg = 1; arg < argc; arg++) {
1609 if (argv[arg][0] != '-') {
1610 const char *args[] = { argv[arg], NULL, NULL };
1611 if (!init) {
1612 setup();
1613 init = true;
1615 create(args);
1616 continue;
1618 if (argv[arg][1] != 'v' && argv[arg][1] != 'M' && (arg + 1) >= argc)
1619 usage();
1620 switch (argv[arg][1]) {
1621 case 'v':
1622 puts("dvtm-"VERSION" © 2007-2014 Marc André Tanner");
1623 exit(EXIT_SUCCESS);
1624 case 'M':
1625 mouse_events_enabled = !mouse_events_enabled;
1626 break;
1627 case 'm': {
1628 char *mod = argv[++arg];
1629 if (mod[0] == '^' && mod[1])
1630 *mod = CTRL(mod[1]);
1631 for (unsigned int b = 0; b < countof(bindings); b++)
1632 if (bindings[b].keys[0] == MOD)
1633 bindings[b].keys[0] = *mod;
1634 break;
1636 case 'd':
1637 set_escdelay(atoi(argv[++arg]));
1638 if (ESCDELAY < 50)
1639 set_escdelay(50);
1640 else if (ESCDELAY > 1000)
1641 set_escdelay(1000);
1642 break;
1643 case 'h':
1644 screen.history = atoi(argv[++arg]);
1645 break;
1646 case 't':
1647 title = argv[++arg];
1648 break;
1649 case 's':
1650 bar.fd = open_or_create_fifo(argv[++arg], &bar.file);
1651 updatebarpos();
1652 break;
1653 case 'c': {
1654 const char *fifo;
1655 cmdfifo.fd = open_or_create_fifo(argv[++arg], &cmdfifo.file);
1656 if (!(fifo = realpath(argv[arg], NULL)))
1657 error("%s\n", strerror(errno));
1658 setenv("DVTM_CMD_FIFO", fifo, 1);
1659 break;
1661 default:
1662 usage();
1665 return init;
1669 main(int argc, char *argv[]) {
1670 KeyCombo keys;
1671 unsigned int key_index = 0;
1672 memset(keys, 0, sizeof(keys));
1673 sigset_t emptyset, blockset;
1675 setenv("DVTM", VERSION, 1);
1676 if (!parse_args(argc, argv)) {
1677 setup();
1678 startup(NULL);
1681 sigemptyset(&emptyset);
1682 sigemptyset(&blockset);
1683 sigaddset(&blockset, SIGWINCH);
1684 sigaddset(&blockset, SIGCHLD);
1685 sigprocmask(SIG_BLOCK, &blockset, NULL);
1687 while (running) {
1688 int r, nfds = 0;
1689 fd_set rd;
1691 if (screen.need_resize) {
1692 resize_screen();
1693 screen.need_resize = false;
1696 FD_ZERO(&rd);
1697 FD_SET(STDIN_FILENO, &rd);
1699 if (cmdfifo.fd != -1) {
1700 FD_SET(cmdfifo.fd, &rd);
1701 nfds = cmdfifo.fd;
1704 if (bar.fd != -1) {
1705 FD_SET(bar.fd, &rd);
1706 nfds = max(nfds, bar.fd);
1709 for (Client *c = clients; c; ) {
1710 if (c->editor && c->editor_died)
1711 handle_editor(c);
1712 if (!c->editor && c->died) {
1713 Client *t = c->next;
1714 destroy(c);
1715 c = t;
1716 continue;
1718 int pty = c->editor ? vt_pty_get(c->editor) : vt_pty_get(c->app);
1719 FD_SET(pty, &rd);
1720 nfds = max(nfds, pty);
1721 c = c->next;
1724 doupdate();
1725 r = pselect(nfds + 1, &rd, NULL, NULL, NULL, &emptyset);
1727 if (r == -1 && errno == EINTR)
1728 continue;
1730 if (r < 0) {
1731 perror("select()");
1732 exit(EXIT_FAILURE);
1735 if (FD_ISSET(STDIN_FILENO, &rd)) {
1736 int code = getch();
1737 if (code >= 0) {
1738 keys[key_index++] = code;
1739 KeyBinding *binding = NULL;
1740 if (code == KEY_MOUSE) {
1741 handle_mouse();
1742 } else if ((binding = keybinding(keys))) {
1743 unsigned int key_length = 0;
1744 while (key_length < MAX_KEYS && binding->keys[key_length])
1745 key_length++;
1746 if (key_index == key_length) {
1747 binding->action.cmd(binding->action.args);
1748 key_index = 0;
1749 memset(keys, 0, sizeof(keys));
1751 } else {
1752 key_index = 0;
1753 memset(keys, 0, sizeof(keys));
1754 keypress(code);
1757 if (r == 1) /* no data available on pty's */
1758 continue;
1761 if (cmdfifo.fd != -1 && FD_ISSET(cmdfifo.fd, &rd))
1762 handle_cmdfifo();
1764 if (bar.fd != -1 && FD_ISSET(bar.fd, &rd))
1765 handle_statusbar();
1767 for (Client *c = clients; c; c = c->next) {
1768 if (FD_ISSET(vt_pty_get(c->term), &rd)) {
1769 if (vt_process(c->term) < 0 && errno == EIO) {
1770 if (c->editor)
1771 c->editor_died = true;
1772 else
1773 c->died = true;
1774 continue;
1778 if (c != sel && is_content_visible(c)) {
1779 draw_content(c);
1780 wnoutrefresh(c->window);
1784 if (is_content_visible(sel)) {
1785 draw_content(sel);
1786 curs_set(vt_cursor_visible(sel->term));
1787 wnoutrefresh(sel->window);
1791 cleanup();
1792 return 0;