Fix Mod-C and eliminate redundant shell process
[dvtm.git] / dvtm.c
blob15ec1214a21d9d6f968fdcb66857d4f2cced84f8
1 /*
2 * The initial "port" of dwm to curses was done by
4 * © 2007-2015 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 unsigned int nmaster;
50 int history;
51 int w;
52 int h;
53 volatile sig_atomic_t need_resize;
54 } Screen;
56 typedef struct {
57 const char *symbol;
58 void (*arrange)(void);
59 } Layout;
61 typedef struct Client Client;
62 struct Client {
63 WINDOW *window;
64 Vt *term;
65 Vt *editor, *app;
66 int editor_fds[2];
67 volatile sig_atomic_t editor_died;
68 const char *cmd;
69 char title[255];
70 int order;
71 pid_t pid;
72 unsigned short int id;
73 unsigned short int x;
74 unsigned short int y;
75 unsigned short int w;
76 unsigned short int h;
77 bool has_title_line;
78 bool minimized;
79 bool urgent;
80 volatile sig_atomic_t died;
81 Client *next;
82 Client *prev;
83 Client *snext;
84 unsigned int tags;
87 typedef struct {
88 short fg;
89 short bg;
90 short fg256;
91 short bg256;
92 short pair;
93 } Color;
95 typedef struct {
96 const char *title;
97 attr_t attrs;
98 Color *color;
99 } ColorRule;
101 #define ALT(k) ((k) + (161 - 'a'))
102 #if defined CTRL && defined _AIX
103 #undef CTRL
104 #endif
105 #ifndef CTRL
106 #define CTRL(k) ((k) & 0x1F)
107 #endif
108 #define CTRL_ALT(k) ((k) + (129 - 'a'))
110 #define MAX_ARGS 3
112 typedef struct {
113 void (*cmd)(const char *args[]);
114 const char *args[MAX_ARGS];
115 } Action;
117 #define MAX_KEYS 3
119 typedef unsigned int KeyCombo[MAX_KEYS];
121 typedef struct {
122 KeyCombo keys;
123 Action action;
124 } KeyBinding;
126 typedef struct {
127 mmask_t mask;
128 Action action;
129 } Button;
131 typedef struct {
132 const char *name;
133 Action action;
134 } Cmd;
136 enum { BAR_TOP, BAR_BOTTOM, BAR_OFF };
138 typedef struct {
139 int fd;
140 int pos, lastpos;
141 bool autohide;
142 unsigned short int h;
143 unsigned short int y;
144 char text[512];
145 const char *file;
146 } StatusBar;
148 typedef struct {
149 int fd;
150 const char *file;
151 unsigned short int id;
152 } CmdFifo;
154 typedef struct {
155 char *data;
156 size_t len;
157 size_t size;
158 } Register;
160 typedef struct {
161 char *name;
162 const char *argv[4];
163 bool filter;
164 bool color;
165 } Editor;
167 #define LENGTH(arr) (sizeof(arr) / sizeof((arr)[0]))
168 #define MAX(x, y) ((x) > (y) ? (x) : (y))
169 #define MIN(x, y) ((x) < (y) ? (x) : (y))
170 #define TAGMASK ((1 << LENGTH(tags)) - 1)
172 #ifdef NDEBUG
173 #define debug(format, args...)
174 #else
175 #define debug eprint
176 #endif
178 /* commands for use by keybindings */
179 static void create(const char *args[]);
180 static void copymode(const char *args[]);
181 static void focusn(const char *args[]);
182 static void focusnext(const char *args[]);
183 static void focusnextnm(const char *args[]);
184 static void focusprev(const char *args[]);
185 static void focusprevnm(const char *args[]);
186 static void focuslast(const char *args[]);
187 static void killclient(const char *args[]);
188 static void paste(const char *args[]);
189 static void quit(const char *args[]);
190 static void redraw(const char *args[]);
191 static void scrollback(const char *args[]);
192 static void send(const char *args[]);
193 static void setlayout(const char *args[]);
194 static void incnmaster(const char *args[]);
195 static void setmfact(const char *args[]);
196 static void startup(const char *args[]);
197 static void tag(const char *args[]);
198 static void togglebar(const char *args[]);
199 static void togglebarpos(const char *args[]);
200 static void toggleminimize(const char *args[]);
201 static void togglemouse(const char *args[]);
202 static void togglerunall(const char *args[]);
203 static void toggletag(const char *args[]);
204 static void toggleview(const char *args[]);
205 static void viewprevtag(const char *args[]);
206 static void view(const char *args[]);
207 static void zoom(const char *args[]);
209 /* commands for use by mouse bindings */
210 static void mouse_focus(const char *args[]);
211 static void mouse_fullscreen(const char *args[]);
212 static void mouse_minimize(const char *args[]);
213 static void mouse_zoom(const char *args[]);
215 /* functions and variables available to layouts via config.h */
216 static Client* nextvisible(Client *c);
217 static void focus(Client *c);
218 static void resize(Client *c, int x, int y, int w, int h);
219 extern Screen screen;
220 static unsigned int waw, wah, wax, way;
221 static Client *clients = NULL;
222 static char *title;
224 #include "config.h"
226 /* global variables */
227 static const char *dvtm_name = "dvtm";
228 Screen screen = { .mfact = MFACT, .nmaster = NMASTER, .history = SCROLL_HISTORY };
229 static Client *stack = NULL;
230 static Client *sel = NULL;
231 static Client *lastsel = NULL;
232 static Client *msel = NULL;
233 static unsigned int seltags;
234 static unsigned int tagset[2] = { 1, 1 };
235 static bool mouse_events_enabled = ENABLE_MOUSE;
236 static Layout *layout = layouts;
237 static StatusBar bar = { .fd = -1, .lastpos = BAR_POS, .pos = BAR_POS, .autohide = BAR_AUTOHIDE, .h = 1 };
238 static CmdFifo cmdfifo = { .fd = -1 };
239 static const char *shell;
240 static Register copyreg;
241 static volatile sig_atomic_t running = true;
242 static bool runinall = false;
244 static void
245 eprint(const char *errstr, ...) {
246 va_list ap;
247 va_start(ap, errstr);
248 vfprintf(stderr, errstr, ap);
249 va_end(ap);
252 static void
253 error(const char *errstr, ...) {
254 va_list ap;
255 va_start(ap, errstr);
256 vfprintf(stderr, errstr, ap);
257 va_end(ap);
258 exit(EXIT_FAILURE);
261 static bool
262 isarrange(void (*func)()) {
263 return func == layout->arrange;
266 static bool
267 isvisible(Client *c) {
268 return c->tags & tagset[seltags];
271 static bool
272 is_content_visible(Client *c) {
273 if (!c)
274 return false;
275 if (isarrange(fullscreen))
276 return sel == c;
277 return isvisible(c) && !c->minimized;
280 static Client*
281 nextvisible(Client *c) {
282 for (; c && !isvisible(c); c = c->next);
283 return c;
286 static void
287 updatebarpos(void) {
288 bar.y = 0;
289 wax = 0;
290 way = 0;
291 wah = screen.h;
292 waw = screen.w;
293 if (bar.pos == BAR_TOP) {
294 wah -= bar.h;
295 way += bar.h;
296 } else if (bar.pos == BAR_BOTTOM) {
297 wah -= bar.h;
298 bar.y = wah;
302 static void
303 hidebar(void) {
304 if (bar.pos != BAR_OFF) {
305 bar.lastpos = bar.pos;
306 bar.pos = BAR_OFF;
310 static void
311 showbar(void) {
312 if (bar.pos == BAR_OFF)
313 bar.pos = bar.lastpos;
316 static void
317 drawbar(void) {
318 int sx, sy, x, y, width;
319 unsigned int occupied = 0, urgent = 0;
320 if (bar.pos == BAR_OFF)
321 return;
323 for (Client *c = clients; c; c = c->next) {
324 occupied |= c->tags;
325 if (c->urgent)
326 urgent |= c->tags;
329 getyx(stdscr, sy, sx);
330 attrset(BAR_ATTR);
331 move(bar.y, 0);
333 for (unsigned int i = 0; i < LENGTH(tags); i++){
334 if (tagset[seltags] & (1 << i))
335 attrset(TAG_SEL);
336 else if (urgent & (1 << i))
337 attrset(TAG_URGENT);
338 else if (occupied & (1 << i))
339 attrset(TAG_OCCUPIED);
340 else
341 attrset(TAG_NORMAL);
342 printw(TAG_SYMBOL, tags[i]);
345 attrset(TAG_NORMAL);
346 addstr(layout->symbol);
348 getyx(stdscr, y, x);
349 (void)y;
350 int maxwidth = screen.w - x - 2;
352 addch(BAR_BEGIN);
353 attrset(BAR_ATTR);
355 wchar_t wbuf[sizeof bar.text];
356 size_t numchars = mbstowcs(wbuf, bar.text, sizeof bar.text);
358 if (numchars != (size_t)-1 && (width = wcswidth(wbuf, maxwidth)) != -1) {
359 int pos;
360 for (pos = 0; pos + width < maxwidth; pos++)
361 addch(' ');
363 for (size_t i = 0; i < numchars; i++) {
364 pos += wcwidth(wbuf[i]);
365 if (pos > maxwidth)
366 break;
367 addnwstr(wbuf+i, 1);
370 clrtoeol();
373 attrset(TAG_NORMAL);
374 mvaddch(bar.y, screen.w - 1, BAR_END);
375 attrset(NORMAL_ATTR);
376 move(sy, sx);
377 wnoutrefresh(stdscr);
380 static int
381 show_border(void) {
382 return (bar.pos != BAR_OFF) || (clients && clients->next);
385 static void
386 draw_border(Client *c) {
387 char t = '\0';
388 int x, y, maxlen, attrs = NORMAL_ATTR;
390 if (!show_border())
391 return;
392 if (sel == c || (runinall && !c->minimized))
393 attrs = SELECTED_ATTR;
394 if (sel != c && c->urgent)
395 attrs = URGENT_ATTR;
397 wattrset(c->window, attrs);
398 getyx(c->window, y, x);
399 mvwhline(c->window, 0, 0, ACS_HLINE, c->w);
400 maxlen = c->w - 10;
401 if (maxlen < 0)
402 maxlen = 0;
403 if ((size_t)maxlen < sizeof(c->title)) {
404 t = c->title[maxlen];
405 c->title[maxlen] = '\0';
408 mvwprintw(c->window, 0, 2, "[%s%s#%d]",
409 *c->title ? c->title : "",
410 *c->title ? " | " : "",
411 c->order);
412 if (t)
413 c->title[maxlen] = t;
414 wmove(c->window, y, x);
417 static void
418 draw_content(Client *c) {
419 vt_draw(c->term, c->window, c->has_title_line, 0);
422 static void
423 draw(Client *c) {
424 if (is_content_visible(c)) {
425 redrawwin(c->window);
426 draw_content(c);
428 if (!isarrange(fullscreen) || sel == c)
429 draw_border(c);
430 wnoutrefresh(c->window);
433 static void
434 draw_all(void) {
435 if (!nextvisible(clients)) {
436 sel = NULL;
437 curs_set(0);
438 erase();
439 drawbar();
440 doupdate();
441 return;
444 if (!isarrange(fullscreen)) {
445 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
446 if (c == sel)
447 continue;
448 draw(c);
451 /* as a last step the selected window is redrawn,
452 * this has the effect that the cursor position is
453 * accurate
455 if (sel)
456 draw(sel);
459 static void
460 arrange(void) {
461 unsigned int m = 0, n = 0;
462 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
463 c->order = ++n;
464 if (c->minimized)
465 m++;
467 erase();
468 attrset(NORMAL_ATTR);
469 if (bar.fd == -1 && bar.autohide) {
470 if ((!clients || !clients->next) && n == 1)
471 hidebar();
472 else
473 showbar();
474 updatebarpos();
476 if (m && !isarrange(fullscreen))
477 wah--;
478 layout->arrange();
479 if (m && !isarrange(fullscreen)) {
480 unsigned int i = 0, nw = waw / m, nx = wax;
481 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
482 if (c->minimized) {
483 resize(c, nx, way+wah, ++i == m ? waw - nx : nw, 1);
484 nx += nw;
487 wah++;
489 focus(NULL);
490 wnoutrefresh(stdscr);
491 drawbar();
492 draw_all();
495 static void
496 attach(Client *c) {
497 if (clients)
498 clients->prev = c;
499 c->next = clients;
500 c->prev = NULL;
501 clients = c;
502 for (int o = 1; c; c = nextvisible(c->next), o++)
503 c->order = o;
506 static void
507 attachafter(Client *c, Client *a) { /* attach c after a */
508 if (c == a)
509 return;
510 if (!a)
511 for (a = clients; a && a->next; a = a->next);
513 if (a) {
514 if (a->next)
515 a->next->prev = c;
516 c->next = a->next;
517 c->prev = a;
518 a->next = c;
519 for (int o = a->order; c; c = nextvisible(c->next))
520 c->order = ++o;
524 static void
525 attachstack(Client *c) {
526 c->snext = stack;
527 stack = c;
530 static void
531 detach(Client *c) {
532 Client *d;
533 if (c->prev)
534 c->prev->next = c->next;
535 if (c->next) {
536 c->next->prev = c->prev;
537 for (d = nextvisible(c->next); d; d = nextvisible(d->next))
538 --d->order;
540 if (c == clients)
541 clients = c->next;
542 c->next = c->prev = NULL;
545 static void
546 settitle(Client *c) {
547 char *term, *t = title;
548 if (!t && sel == c && *c->title)
549 t = c->title;
550 if (t && (term = getenv("TERM")) && !strstr(term, "linux"))
551 printf("\033]0;%s\007", t);
554 static void
555 detachstack(Client *c) {
556 Client **tc;
557 for (tc=&stack; *tc && *tc != c; tc=&(*tc)->snext);
558 *tc = c->snext;
561 static void
562 focus(Client *c) {
563 if (!c)
564 for (c = stack; c && !isvisible(c); c = c->snext);
565 if (sel == c)
566 return;
567 lastsel = sel;
568 sel = c;
569 if (lastsel) {
570 lastsel->urgent = false;
571 if (!isarrange(fullscreen)) {
572 draw_border(lastsel);
573 wnoutrefresh(lastsel->window);
577 if (c) {
578 detachstack(c);
579 attachstack(c);
580 settitle(c);
581 c->urgent = false;
582 if (isarrange(fullscreen)) {
583 draw(c);
584 } else {
585 draw_border(c);
586 wnoutrefresh(c->window);
589 curs_set(c && !c->minimized && vt_cursor_visible(c->term));
592 static void
593 applycolorrules(Client *c) {
594 const ColorRule *r = colorrules;
595 short fg = r->color->fg, bg = r->color->bg;
596 attr_t attrs = r->attrs;
598 for (unsigned int i = 1; i < LENGTH(colorrules); i++) {
599 r = &colorrules[i];
600 if (strstr(c->title, r->title)) {
601 attrs = r->attrs;
602 fg = r->color->fg;
603 bg = r->color->bg;
604 break;
608 vt_default_colors_set(c->term, attrs, fg, bg);
611 static void
612 term_title_handler(Vt *term, const char *title) {
613 Client *c = (Client *)vt_data_get(term);
614 if (title)
615 strncpy(c->title, title, sizeof(c->title) - 1);
616 c->title[title ? sizeof(c->title) - 1 : 0] = '\0';
617 settitle(c);
618 if (!isarrange(fullscreen) || sel == c)
619 draw_border(c);
620 applycolorrules(c);
623 static void
624 term_urgent_handler(Vt *term) {
625 Client *c = (Client *)vt_data_get(term);
626 c->urgent = true;
627 printf("\a");
628 fflush(stdout);
629 drawbar();
630 if (!isarrange(fullscreen) && sel != c && isvisible(c))
631 draw_border(c);
634 static void
635 move_client(Client *c, int x, int y) {
636 if (c->x == x && c->y == y)
637 return;
638 debug("moving, x: %d y: %d\n", x, y);
639 if (mvwin(c->window, y, x) == ERR) {
640 eprint("error moving, x: %d y: %d\n", x, y);
641 } else {
642 c->x = x;
643 c->y = y;
647 static void
648 resize_client(Client *c, int w, int h) {
649 bool has_title_line = show_border();
650 bool resize_window = c->w != w || c->h != h;
651 if (resize_window) {
652 debug("resizing, w: %d h: %d\n", w, h);
653 if (wresize(c->window, h, w) == ERR) {
654 eprint("error resizing, w: %d h: %d\n", w, h);
655 } else {
656 c->w = w;
657 c->h = h;
660 if (resize_window || c->has_title_line != has_title_line) {
661 c->has_title_line = has_title_line;
662 vt_resize(c->app, h - has_title_line, w);
663 if (c->editor)
664 vt_resize(c->editor, h - has_title_line, w);
668 static void
669 resize(Client *c, int x, int y, int w, int h) {
670 resize_client(c, w, h);
671 move_client(c, x, y);
674 static Client*
675 get_client_by_coord(unsigned int x, unsigned int y) {
676 if (y < way || y >= wah)
677 return NULL;
678 if (isarrange(fullscreen))
679 return sel;
680 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
681 if (x >= c->x && x < c->x + c->w && y >= c->y && y < c->y + c->h) {
682 debug("mouse event, x: %d y: %d client: %d\n", x, y, c->order);
683 return c;
686 return NULL;
689 static void
690 sigchld_handler(int sig) {
691 int errsv = errno;
692 int status;
693 pid_t pid;
695 while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
696 if (pid == -1) {
697 if (errno == ECHILD) {
698 /* no more child processes */
699 break;
701 eprint("waitpid: %s\n", strerror(errno));
702 break;
705 debug("child with pid %d died\n", pid);
707 for (Client *c = clients; c; c = c->next) {
708 if (c->pid == pid) {
709 c->died = true;
710 break;
712 if (c->editor && vt_pid_get(c->editor) == pid) {
713 c->editor_died = true;
714 break;
719 errno = errsv;
722 static void
723 sigwinch_handler(int sig) {
724 screen.need_resize = true;
727 static void
728 sigterm_handler(int sig) {
729 running = false;
732 static void
733 resize_screen(void) {
734 struct winsize ws;
736 if (ioctl(0, TIOCGWINSZ, &ws) == -1) {
737 getmaxyx(stdscr, screen.h, screen.w);
738 } else {
739 screen.w = ws.ws_col;
740 screen.h = ws.ws_row;
743 debug("resize_screen(), w: %d h: %d\n", screen.w, screen.h);
745 resizeterm(screen.h, screen.w);
746 wresize(stdscr, screen.h, screen.w);
747 updatebarpos();
748 clear();
749 arrange();
752 static KeyBinding*
753 keybinding(KeyCombo keys) {
754 unsigned int keycount = 0;
755 while (keycount < MAX_KEYS && keys[keycount])
756 keycount++;
757 for (unsigned int b = 0; b < LENGTH(bindings); b++) {
758 for (unsigned int k = 0; k < keycount; k++) {
759 if (keys[k] != bindings[b].keys[k])
760 break;
761 if (k == keycount - 1)
762 return &bindings[b];
765 return NULL;
768 static unsigned int
769 bitoftag(const char *tag) {
770 unsigned int i;
771 for (i = 0; (i < LENGTH(tags)) && (tags[i] != tag); i++);
772 return (i < LENGTH(tags)) ? (1 << i) : ~0;
775 static void
776 tagschanged() {
777 bool allminimized = true;
778 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
779 if (!c->minimized) {
780 allminimized = false;
781 break;
784 if (allminimized && nextvisible(clients)) {
785 focus(NULL);
786 toggleminimize(NULL);
788 arrange();
791 static void
792 tag(const char *args[]) {
793 if (!sel)
794 return;
795 sel->tags = bitoftag(args[0]) & TAGMASK;
796 tagschanged();
799 static void
800 toggletag(const char *args[]) {
801 if (!sel)
802 return;
803 unsigned int newtags = sel->tags ^ (bitoftag(args[0]) & TAGMASK);
804 if (newtags) {
805 sel->tags = newtags;
806 tagschanged();
810 static void
811 toggleview(const char *args[]) {
812 unsigned int newtagset = tagset[seltags] ^ (bitoftag(args[0]) & TAGMASK);
813 if (newtagset) {
814 tagset[seltags] = newtagset;
815 tagschanged();
819 static void
820 view(const char *args[]) {
821 unsigned int newtagset = bitoftag(args[0]) & TAGMASK;
822 if (tagset[seltags] != newtagset && newtagset) {
823 seltags ^= 1; /* toggle sel tagset */
824 tagset[seltags] = newtagset;
825 tagschanged();
829 static void
830 viewprevtag(const char *args[]) {
831 seltags ^= 1;
832 tagschanged();
835 static void
836 keypress(int code) {
837 unsigned int len = 1;
838 char buf[8] = { '\e' };
840 if (code == '\e') {
841 /* pass characters following escape to the underlying app */
842 nodelay(stdscr, TRUE);
843 for (int t; len < sizeof(buf) && (t = getch()) != ERR; len++)
844 buf[len] = t;
845 nodelay(stdscr, FALSE);
848 for (Client *c = runinall ? nextvisible(clients) : sel; c; c = nextvisible(c->next)) {
849 if (is_content_visible(c)) {
850 c->urgent = false;
851 if (code == '\e')
852 vt_write(c->term, buf, len);
853 else
854 vt_keypress(c->term, code);
856 if (!runinall)
857 break;
861 static void
862 mouse_setup(void) {
863 #ifdef CONFIG_MOUSE
864 mmask_t mask = 0;
866 if (mouse_events_enabled) {
867 mask = BUTTON1_CLICKED | BUTTON2_CLICKED;
868 for (unsigned int i = 0; i < LENGTH(buttons); i++)
869 mask |= buttons[i].mask;
871 mousemask(mask, NULL);
872 #endif /* CONFIG_MOUSE */
875 static bool
876 checkshell(const char *shell) {
877 if (shell == NULL || *shell == '\0' || *shell != '/')
878 return false;
879 if (!strcmp(strrchr(shell, '/')+1, dvtm_name))
880 return false;
881 if (access(shell, X_OK))
882 return false;
883 return true;
886 static const char *
887 getshell(void) {
888 const char *shell = getenv("SHELL");
889 struct passwd *pw;
891 if (checkshell(shell))
892 return shell;
893 if ((pw = getpwuid(getuid())) && checkshell(pw->pw_shell))
894 return pw->pw_shell;
895 return "/bin/sh";
898 static void
899 setup(void) {
900 shell = getshell();
901 setlocale(LC_CTYPE, "");
902 initscr();
903 start_color();
904 noecho();
905 nonl();
906 keypad(stdscr, TRUE);
907 mouse_setup();
908 raw();
909 vt_init();
910 vt_keytable_set(keytable, LENGTH(keytable));
911 for (unsigned int i = 0; i < LENGTH(colors); i++) {
912 if (COLORS == 256) {
913 if (colors[i].fg256)
914 colors[i].fg = colors[i].fg256;
915 if (colors[i].bg256)
916 colors[i].bg = colors[i].bg256;
918 colors[i].pair = vt_color_reserve(colors[i].fg, colors[i].bg);
920 resize_screen();
921 struct sigaction sa;
922 sa.sa_flags = 0;
923 sigemptyset(&sa.sa_mask);
924 sa.sa_handler = sigwinch_handler;
925 sigaction(SIGWINCH, &sa, NULL);
926 sa.sa_handler = sigchld_handler;
927 sigaction(SIGCHLD, &sa, NULL);
928 sa.sa_handler = sigterm_handler;
929 sigaction(SIGTERM, &sa, NULL);
930 sa.sa_handler = SIG_IGN;
931 sigaction(SIGPIPE, &sa, NULL);
934 static void
935 destroy(Client *c) {
936 if (sel == c)
937 focusnextnm(NULL);
938 detach(c);
939 detachstack(c);
940 if (sel == c) {
941 Client *next = nextvisible(clients);
942 if (next) {
943 focus(next);
944 toggleminimize(NULL);
945 } else {
946 sel = NULL;
949 if (lastsel == c)
950 lastsel = NULL;
951 werase(c->window);
952 wnoutrefresh(c->window);
953 vt_destroy(c->term);
954 delwin(c->window);
955 if (!clients && LENGTH(actions)) {
956 if (!strcmp(c->cmd, shell))
957 quit(NULL);
958 else
959 create(NULL);
961 free(c);
962 arrange();
965 static void
966 cleanup(void) {
967 while (clients)
968 destroy(clients);
969 vt_shutdown();
970 endwin();
971 free(copyreg.data);
972 if (bar.fd > 0)
973 close(bar.fd);
974 if (bar.file)
975 unlink(bar.file);
976 if (cmdfifo.fd > 0)
977 close(cmdfifo.fd);
978 if (cmdfifo.file)
979 unlink(cmdfifo.file);
982 static char *getcwd_by_pid(Client *c) {
983 if (!c)
984 return NULL;
985 char buf[32];
986 snprintf(buf, sizeof buf, "/proc/%d/cwd", c->pid);
987 return realpath(buf, NULL);
990 static void
991 create(const char *args[]) {
992 const char *cmd;
993 const char *pargs[4];
994 char buf[8], *cwd = NULL;
995 const char *env[] = {
996 "DVTM_WINDOW_ID", buf,
997 NULL
1000 if (args && args[0]) {
1001 cmd = args[0];
1002 pargs[0] = shell;
1003 pargs[1] = "-c";
1004 pargs[2] = cmd;
1005 pargs[3] = NULL;
1006 } else {
1007 cmd = shell;
1008 pargs[0] = cmd;
1009 pargs[1] = NULL;
1011 Client *c = calloc(1, sizeof(Client));
1012 if (!c)
1013 return;
1014 c->tags = tagset[seltags];
1015 c->id = ++cmdfifo.id;
1016 snprintf(buf, sizeof buf, "%d", c->id);
1018 if (!(c->window = newwin(wah, waw, way, wax))) {
1019 free(c);
1020 return;
1023 c->term = c->app = vt_create(screen.h, screen.w, screen.history);
1024 if (!c->term) {
1025 delwin(c->window);
1026 free(c);
1027 return;
1030 c->cmd = cmd;
1031 if (args && args[1]) {
1032 strncpy(c->title, args[1], sizeof(c->title) - 1);
1033 c->title[sizeof(c->title) - 1] = '\0';
1035 if (args && args[2])
1036 cwd = !strcmp(args[2], "$CWD") ? getcwd_by_pid(sel) : (char*)args[2];
1037 c->pid = vt_forkpty(c->term, shell, pargs, cwd, env, NULL, NULL);
1038 if (args && args[2] && !strcmp(args[2], "$CWD"))
1039 free(cwd);
1040 vt_data_set(c->term, c);
1041 vt_title_handler_set(c->term, term_title_handler);
1042 vt_urgent_handler_set(c->term, term_urgent_handler);
1043 c->x = wax;
1044 c->y = way;
1045 debug("client with pid %d forked\n", c->pid);
1046 attach(c);
1047 focus(c);
1048 arrange();
1051 static void
1052 copymode(const char *args[]) {
1053 if (!sel || sel->editor)
1054 return;
1055 if (!(sel->editor = vt_create(sel->h - sel->has_title_line, sel->w, 0)))
1056 return;
1058 char *ed = getenv("DVTM_EDITOR");
1059 int *to = &sel->editor_fds[0], *from = NULL;
1060 sel->editor_fds[0] = sel->editor_fds[1] = -1;
1062 if (!ed)
1063 ed = getenv("EDITOR");
1064 if (!ed)
1065 ed = getenv("PAGER");
1066 if (!ed)
1067 ed = editors[0].name;
1069 const char **argv = (const char*[]){ ed, "-", NULL, NULL };
1070 char argline[32];
1071 bool colored = false;
1073 for (unsigned int i = 0; i < LENGTH(editors); i++) {
1074 if (!strcmp(editors[i].name, ed)) {
1075 for (int j = 1; editors[i].argv[j]; j++) {
1076 if (strstr(editors[i].argv[j], "%d")) {
1077 int line = vt_content_start(sel->app);
1078 snprintf(argline, sizeof(argline), "+%d", line);
1079 argv[j] = argline;
1080 } else {
1081 argv[j] = editors[i].argv[j];
1084 if (editors[i].filter)
1085 from = &sel->editor_fds[1];
1086 colored = editors[i].color;
1087 break;
1091 if (vt_forkpty(sel->editor, ed, argv, NULL, NULL, to, from) < 0) {
1092 vt_destroy(sel->editor);
1093 sel->editor = NULL;
1094 return;
1097 sel->term = sel->editor;
1099 if (sel->editor_fds[0] != -1) {
1100 char *buf = NULL;
1101 size_t len = vt_content_get(sel->app, &buf, colored);
1102 char *cur = buf;
1103 while (len > 0) {
1104 ssize_t res = write(sel->editor_fds[0], cur, len);
1105 if (res < 0) {
1106 if (errno == EAGAIN || errno == EINTR)
1107 continue;
1108 break;
1110 cur += res;
1111 len -= res;
1113 free(buf);
1114 close(sel->editor_fds[0]);
1115 sel->editor_fds[0] = -1;
1118 if (args[0])
1119 vt_write(sel->editor, args[0], strlen(args[0]));
1122 static void
1123 focusn(const char *args[]) {
1124 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
1125 if (c->order == atoi(args[0])) {
1126 focus(c);
1127 if (c->minimized)
1128 toggleminimize(NULL);
1129 return;
1134 static void
1135 focusnext(const char *args[]) {
1136 Client *c;
1137 if (!sel)
1138 return;
1139 for (c = sel->next; c && !isvisible(c); c = c->next);
1140 if (!c)
1141 for (c = clients; c && !isvisible(c); c = c->next);
1142 if (c)
1143 focus(c);
1146 static void
1147 focusnextnm(const char *args[]) {
1148 if (!sel)
1149 return;
1150 Client *c = sel;
1151 do {
1152 c = nextvisible(c->next);
1153 if (!c)
1154 c = nextvisible(clients);
1155 } while (c->minimized && c != sel);
1156 focus(c);
1159 static void
1160 focusprev(const char *args[]) {
1161 Client *c;
1162 if (!sel)
1163 return;
1164 for (c = sel->prev; c && !isvisible(c); c = c->prev);
1165 if (!c) {
1166 for (c = clients; c && c->next; c = c->next);
1167 for (; c && !isvisible(c); c = c->prev);
1169 if (c)
1170 focus(c);
1173 static void
1174 focusprevnm(const char *args[]) {
1175 if (!sel)
1176 return;
1177 Client *c = sel;
1178 do {
1179 for (c = c->prev; c && !isvisible(c); c = c->prev);
1180 if (!c) {
1181 for (c = clients; c && c->next; c = c->next);
1182 for (; c && !isvisible(c); c = c->prev);
1184 } while (c && c != sel && c->minimized);
1185 focus(c);
1188 static void
1189 focuslast(const char *args[]) {
1190 if (lastsel)
1191 focus(lastsel);
1194 static void
1195 killclient(const char *args[]) {
1196 if (!sel)
1197 return;
1198 debug("killing client with pid: %d\n", sel->pid);
1199 kill(-sel->pid, SIGKILL);
1202 static void
1203 paste(const char *args[]) {
1204 if (sel && copyreg.data)
1205 vt_write(sel->term, copyreg.data, copyreg.len);
1208 static void
1209 quit(const char *args[]) {
1210 cleanup();
1211 exit(EXIT_SUCCESS);
1214 static void
1215 redraw(const char *args[]) {
1216 for (Client *c = clients; c; c = c->next) {
1217 if (!c->minimized) {
1218 vt_dirty(c->term);
1219 wclear(c->window);
1220 wnoutrefresh(c->window);
1223 resize_screen();
1226 static void
1227 scrollback(const char *args[]) {
1228 if (!is_content_visible(sel))
1229 return;
1231 if (!args[0] || atoi(args[0]) < 0)
1232 vt_scroll(sel->term, -sel->h/2);
1233 else
1234 vt_scroll(sel->term, sel->h/2);
1236 draw(sel);
1237 curs_set(vt_cursor_visible(sel->term));
1240 static void
1241 send(const char *args[]) {
1242 if (sel && args && args[0])
1243 vt_write(sel->term, args[0], strlen(args[0]));
1246 static void
1247 setlayout(const char *args[]) {
1248 unsigned int i;
1250 if (!args || !args[0]) {
1251 if (++layout == &layouts[LENGTH(layouts)])
1252 layout = &layouts[0];
1253 } else {
1254 for (i = 0; i < LENGTH(layouts); i++)
1255 if (!strcmp(args[0], layouts[i].symbol))
1256 break;
1257 if (i == LENGTH(layouts))
1258 return;
1259 layout = &layouts[i];
1261 arrange();
1264 static void
1265 incnmaster(const char *args[]) {
1266 int delta;
1268 if (isarrange(fullscreen) || isarrange(grid))
1269 return;
1270 /* arg handling, manipulate nmaster */
1271 if (args[0] == NULL) {
1272 screen.nmaster = NMASTER;
1273 } else if (sscanf(args[0], "%d", &delta) == 1) {
1274 if (args[0][0] == '+' || args[0][0] == '-')
1275 screen.nmaster += delta;
1276 else
1277 screen.nmaster = delta;
1278 if (screen.nmaster < 1)
1279 screen.nmaster = 1;
1281 arrange();
1284 static void
1285 setmfact(const char *args[]) {
1286 float delta;
1288 if (isarrange(fullscreen) || isarrange(grid))
1289 return;
1290 /* arg handling, manipulate mfact */
1291 if (args[0] == NULL) {
1292 screen.mfact = MFACT;
1293 } else if (sscanf(args[0], "%f", &delta) == 1) {
1294 if (args[0][0] == '+' || args[0][0] == '-')
1295 screen.mfact += delta;
1296 else
1297 screen.mfact = delta;
1298 if (screen.mfact < 0.1)
1299 screen.mfact = 0.1;
1300 else if (screen.mfact > 0.9)
1301 screen.mfact = 0.9;
1303 arrange();
1306 static void
1307 startup(const char *args[]) {
1308 for (unsigned int i = 0; i < LENGTH(actions); i++)
1309 actions[i].cmd(actions[i].args);
1312 static void
1313 togglebar(const char *args[]) {
1314 if (bar.pos == BAR_OFF)
1315 showbar();
1316 else
1317 hidebar();
1318 bar.autohide = false;
1319 updatebarpos();
1320 redraw(NULL);
1323 static void
1324 togglebarpos(const char *args[]) {
1325 switch (bar.pos == BAR_OFF ? bar.lastpos : bar.pos) {
1326 case BAR_TOP:
1327 bar.pos = BAR_BOTTOM;
1328 break;
1329 case BAR_BOTTOM:
1330 bar.pos = BAR_TOP;
1331 break;
1333 updatebarpos();
1334 redraw(NULL);
1337 static void
1338 toggleminimize(const char *args[]) {
1339 Client *c, *m, *t;
1340 unsigned int n;
1341 if (!sel)
1342 return;
1343 /* the last window can't be minimized */
1344 if (!sel->minimized) {
1345 for (n = 0, c = nextvisible(clients); c; c = nextvisible(c->next))
1346 if (!c->minimized)
1347 n++;
1348 if (n == 1)
1349 return;
1351 sel->minimized = !sel->minimized;
1352 m = sel;
1353 /* check whether the master client was minimized */
1354 if (sel == nextvisible(clients) && sel->minimized) {
1355 c = nextvisible(sel->next);
1356 detach(c);
1357 attach(c);
1358 focus(c);
1359 detach(m);
1360 for (; c && (t = nextvisible(c->next)) && !t->minimized; c = t);
1361 attachafter(m, c);
1362 } else if (m->minimized) {
1363 /* non master window got minimized move it above all other
1364 * minimized ones */
1365 focusnextnm(NULL);
1366 detach(m);
1367 for (c = nextvisible(clients); c && (t = nextvisible(c->next)) && !t->minimized; c = t);
1368 attachafter(m, c);
1369 } else { /* window is no longer minimized, move it to the master area */
1370 vt_dirty(m->term);
1371 detach(m);
1372 attach(m);
1374 arrange();
1377 static void
1378 togglemouse(const char *args[]) {
1379 mouse_events_enabled = !mouse_events_enabled;
1380 mouse_setup();
1383 static void
1384 togglerunall(const char *args[]) {
1385 runinall = !runinall;
1386 draw_all();
1389 static void
1390 zoom(const char *args[]) {
1391 Client *c;
1393 if (!sel)
1394 return;
1395 if (args && args[0])
1396 focusn(args);
1397 if ((c = sel) == nextvisible(clients))
1398 if (!(c = nextvisible(c->next)))
1399 return;
1400 detach(c);
1401 attach(c);
1402 focus(c);
1403 if (c->minimized)
1404 toggleminimize(NULL);
1405 arrange();
1408 /* commands for use by mouse bindings */
1409 static void
1410 mouse_focus(const char *args[]) {
1411 focus(msel);
1412 if (msel->minimized)
1413 toggleminimize(NULL);
1416 static void
1417 mouse_fullscreen(const char *args[]) {
1418 mouse_focus(NULL);
1419 if (isarrange(fullscreen))
1420 setlayout(NULL);
1421 else
1422 setlayout(args);
1425 static void
1426 mouse_minimize(const char *args[]) {
1427 focus(msel);
1428 toggleminimize(NULL);
1431 static void
1432 mouse_zoom(const char *args[]) {
1433 focus(msel);
1434 zoom(NULL);
1437 static Cmd *
1438 get_cmd_by_name(const char *name) {
1439 for (unsigned int i = 0; i < LENGTH(commands); i++) {
1440 if (!strcmp(name, commands[i].name))
1441 return &commands[i];
1443 return NULL;
1446 static void
1447 handle_cmdfifo(void) {
1448 int r;
1449 char *p, *s, cmdbuf[512], c;
1450 Cmd *cmd;
1451 switch (r = read(cmdfifo.fd, cmdbuf, sizeof cmdbuf - 1)) {
1452 case -1:
1453 case 0:
1454 cmdfifo.fd = -1;
1455 break;
1456 default:
1457 cmdbuf[r] = '\0';
1458 p = cmdbuf;
1459 while (*p) {
1460 /* find the command name */
1461 for (; *p == ' ' || *p == '\n'; p++);
1462 for (s = p; *p && *p != ' ' && *p != '\n'; p++);
1463 if ((c = *p))
1464 *p++ = '\0';
1465 if (*s && (cmd = get_cmd_by_name(s)) != NULL) {
1466 bool quote = false;
1467 int argc = 0;
1468 const char *args[MAX_ARGS], *arg;
1469 memset(args, 0, sizeof(args));
1470 /* if arguments were specified in config.h ignore the one given via
1471 * the named pipe and thus skip everything until we find a new line
1473 if (cmd->action.args[0] || c == '\n') {
1474 debug("execute %s", s);
1475 cmd->action.cmd(cmd->action.args);
1476 while (*p && *p != '\n')
1477 p++;
1478 continue;
1480 /* no arguments were given in config.h so we parse the command line */
1481 while (*p == ' ')
1482 p++;
1483 arg = p;
1484 for (; (c = *p); p++) {
1485 switch (*p) {
1486 case '\\':
1487 /* remove the escape character '\\' move every
1488 * following character to the left by one position
1490 switch (p[1]) {
1491 case '\\':
1492 case '\'':
1493 case '\"': {
1494 char *t = p+1;
1495 do {
1496 t[-1] = *t;
1497 } while (*t++);
1500 break;
1501 case '\'':
1502 case '\"':
1503 quote = !quote;
1504 break;
1505 case ' ':
1506 if (!quote) {
1507 case '\n':
1508 /* remove trailing quote if there is one */
1509 if (*(p - 1) == '\'' || *(p - 1) == '\"')
1510 *(p - 1) = '\0';
1511 *p++ = '\0';
1512 /* remove leading quote if there is one */
1513 if (*arg == '\'' || *arg == '\"')
1514 arg++;
1515 if (argc < MAX_ARGS)
1516 args[argc++] = arg;
1518 while (*p == ' ')
1519 ++p;
1520 arg = p--;
1522 break;
1525 if (c == '\n' || *p == '\n') {
1526 if (!*p)
1527 p++;
1528 debug("execute %s", s);
1529 for(int i = 0; i < argc; i++)
1530 debug(" %s", args[i]);
1531 debug("\n");
1532 cmd->action.cmd(args);
1533 break;
1541 static void
1542 handle_mouse(void) {
1543 #ifdef CONFIG_MOUSE
1544 MEVENT event;
1545 unsigned int i;
1546 if (getmouse(&event) != OK)
1547 return;
1548 msel = get_client_by_coord(event.x, event.y);
1550 if (!msel)
1551 return;
1553 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);
1555 vt_mouse(msel->term, event.x - msel->x, event.y - msel->y, event.bstate);
1557 for (i = 0; i < LENGTH(buttons); i++) {
1558 if (event.bstate & buttons[i].mask)
1559 buttons[i].action.cmd(buttons[i].action.args);
1562 msel = NULL;
1563 #endif /* CONFIG_MOUSE */
1566 static void
1567 handle_statusbar(void) {
1568 char *p;
1569 int r;
1570 switch (r = read(bar.fd, bar.text, sizeof bar.text - 1)) {
1571 case -1:
1572 strncpy(bar.text, strerror(errno), sizeof bar.text - 1);
1573 bar.text[sizeof bar.text - 1] = '\0';
1574 bar.fd = -1;
1575 break;
1576 case 0:
1577 bar.fd = -1;
1578 break;
1579 default:
1580 bar.text[r] = '\0';
1581 p = bar.text + r - 1;
1582 for (; p >= bar.text && *p == '\n'; *p-- = '\0');
1583 for (; p >= bar.text && *p != '\n'; --p);
1584 if (p >= bar.text)
1585 memmove(bar.text, p + 1, strlen(p));
1586 drawbar();
1590 static void
1591 handle_editor(Client *c) {
1592 if (!copyreg.data && (copyreg.data = malloc(screen.history)))
1593 copyreg.size = screen.history;
1594 copyreg.len = 0;
1595 while (c->editor_fds[1] != -1 && copyreg.len < copyreg.size) {
1596 ssize_t len = read(c->editor_fds[1], copyreg.data + copyreg.len, copyreg.size - copyreg.len);
1597 if (len == -1) {
1598 if (errno == EINTR)
1599 continue;
1600 break;
1602 if (len == 0)
1603 break;
1604 copyreg.len += len;
1605 if (copyreg.len == copyreg.size) {
1606 copyreg.size *= 2;
1607 if (!(copyreg.data = realloc(copyreg.data, copyreg.size))) {
1608 copyreg.size = 0;
1609 copyreg.len = 0;
1613 c->editor_died = false;
1614 c->editor_fds[1] = -1;
1615 vt_destroy(c->editor);
1616 c->editor = NULL;
1617 c->term = c->app;
1618 vt_dirty(c->term);
1619 draw_content(c);
1620 wnoutrefresh(c->window);
1623 static int
1624 open_or_create_fifo(const char *name, const char **name_created) {
1625 struct stat info;
1626 int fd;
1628 do {
1629 if ((fd = open(name, O_RDWR|O_NONBLOCK)) == -1) {
1630 if (errno == ENOENT && !mkfifo(name, S_IRUSR|S_IWUSR)) {
1631 *name_created = name;
1632 continue;
1634 error("%s\n", strerror(errno));
1636 } while (fd == -1);
1638 if (fstat(fd, &info) == -1)
1639 error("%s\n", strerror(errno));
1640 if (!S_ISFIFO(info.st_mode))
1641 error("%s is not a named pipe\n", name);
1642 return fd;
1645 static void
1646 usage(void) {
1647 cleanup();
1648 eprint("usage: dvtm [-v] [-M] [-m mod] [-d delay] [-h lines] [-t title] "
1649 "[-s status-fifo] [-c cmd-fifo] [cmd...]\n");
1650 exit(EXIT_FAILURE);
1653 static bool
1654 parse_args(int argc, char *argv[]) {
1655 bool init = false;
1656 const char *name = argv[0];
1658 if (name && (name = strrchr(name, '/')))
1659 dvtm_name = name + 1;
1660 if (!getenv("ESCDELAY"))
1661 set_escdelay(100);
1662 for (int arg = 1; arg < argc; arg++) {
1663 if (argv[arg][0] != '-') {
1664 const char *args[] = { argv[arg], NULL, NULL };
1665 if (!init) {
1666 setup();
1667 init = true;
1669 create(args);
1670 continue;
1672 if (argv[arg][1] != 'v' && argv[arg][1] != 'M' && (arg + 1) >= argc)
1673 usage();
1674 switch (argv[arg][1]) {
1675 case 'v':
1676 puts("dvtm-"VERSION" © 2007-2015 Marc André Tanner");
1677 exit(EXIT_SUCCESS);
1678 case 'M':
1679 mouse_events_enabled = !mouse_events_enabled;
1680 break;
1681 case 'm': {
1682 char *mod = argv[++arg];
1683 if (mod[0] == '^' && mod[1])
1684 *mod = CTRL(mod[1]);
1685 for (unsigned int b = 0; b < LENGTH(bindings); b++)
1686 if (bindings[b].keys[0] == MOD)
1687 bindings[b].keys[0] = *mod;
1688 break;
1690 case 'd':
1691 set_escdelay(atoi(argv[++arg]));
1692 if (ESCDELAY < 50)
1693 set_escdelay(50);
1694 else if (ESCDELAY > 1000)
1695 set_escdelay(1000);
1696 break;
1697 case 'h':
1698 screen.history = atoi(argv[++arg]);
1699 break;
1700 case 't':
1701 title = argv[++arg];
1702 break;
1703 case 's':
1704 bar.fd = open_or_create_fifo(argv[++arg], &bar.file);
1705 updatebarpos();
1706 break;
1707 case 'c': {
1708 const char *fifo;
1709 cmdfifo.fd = open_or_create_fifo(argv[++arg], &cmdfifo.file);
1710 if (!(fifo = realpath(argv[arg], NULL)))
1711 error("%s\n", strerror(errno));
1712 setenv("DVTM_CMD_FIFO", fifo, 1);
1713 break;
1715 default:
1716 usage();
1719 return init;
1723 main(int argc, char *argv[]) {
1724 KeyCombo keys;
1725 unsigned int key_index = 0;
1726 memset(keys, 0, sizeof(keys));
1727 sigset_t emptyset, blockset;
1729 setenv("DVTM", VERSION, 1);
1730 if (!parse_args(argc, argv)) {
1731 setup();
1732 startup(NULL);
1735 sigemptyset(&emptyset);
1736 sigemptyset(&blockset);
1737 sigaddset(&blockset, SIGWINCH);
1738 sigaddset(&blockset, SIGCHLD);
1739 sigprocmask(SIG_BLOCK, &blockset, NULL);
1741 while (running) {
1742 int r, nfds = 0;
1743 fd_set rd;
1745 if (screen.need_resize) {
1746 resize_screen();
1747 screen.need_resize = false;
1750 FD_ZERO(&rd);
1751 FD_SET(STDIN_FILENO, &rd);
1753 if (cmdfifo.fd != -1) {
1754 FD_SET(cmdfifo.fd, &rd);
1755 nfds = cmdfifo.fd;
1758 if (bar.fd != -1) {
1759 FD_SET(bar.fd, &rd);
1760 nfds = MAX(nfds, bar.fd);
1763 for (Client *c = clients; c; ) {
1764 if (c->editor && c->editor_died)
1765 handle_editor(c);
1766 if (!c->editor && c->died) {
1767 Client *t = c->next;
1768 destroy(c);
1769 c = t;
1770 continue;
1772 int pty = c->editor ? vt_pty_get(c->editor) : vt_pty_get(c->app);
1773 FD_SET(pty, &rd);
1774 nfds = MAX(nfds, pty);
1775 c = c->next;
1778 doupdate();
1779 r = pselect(nfds + 1, &rd, NULL, NULL, NULL, &emptyset);
1781 if (r == -1 && errno == EINTR)
1782 continue;
1784 if (r < 0) {
1785 perror("select()");
1786 exit(EXIT_FAILURE);
1789 if (FD_ISSET(STDIN_FILENO, &rd)) {
1790 int code = getch();
1791 if (code >= 0) {
1792 keys[key_index++] = code;
1793 KeyBinding *binding = NULL;
1794 if (code == KEY_MOUSE) {
1795 key_index = 0;
1796 handle_mouse();
1797 } else if ((binding = keybinding(keys))) {
1798 unsigned int key_length = 0;
1799 while (key_length < MAX_KEYS && binding->keys[key_length])
1800 key_length++;
1801 if (key_index == key_length) {
1802 binding->action.cmd(binding->action.args);
1803 key_index = 0;
1804 memset(keys, 0, sizeof(keys));
1806 } else {
1807 key_index = 0;
1808 memset(keys, 0, sizeof(keys));
1809 keypress(code);
1812 if (r == 1) /* no data available on pty's */
1813 continue;
1816 if (cmdfifo.fd != -1 && FD_ISSET(cmdfifo.fd, &rd))
1817 handle_cmdfifo();
1819 if (bar.fd != -1 && FD_ISSET(bar.fd, &rd))
1820 handle_statusbar();
1822 for (Client *c = clients; c; c = c->next) {
1823 if (FD_ISSET(vt_pty_get(c->term), &rd)) {
1824 if (vt_process(c->term) < 0 && errno == EIO) {
1825 if (c->editor)
1826 c->editor_died = true;
1827 else
1828 c->died = true;
1829 continue;
1833 if (c != sel && is_content_visible(c)) {
1834 draw_content(c);
1835 wnoutrefresh(c->window);
1839 if (is_content_visible(sel)) {
1840 draw_content(sel);
1841 curs_set(vt_cursor_visible(sel->term));
1842 wnoutrefresh(sel->window);
1846 cleanup();
1847 return 0;