Simplify if statements
[dvtm.git] / dvtm.c
blob6989babd665c7a0b47b39c98da93c25ce3112889
1 /*
2 * The initial "port" of dwm to curses was done by
4 * © 2007-2016 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 8
112 typedef struct {
113 void (*cmd)(const char *args[]);
114 const char *args[3];
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 focusid(const char *args[]);
183 static void focusnext(const char *args[]);
184 static void focusnextnm(const char *args[]);
185 static void focusprev(const char *args[]);
186 static void focusprevnm(const char *args[]);
187 static void focuslast(const char *args[]);
188 static void killclient(const char *args[]);
189 static void paste(const char *args[]);
190 static void quit(const char *args[]);
191 static void redraw(const char *args[]);
192 static void scrollback(const char *args[]);
193 static void send(const char *args[]);
194 static void setlayout(const char *args[]);
195 static void incnmaster(const char *args[]);
196 static void setmfact(const char *args[]);
197 static void startup(const char *args[]);
198 static void tag(const char *args[]);
199 static void tagid(const char *args[]);
200 static void togglebar(const char *args[]);
201 static void togglebarpos(const char *args[]);
202 static void toggleminimize(const char *args[]);
203 static void togglemouse(const char *args[]);
204 static void togglerunall(const char *args[]);
205 static void toggletag(const char *args[]);
206 static void toggleview(const char *args[]);
207 static void viewprevtag(const char *args[]);
208 static void view(const char *args[]);
209 static void zoom(const char *args[]);
211 /* commands for use by mouse bindings */
212 static void mouse_focus(const char *args[]);
213 static void mouse_fullscreen(const char *args[]);
214 static void mouse_minimize(const char *args[]);
215 static void mouse_zoom(const char *args[]);
217 /* functions and variables available to layouts via config.h */
218 static Client* nextvisible(Client *c);
219 static void focus(Client *c);
220 static void resize(Client *c, int x, int y, int w, int h);
221 extern Screen screen;
222 static unsigned int waw, wah, wax, way;
223 static Client *clients = NULL;
224 static char *title;
226 #include "config.h"
228 /* global variables */
229 static const char *dvtm_name = "dvtm";
230 Screen screen = { .mfact = MFACT, .nmaster = NMASTER, .history = SCROLL_HISTORY };
231 static Client *stack = NULL;
232 static Client *sel = NULL;
233 static Client *lastsel = NULL;
234 static Client *msel = NULL;
235 static unsigned int seltags;
236 static unsigned int tagset[2] = { 1, 1 };
237 static bool mouse_events_enabled = ENABLE_MOUSE;
238 static Layout *layout = layouts;
239 static StatusBar bar = { .fd = -1, .lastpos = BAR_POS, .pos = BAR_POS, .autohide = BAR_AUTOHIDE, .h = 1 };
240 static CmdFifo cmdfifo = { .fd = -1 };
241 static const char *shell;
242 static Register copyreg;
243 static volatile sig_atomic_t running = true;
244 static bool runinall = false;
246 static void
247 eprint(const char *errstr, ...) {
248 va_list ap;
249 va_start(ap, errstr);
250 vfprintf(stderr, errstr, ap);
251 va_end(ap);
254 static void
255 error(const char *errstr, ...) {
256 va_list ap;
257 va_start(ap, errstr);
258 vfprintf(stderr, errstr, ap);
259 va_end(ap);
260 exit(EXIT_FAILURE);
263 static bool
264 isarrange(void (*func)()) {
265 return func == layout->arrange;
268 static bool
269 isvisible(Client *c) {
270 return c->tags & tagset[seltags];
273 static bool
274 is_content_visible(Client *c) {
275 if (!c)
276 return false;
277 if (isarrange(fullscreen))
278 return sel == c;
279 return isvisible(c) && !c->minimized;
282 static Client*
283 nextvisible(Client *c) {
284 for (; c && !isvisible(c); c = c->next);
285 return c;
288 static void
289 updatebarpos(void) {
290 bar.y = 0;
291 wax = 0;
292 way = 0;
293 wah = screen.h;
294 waw = screen.w;
295 if (bar.pos == BAR_TOP) {
296 wah -= bar.h;
297 way += bar.h;
298 } else if (bar.pos == BAR_BOTTOM) {
299 wah -= bar.h;
300 bar.y = wah;
304 static void
305 hidebar(void) {
306 if (bar.pos != BAR_OFF) {
307 bar.lastpos = bar.pos;
308 bar.pos = BAR_OFF;
312 static void
313 showbar(void) {
314 if (bar.pos == BAR_OFF)
315 bar.pos = bar.lastpos;
318 static void
319 drawbar(void) {
320 int sx, sy, x, y, width;
321 unsigned int occupied = 0, urgent = 0;
322 if (bar.pos == BAR_OFF)
323 return;
325 for (Client *c = clients; c; c = c->next) {
326 occupied |= c->tags;
327 if (c->urgent)
328 urgent |= c->tags;
331 getyx(stdscr, sy, sx);
332 attrset(BAR_ATTR);
333 move(bar.y, 0);
335 for (unsigned int i = 0; i < LENGTH(tags); i++){
336 if (tagset[seltags] & (1 << i))
337 attrset(TAG_SEL);
338 else if (urgent & (1 << i))
339 attrset(TAG_URGENT);
340 else if (occupied & (1 << i))
341 attrset(TAG_OCCUPIED);
342 else
343 attrset(TAG_NORMAL);
344 printw(TAG_SYMBOL, tags[i]);
347 attrset(runinall ? TAG_SEL : TAG_NORMAL);
348 addstr(layout->symbol);
349 attrset(TAG_NORMAL);
351 getyx(stdscr, y, x);
352 (void)y;
353 int maxwidth = screen.w - x - 2;
355 addch(BAR_BEGIN);
356 attrset(BAR_ATTR);
358 wchar_t wbuf[sizeof bar.text];
359 size_t numchars = mbstowcs(wbuf, bar.text, sizeof bar.text);
361 if (numchars != (size_t)-1 && (width = wcswidth(wbuf, maxwidth)) != -1) {
362 int pos;
363 for (pos = 0; pos + width < maxwidth; pos++)
364 addch(' ');
366 for (size_t i = 0; i < numchars; i++) {
367 pos += wcwidth(wbuf[i]);
368 if (pos > maxwidth)
369 break;
370 addnwstr(wbuf+i, 1);
373 clrtoeol();
376 attrset(TAG_NORMAL);
377 mvaddch(bar.y, screen.w - 1, BAR_END);
378 attrset(NORMAL_ATTR);
379 move(sy, sx);
380 wnoutrefresh(stdscr);
383 static int
384 show_border(void) {
385 return (bar.pos != BAR_OFF) || (clients && clients->next);
388 static void
389 draw_border(Client *c) {
390 char t = '\0';
391 int x, y, maxlen, attrs = NORMAL_ATTR;
393 if (!show_border())
394 return;
395 if (sel != c && c->urgent)
396 attrs = URGENT_ATTR;
397 if (sel == c || (runinall && !c->minimized))
398 attrs = SELECTED_ATTR;
400 wattrset(c->window, attrs);
401 getyx(c->window, y, x);
402 mvwhline(c->window, 0, 0, ACS_HLINE, c->w);
403 maxlen = c->w - 10;
404 if (maxlen < 0)
405 maxlen = 0;
406 if ((size_t)maxlen < sizeof(c->title)) {
407 t = c->title[maxlen];
408 c->title[maxlen] = '\0';
411 mvwprintw(c->window, 0, 2, "[%s%s#%d]",
412 *c->title ? c->title : "",
413 *c->title ? " | " : "",
414 c->order);
415 if (t)
416 c->title[maxlen] = t;
417 wmove(c->window, y, x);
420 static void
421 draw_content(Client *c) {
422 vt_draw(c->term, c->window, c->has_title_line, 0);
425 static void
426 draw(Client *c) {
427 if (is_content_visible(c)) {
428 redrawwin(c->window);
429 draw_content(c);
431 if (!isarrange(fullscreen) || sel == c)
432 draw_border(c);
433 wnoutrefresh(c->window);
436 static void
437 draw_all(void) {
438 if (!nextvisible(clients)) {
439 sel = NULL;
440 curs_set(0);
441 erase();
442 drawbar();
443 doupdate();
444 return;
447 if (!isarrange(fullscreen)) {
448 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
449 if (c != sel)
450 draw(c);
453 /* as a last step the selected window is redrawn,
454 * this has the effect that the cursor position is
455 * accurate
457 if (sel)
458 draw(sel);
461 static void
462 arrange(void) {
463 unsigned int m = 0, n = 0;
464 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
465 c->order = ++n;
466 if (c->minimized)
467 m++;
469 erase();
470 attrset(NORMAL_ATTR);
471 if (bar.fd == -1 && bar.autohide) {
472 if ((!clients || !clients->next) && n == 1)
473 hidebar();
474 else
475 showbar();
476 updatebarpos();
478 if (m && !isarrange(fullscreen))
479 wah--;
480 layout->arrange();
481 if (m && !isarrange(fullscreen)) {
482 unsigned int i = 0, nw = waw / m, nx = wax;
483 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
484 if (c->minimized) {
485 resize(c, nx, way+wah, ++i == m ? waw - nx : nw, 1);
486 nx += nw;
489 wah++;
491 focus(NULL);
492 wnoutrefresh(stdscr);
493 drawbar();
494 draw_all();
497 static void
498 attach(Client *c) {
499 if (clients)
500 clients->prev = c;
501 c->next = clients;
502 c->prev = NULL;
503 clients = c;
504 for (int o = 1; c; c = nextvisible(c->next), o++)
505 c->order = o;
508 static void
509 attachafter(Client *c, Client *a) { /* attach c after a */
510 if (c == a)
511 return;
512 if (!a)
513 for (a = clients; a && a->next; a = a->next);
515 if (a) {
516 if (a->next)
517 a->next->prev = c;
518 c->next = a->next;
519 c->prev = a;
520 a->next = c;
521 for (int o = a->order; c; c = nextvisible(c->next))
522 c->order = ++o;
526 static void
527 attachstack(Client *c) {
528 c->snext = stack;
529 stack = c;
532 static void
533 detach(Client *c) {
534 Client *d;
535 if (c->prev)
536 c->prev->next = c->next;
537 if (c->next) {
538 c->next->prev = c->prev;
539 for (d = nextvisible(c->next); d; d = nextvisible(d->next))
540 --d->order;
542 if (c == clients)
543 clients = c->next;
544 c->next = c->prev = NULL;
547 static void
548 settitle(Client *c) {
549 char *term, *t = title;
550 if (!t && sel == c && *c->title)
551 t = c->title;
552 if (t && (term = getenv("TERM")) && !strstr(term, "linux")) {
553 printf("\033]0;%s\007", t);
554 fflush(stdout);
558 static void
559 detachstack(Client *c) {
560 Client **tc;
561 for (tc = &stack; *tc && *tc != c; tc = &(*tc)->snext);
562 *tc = c->snext;
565 static void
566 focus(Client *c) {
567 if (!c)
568 for (c = stack; c && !isvisible(c); c = c->snext);
569 if (sel == c)
570 return;
571 lastsel = sel;
572 sel = c;
573 if (lastsel) {
574 lastsel->urgent = false;
575 if (!isarrange(fullscreen)) {
576 draw_border(lastsel);
577 wnoutrefresh(lastsel->window);
581 if (c) {
582 detachstack(c);
583 attachstack(c);
584 settitle(c);
585 c->urgent = false;
586 if (isarrange(fullscreen)) {
587 draw(c);
588 } else {
589 draw_border(c);
590 wnoutrefresh(c->window);
593 curs_set(c && !c->minimized && vt_cursor_visible(c->term));
596 static void
597 applycolorrules(Client *c) {
598 const ColorRule *r = colorrules;
599 short fg = r->color->fg, bg = r->color->bg;
600 attr_t attrs = r->attrs;
602 for (unsigned int i = 1; i < LENGTH(colorrules); i++) {
603 r = &colorrules[i];
604 if (strstr(c->title, r->title)) {
605 attrs = r->attrs;
606 fg = r->color->fg;
607 bg = r->color->bg;
608 break;
612 vt_default_colors_set(c->term, attrs, fg, bg);
615 static void
616 term_title_handler(Vt *term, const char *title) {
617 Client *c = (Client *)vt_data_get(term);
618 if (title)
619 strncpy(c->title, title, sizeof(c->title) - 1);
620 c->title[title ? sizeof(c->title) - 1 : 0] = '\0';
621 settitle(c);
622 if (!isarrange(fullscreen) || sel == c)
623 draw_border(c);
624 applycolorrules(c);
627 static void
628 term_urgent_handler(Vt *term) {
629 Client *c = (Client *)vt_data_get(term);
630 c->urgent = true;
631 printf("\a");
632 fflush(stdout);
633 drawbar();
634 if (!isarrange(fullscreen) && sel != c && isvisible(c))
635 draw_border(c);
638 static void
639 move_client(Client *c, int x, int y) {
640 if (c->x == x && c->y == y)
641 return;
642 debug("moving, x: %d y: %d\n", x, y);
643 if (mvwin(c->window, y, x) == ERR) {
644 eprint("error moving, x: %d y: %d\n", x, y);
645 } else {
646 c->x = x;
647 c->y = y;
651 static void
652 resize_client(Client *c, int w, int h) {
653 bool has_title_line = show_border();
654 bool resize_window = c->w != w || c->h != h;
655 if (resize_window) {
656 debug("resizing, w: %d h: %d\n", w, h);
657 if (wresize(c->window, h, w) == ERR) {
658 eprint("error resizing, w: %d h: %d\n", w, h);
659 } else {
660 c->w = w;
661 c->h = h;
664 if (resize_window || c->has_title_line != has_title_line) {
665 c->has_title_line = has_title_line;
666 vt_resize(c->app, h - has_title_line, w);
667 if (c->editor)
668 vt_resize(c->editor, h - has_title_line, w);
672 static void
673 resize(Client *c, int x, int y, int w, int h) {
674 resize_client(c, w, h);
675 move_client(c, x, y);
678 static Client*
679 get_client_by_coord(unsigned int x, unsigned int y) {
680 if (y < way || y >= wah)
681 return NULL;
682 if (isarrange(fullscreen))
683 return sel;
684 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
685 if (x >= c->x && x < c->x + c->w && y >= c->y && y < c->y + c->h) {
686 debug("mouse event, x: %d y: %d client: %d\n", x, y, c->order);
687 return c;
690 return NULL;
693 static void
694 sigchld_handler(int sig) {
695 int errsv = errno;
696 int status;
697 pid_t pid;
699 while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
700 if (pid == -1) {
701 if (errno == ECHILD) {
702 /* no more child processes */
703 break;
705 eprint("waitpid: %s\n", strerror(errno));
706 break;
709 debug("child with pid %d died\n", pid);
711 for (Client *c = clients; c; c = c->next) {
712 if (c->pid == pid) {
713 c->died = true;
714 break;
716 if (c->editor && vt_pid_get(c->editor) == pid) {
717 c->editor_died = true;
718 break;
723 errno = errsv;
726 static void
727 sigwinch_handler(int sig) {
728 screen.need_resize = true;
731 static void
732 sigterm_handler(int sig) {
733 running = false;
736 static void
737 resize_screen(void) {
738 struct winsize ws;
740 if (ioctl(0, TIOCGWINSZ, &ws) == -1) {
741 getmaxyx(stdscr, screen.h, screen.w);
742 } else {
743 screen.w = ws.ws_col;
744 screen.h = ws.ws_row;
747 debug("resize_screen(), w: %d h: %d\n", screen.w, screen.h);
749 resizeterm(screen.h, screen.w);
750 wresize(stdscr, screen.h, screen.w);
751 updatebarpos();
752 clear();
753 arrange();
756 static KeyBinding*
757 keybinding(KeyCombo keys, unsigned int keycount) {
758 for (unsigned int b = 0; b < LENGTH(bindings); b++) {
759 for (unsigned int k = 0; k < keycount; k++) {
760 if (keys[k] != bindings[b].keys[k])
761 break;
762 if (k == keycount - 1)
763 return &bindings[b];
766 return NULL;
769 static unsigned int
770 bitoftag(const char *tag) {
771 unsigned int i;
772 if (!tag)
773 return ~0;
774 for (i = 0; (i < LENGTH(tags)) && strcmp(tags[i], tag); i++);
775 return (i < LENGTH(tags)) ? (1 << i) : 0;
778 static void
779 tagschanged() {
780 bool allminimized = true;
781 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
782 if (!c->minimized) {
783 allminimized = false;
784 break;
787 if (allminimized && nextvisible(clients)) {
788 focus(NULL);
789 toggleminimize(NULL);
791 arrange();
794 static void
795 tag(const char *args[]) {
796 if (!sel)
797 return;
798 sel->tags = bitoftag(args[0]) & TAGMASK;
799 tagschanged();
802 static void
803 tagid(const char *args[]) {
804 if (!args[0] || !args[1])
805 return;
807 const int win_id = atoi(args[0]);
808 for (Client *c = clients; c; c = c->next) {
809 if (c->id == win_id) {
810 unsigned int ntags = c->tags;
811 for (unsigned int i = 1; i < MAX_ARGS && args[i]; i++) {
812 if (args[i][0] == '+')
813 ntags |= bitoftag(args[i]+1);
814 else if (args[i][0] == '-')
815 ntags &= ~bitoftag(args[i]+1);
816 else
817 ntags = bitoftag(args[i]);
819 ntags &= TAGMASK;
820 if (ntags) {
821 c->tags = ntags;
822 tagschanged();
824 return;
829 static void
830 toggletag(const char *args[]) {
831 if (!sel)
832 return;
833 unsigned int newtags = sel->tags ^ (bitoftag(args[0]) & TAGMASK);
834 if (newtags) {
835 sel->tags = newtags;
836 tagschanged();
840 static void
841 toggleview(const char *args[]) {
842 unsigned int newtagset = tagset[seltags] ^ (bitoftag(args[0]) & TAGMASK);
843 if (newtagset) {
844 tagset[seltags] = newtagset;
845 tagschanged();
849 static void
850 view(const char *args[]) {
851 unsigned int newtagset = bitoftag(args[0]) & TAGMASK;
852 if (tagset[seltags] != newtagset && newtagset) {
853 seltags ^= 1; /* toggle sel tagset */
854 tagset[seltags] = newtagset;
855 tagschanged();
859 static void
860 viewprevtag(const char *args[]) {
861 seltags ^= 1;
862 tagschanged();
865 static void
866 keypress(int code) {
867 unsigned int len = 1;
868 char buf[8] = { '\e' };
870 if (code == '\e') {
871 /* pass characters following escape to the underlying app */
872 nodelay(stdscr, TRUE);
873 for (int t; len < sizeof(buf) && (t = getch()) != ERR; len++)
874 buf[len] = t;
875 nodelay(stdscr, FALSE);
878 for (Client *c = runinall ? nextvisible(clients) : sel; c; c = nextvisible(c->next)) {
879 if (is_content_visible(c)) {
880 c->urgent = false;
881 if (code == '\e')
882 vt_write(c->term, buf, len);
883 else
884 vt_keypress(c->term, code);
886 if (!runinall)
887 break;
891 static void
892 mouse_setup(void) {
893 #ifdef CONFIG_MOUSE
894 mmask_t mask = 0;
896 if (mouse_events_enabled) {
897 mask = BUTTON1_CLICKED | BUTTON2_CLICKED;
898 for (unsigned int i = 0; i < LENGTH(buttons); i++)
899 mask |= buttons[i].mask;
901 mousemask(mask, NULL);
902 #endif /* CONFIG_MOUSE */
905 static bool
906 checkshell(const char *shell) {
907 if (shell == NULL || *shell == '\0' || *shell != '/')
908 return false;
909 if (!strcmp(strrchr(shell, '/')+1, dvtm_name))
910 return false;
911 if (access(shell, X_OK))
912 return false;
913 return true;
916 static const char *
917 getshell(void) {
918 const char *shell = getenv("SHELL");
919 struct passwd *pw;
921 if (checkshell(shell))
922 return shell;
923 if ((pw = getpwuid(getuid())) && checkshell(pw->pw_shell))
924 return pw->pw_shell;
925 return "/bin/sh";
928 static void
929 setup(void) {
930 shell = getshell();
931 setlocale(LC_CTYPE, "");
932 initscr();
933 start_color();
934 noecho();
935 nonl();
936 keypad(stdscr, TRUE);
937 mouse_setup();
938 raw();
939 vt_init();
940 vt_keytable_set(keytable, LENGTH(keytable));
941 for (unsigned int i = 0; i < LENGTH(colors); i++) {
942 if (COLORS == 256) {
943 if (colors[i].fg256)
944 colors[i].fg = colors[i].fg256;
945 if (colors[i].bg256)
946 colors[i].bg = colors[i].bg256;
948 colors[i].pair = vt_color_reserve(colors[i].fg, colors[i].bg);
950 resize_screen();
951 struct sigaction sa;
952 memset(&sa, 0, sizeof sa);
953 sa.sa_flags = 0;
954 sigemptyset(&sa.sa_mask);
955 sa.sa_handler = sigwinch_handler;
956 sigaction(SIGWINCH, &sa, NULL);
957 sa.sa_handler = sigchld_handler;
958 sigaction(SIGCHLD, &sa, NULL);
959 sa.sa_handler = sigterm_handler;
960 sigaction(SIGTERM, &sa, NULL);
961 sa.sa_handler = SIG_IGN;
962 sigaction(SIGPIPE, &sa, NULL);
965 static void
966 destroy(Client *c) {
967 if (sel == c)
968 focusnextnm(NULL);
969 detach(c);
970 detachstack(c);
971 if (sel == c) {
972 Client *next = nextvisible(clients);
973 if (next) {
974 focus(next);
975 toggleminimize(NULL);
976 } else {
977 sel = NULL;
980 if (lastsel == c)
981 lastsel = NULL;
982 werase(c->window);
983 wnoutrefresh(c->window);
984 vt_destroy(c->term);
985 delwin(c->window);
986 if (!clients && LENGTH(actions)) {
987 if (!strcmp(c->cmd, shell))
988 quit(NULL);
989 else
990 create(NULL);
992 free(c);
993 arrange();
996 static void
997 cleanup(void) {
998 while (clients)
999 destroy(clients);
1000 vt_shutdown();
1001 endwin();
1002 free(copyreg.data);
1003 if (bar.fd > 0)
1004 close(bar.fd);
1005 if (bar.file)
1006 unlink(bar.file);
1007 if (cmdfifo.fd > 0)
1008 close(cmdfifo.fd);
1009 if (cmdfifo.file)
1010 unlink(cmdfifo.file);
1013 static char *getcwd_by_pid(Client *c) {
1014 if (!c)
1015 return NULL;
1016 char buf[32];
1017 snprintf(buf, sizeof buf, "/proc/%d/cwd", c->pid);
1018 return realpath(buf, NULL);
1021 static void
1022 create(const char *args[]) {
1023 const char *pargs[4] = { shell, NULL };
1024 char buf[8], *cwd = NULL;
1025 const char *env[] = {
1026 "DVTM_WINDOW_ID", buf,
1027 NULL
1030 if (args && args[0]) {
1031 pargs[1] = "-c";
1032 pargs[2] = args[0];
1033 pargs[3] = NULL;
1035 Client *c = calloc(1, sizeof(Client));
1036 if (!c)
1037 return;
1038 c->tags = tagset[seltags];
1039 c->id = ++cmdfifo.id;
1040 snprintf(buf, sizeof buf, "%d", c->id);
1042 if (!(c->window = newwin(wah, waw, way, wax))) {
1043 free(c);
1044 return;
1047 c->term = c->app = vt_create(screen.h, screen.w, screen.history);
1048 if (!c->term) {
1049 delwin(c->window);
1050 free(c);
1051 return;
1054 c->cmd = (args && args[0]) ? args[0] : shell;
1055 if (args && args[1]) {
1056 strncpy(c->title, args[1], sizeof(c->title) - 1);
1057 c->title[sizeof(c->title) - 1] = '\0';
1059 if (args && args[2])
1060 cwd = !strcmp(args[2], "$CWD") ? getcwd_by_pid(sel) : (char*)args[2];
1061 c->pid = vt_forkpty(c->term, shell, pargs, cwd, env, NULL, NULL);
1062 if (args && args[2] && !strcmp(args[2], "$CWD"))
1063 free(cwd);
1064 vt_data_set(c->term, c);
1065 vt_title_handler_set(c->term, term_title_handler);
1066 vt_urgent_handler_set(c->term, term_urgent_handler);
1067 c->x = wax;
1068 c->y = way;
1069 debug("client with pid %d forked\n", c->pid);
1070 attach(c);
1071 focus(c);
1072 arrange();
1075 static void
1076 copymode(const char *args[]) {
1077 if (!sel || sel->editor)
1078 return;
1079 if (!(sel->editor = vt_create(sel->h - sel->has_title_line, sel->w, 0)))
1080 return;
1082 char *ed = getenv("DVTM_EDITOR");
1083 int *to = &sel->editor_fds[0], *from = NULL;
1084 sel->editor_fds[0] = sel->editor_fds[1] = -1;
1086 if (!ed)
1087 ed = getenv("EDITOR");
1088 if (!ed)
1089 ed = getenv("PAGER");
1090 if (!ed)
1091 ed = editors[0].name;
1093 const char **argv = (const char*[]){ ed, "-", NULL, NULL };
1094 char argline[32];
1095 bool colored = false;
1097 for (unsigned int i = 0; i < LENGTH(editors); i++) {
1098 if (!strcmp(editors[i].name, ed)) {
1099 for (int j = 1; editors[i].argv[j]; j++) {
1100 if (strstr(editors[i].argv[j], "%d")) {
1101 int line = vt_content_start(sel->app);
1102 snprintf(argline, sizeof(argline), "+%d", line);
1103 argv[j] = argline;
1104 } else {
1105 argv[j] = editors[i].argv[j];
1108 if (editors[i].filter)
1109 from = &sel->editor_fds[1];
1110 colored = editors[i].color;
1111 break;
1115 if (vt_forkpty(sel->editor, ed, argv, NULL, NULL, to, from) < 0) {
1116 vt_destroy(sel->editor);
1117 sel->editor = NULL;
1118 return;
1121 sel->term = sel->editor;
1123 if (sel->editor_fds[0] != -1) {
1124 char *buf = NULL;
1125 size_t len = vt_content_get(sel->app, &buf, colored);
1126 char *cur = buf;
1127 while (len > 0) {
1128 ssize_t res = write(sel->editor_fds[0], cur, len);
1129 if (res < 0) {
1130 if (errno == EAGAIN || errno == EINTR)
1131 continue;
1132 break;
1134 cur += res;
1135 len -= res;
1137 free(buf);
1138 close(sel->editor_fds[0]);
1139 sel->editor_fds[0] = -1;
1142 if (args[0])
1143 vt_write(sel->editor, args[0], strlen(args[0]));
1146 static void
1147 focusn(const char *args[]) {
1148 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
1149 if (c->order == atoi(args[0])) {
1150 focus(c);
1151 if (c->minimized)
1152 toggleminimize(NULL);
1153 return;
1158 static void
1159 focusid(const char *args[]) {
1160 if (!args[0])
1161 return;
1163 const int win_id = atoi(args[0]);
1164 for (Client *c = clients; c; c = c->next) {
1165 if (c->id == win_id) {
1166 focus(c);
1167 if (c->minimized)
1168 toggleminimize(NULL);
1169 if (!isvisible(c)) {
1170 c->tags |= tagset[seltags];
1171 tagschanged();
1173 return;
1178 static void
1179 focusnext(const char *args[]) {
1180 Client *c;
1181 if (!sel)
1182 return;
1183 for (c = sel->next; c && !isvisible(c); c = c->next);
1184 if (!c)
1185 for (c = clients; c && !isvisible(c); c = c->next);
1186 if (c)
1187 focus(c);
1190 static void
1191 focusnextnm(const char *args[]) {
1192 if (!sel)
1193 return;
1194 Client *c = sel;
1195 do {
1196 c = nextvisible(c->next);
1197 if (!c)
1198 c = nextvisible(clients);
1199 } while (c->minimized && c != sel);
1200 focus(c);
1203 static void
1204 focusprev(const char *args[]) {
1205 Client *c;
1206 if (!sel)
1207 return;
1208 for (c = sel->prev; c && !isvisible(c); c = c->prev);
1209 if (!c) {
1210 for (c = clients; c && c->next; c = c->next);
1211 for (; c && !isvisible(c); c = c->prev);
1213 if (c)
1214 focus(c);
1217 static void
1218 focusprevnm(const char *args[]) {
1219 if (!sel)
1220 return;
1221 Client *c = sel;
1222 do {
1223 for (c = c->prev; c && !isvisible(c); c = c->prev);
1224 if (!c) {
1225 for (c = clients; c && c->next; c = c->next);
1226 for (; c && !isvisible(c); c = c->prev);
1228 } while (c && c != sel && c->minimized);
1229 focus(c);
1232 static void
1233 focuslast(const char *args[]) {
1234 if (lastsel)
1235 focus(lastsel);
1238 static void
1239 killclient(const char *args[]) {
1240 if (!sel)
1241 return;
1242 debug("killing client with pid: %d\n", sel->pid);
1243 kill(-sel->pid, SIGKILL);
1246 static void
1247 paste(const char *args[]) {
1248 if (sel && copyreg.data)
1249 vt_write(sel->term, copyreg.data, copyreg.len);
1252 static void
1253 quit(const char *args[]) {
1254 cleanup();
1255 exit(EXIT_SUCCESS);
1258 static void
1259 redraw(const char *args[]) {
1260 for (Client *c = clients; c; c = c->next) {
1261 if (!c->minimized) {
1262 vt_dirty(c->term);
1263 wclear(c->window);
1264 wnoutrefresh(c->window);
1267 resize_screen();
1270 static void
1271 scrollback(const char *args[]) {
1272 if (!is_content_visible(sel))
1273 return;
1275 if (!args[0] || atoi(args[0]) < 0)
1276 vt_scroll(sel->term, -sel->h/2);
1277 else
1278 vt_scroll(sel->term, sel->h/2);
1280 draw(sel);
1281 curs_set(vt_cursor_visible(sel->term));
1284 static void
1285 send(const char *args[]) {
1286 if (sel && args && args[0])
1287 vt_write(sel->term, args[0], strlen(args[0]));
1290 static void
1291 setlayout(const char *args[]) {
1292 unsigned int i;
1294 if (!args || !args[0]) {
1295 if (++layout == &layouts[LENGTH(layouts)])
1296 layout = &layouts[0];
1297 } else {
1298 for (i = 0; i < LENGTH(layouts); i++)
1299 if (!strcmp(args[0], layouts[i].symbol))
1300 break;
1301 if (i == LENGTH(layouts))
1302 return;
1303 layout = &layouts[i];
1305 arrange();
1308 static void
1309 incnmaster(const char *args[]) {
1310 int delta;
1312 if (isarrange(fullscreen) || isarrange(grid))
1313 return;
1314 /* arg handling, manipulate nmaster */
1315 if (args[0] == NULL) {
1316 screen.nmaster = NMASTER;
1317 } else if (sscanf(args[0], "%d", &delta) == 1) {
1318 if (args[0][0] == '+' || args[0][0] == '-')
1319 screen.nmaster += delta;
1320 else
1321 screen.nmaster = delta;
1322 if (screen.nmaster < 1)
1323 screen.nmaster = 1;
1325 arrange();
1328 static void
1329 setmfact(const char *args[]) {
1330 float delta;
1332 if (isarrange(fullscreen) || isarrange(grid))
1333 return;
1334 /* arg handling, manipulate mfact */
1335 if (args[0] == NULL) {
1336 screen.mfact = MFACT;
1337 } else if (sscanf(args[0], "%f", &delta) == 1) {
1338 if (args[0][0] == '+' || args[0][0] == '-')
1339 screen.mfact += delta;
1340 else
1341 screen.mfact = delta;
1342 if (screen.mfact < 0.1)
1343 screen.mfact = 0.1;
1344 else if (screen.mfact > 0.9)
1345 screen.mfact = 0.9;
1347 arrange();
1350 static void
1351 startup(const char *args[]) {
1352 for (unsigned int i = 0; i < LENGTH(actions); i++)
1353 actions[i].cmd(actions[i].args);
1356 static void
1357 togglebar(const char *args[]) {
1358 if (bar.pos == BAR_OFF)
1359 showbar();
1360 else
1361 hidebar();
1362 bar.autohide = false;
1363 updatebarpos();
1364 redraw(NULL);
1367 static void
1368 togglebarpos(const char *args[]) {
1369 switch (bar.pos == BAR_OFF ? bar.lastpos : bar.pos) {
1370 case BAR_TOP:
1371 bar.pos = BAR_BOTTOM;
1372 break;
1373 case BAR_BOTTOM:
1374 bar.pos = BAR_TOP;
1375 break;
1377 updatebarpos();
1378 redraw(NULL);
1381 static void
1382 toggleminimize(const char *args[]) {
1383 Client *c, *m, *t;
1384 unsigned int n;
1385 if (!sel)
1386 return;
1387 /* the last window can't be minimized */
1388 if (!sel->minimized) {
1389 for (n = 0, c = nextvisible(clients); c; c = nextvisible(c->next))
1390 if (!c->minimized)
1391 n++;
1392 if (n == 1)
1393 return;
1395 sel->minimized = !sel->minimized;
1396 m = sel;
1397 /* check whether the master client was minimized */
1398 if (sel == nextvisible(clients) && sel->minimized) {
1399 c = nextvisible(sel->next);
1400 detach(c);
1401 attach(c);
1402 focus(c);
1403 detach(m);
1404 for (; c && (t = nextvisible(c->next)) && !t->minimized; c = t);
1405 attachafter(m, c);
1406 } else if (m->minimized) {
1407 /* non master window got minimized move it above all other
1408 * minimized ones */
1409 focusnextnm(NULL);
1410 detach(m);
1411 for (c = nextvisible(clients); c && (t = nextvisible(c->next)) && !t->minimized; c = t);
1412 attachafter(m, c);
1413 } else { /* window is no longer minimized, move it to the master area */
1414 vt_dirty(m->term);
1415 detach(m);
1416 attach(m);
1418 arrange();
1421 static void
1422 togglemouse(const char *args[]) {
1423 mouse_events_enabled = !mouse_events_enabled;
1424 mouse_setup();
1427 static void
1428 togglerunall(const char *args[]) {
1429 runinall = !runinall;
1430 drawbar();
1431 draw_all();
1434 static void
1435 zoom(const char *args[]) {
1436 Client *c;
1438 if (!sel)
1439 return;
1440 if (args && args[0])
1441 focusn(args);
1442 if ((c = sel) == nextvisible(clients))
1443 if (!(c = nextvisible(c->next)))
1444 return;
1445 detach(c);
1446 attach(c);
1447 focus(c);
1448 if (c->minimized)
1449 toggleminimize(NULL);
1450 arrange();
1453 /* commands for use by mouse bindings */
1454 static void
1455 mouse_focus(const char *args[]) {
1456 focus(msel);
1457 if (msel->minimized)
1458 toggleminimize(NULL);
1461 static void
1462 mouse_fullscreen(const char *args[]) {
1463 mouse_focus(NULL);
1464 if (isarrange(fullscreen))
1465 setlayout(NULL);
1466 else
1467 setlayout(args);
1470 static void
1471 mouse_minimize(const char *args[]) {
1472 focus(msel);
1473 toggleminimize(NULL);
1476 static void
1477 mouse_zoom(const char *args[]) {
1478 focus(msel);
1479 zoom(NULL);
1482 static Cmd *
1483 get_cmd_by_name(const char *name) {
1484 for (unsigned int i = 0; i < LENGTH(commands); i++) {
1485 if (!strcmp(name, commands[i].name))
1486 return &commands[i];
1488 return NULL;
1491 static void
1492 handle_cmdfifo(void) {
1493 int r;
1494 char *p, *s, cmdbuf[512], c;
1495 Cmd *cmd;
1497 r = read(cmdfifo.fd, cmdbuf, sizeof cmdbuf - 1);
1498 if (r <= 0) {
1499 cmdfifo.fd = -1;
1500 return;
1503 cmdbuf[r] = '\0';
1504 p = cmdbuf;
1505 while (*p) {
1506 /* find the command name */
1507 for (; *p == ' ' || *p == '\n'; p++);
1508 for (s = p; *p && *p != ' ' && *p != '\n'; p++);
1509 if ((c = *p))
1510 *p++ = '\0';
1511 if (*s && (cmd = get_cmd_by_name(s)) != NULL) {
1512 bool quote = false;
1513 int argc = 0;
1514 const char *args[MAX_ARGS], *arg;
1515 memset(args, 0, sizeof(args));
1516 /* if arguments were specified in config.h ignore the one given via
1517 * the named pipe and thus skip everything until we find a new line
1519 if (cmd->action.args[0] || c == '\n') {
1520 debug("execute %s", s);
1521 cmd->action.cmd(cmd->action.args);
1522 while (*p && *p != '\n')
1523 p++;
1524 continue;
1526 /* no arguments were given in config.h so we parse the command line */
1527 while (*p == ' ')
1528 p++;
1529 arg = p;
1530 for (; (c = *p); p++) {
1531 switch (*p) {
1532 case '\\':
1533 /* remove the escape character '\\' move every
1534 * following character to the left by one position
1536 switch (p[1]) {
1537 case '\\':
1538 case '\'':
1539 case '\"': {
1540 char *t = p+1;
1541 do {
1542 t[-1] = *t;
1543 } while (*t++);
1546 break;
1547 case '\'':
1548 case '\"':
1549 quote = !quote;
1550 break;
1551 case ' ':
1552 if (!quote) {
1553 case '\n':
1554 /* remove trailing quote if there is one */
1555 if (*(p - 1) == '\'' || *(p - 1) == '\"')
1556 *(p - 1) = '\0';
1557 *p++ = '\0';
1558 /* remove leading quote if there is one */
1559 if (*arg == '\'' || *arg == '\"')
1560 arg++;
1561 if (argc < MAX_ARGS)
1562 args[argc++] = arg;
1564 while (*p == ' ')
1565 ++p;
1566 arg = p--;
1568 break;
1571 if (c == '\n' || *p == '\n') {
1572 if (!*p)
1573 p++;
1574 debug("execute %s", s);
1575 for(int i = 0; i < argc; i++)
1576 debug(" %s", args[i]);
1577 debug("\n");
1578 cmd->action.cmd(args);
1579 break;
1586 static void
1587 handle_mouse(void) {
1588 #ifdef CONFIG_MOUSE
1589 MEVENT event;
1590 unsigned int i;
1591 if (getmouse(&event) != OK)
1592 return;
1593 msel = get_client_by_coord(event.x, event.y);
1595 if (!msel)
1596 return;
1598 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);
1600 vt_mouse(msel->term, event.x - msel->x, event.y - msel->y, event.bstate);
1602 for (i = 0; i < LENGTH(buttons); i++) {
1603 if (event.bstate & buttons[i].mask)
1604 buttons[i].action.cmd(buttons[i].action.args);
1607 msel = NULL;
1608 #endif /* CONFIG_MOUSE */
1611 static void
1612 handle_statusbar(void) {
1613 char *p;
1614 int r;
1615 switch (r = read(bar.fd, bar.text, sizeof bar.text - 1)) {
1616 case -1:
1617 strncpy(bar.text, strerror(errno), sizeof bar.text - 1);
1618 bar.text[sizeof bar.text - 1] = '\0';
1619 bar.fd = -1;
1620 break;
1621 case 0:
1622 bar.fd = -1;
1623 break;
1624 default:
1625 bar.text[r] = '\0';
1626 p = bar.text + r - 1;
1627 for (; p >= bar.text && *p == '\n'; *p-- = '\0');
1628 for (; p >= bar.text && *p != '\n'; --p);
1629 if (p >= bar.text)
1630 memmove(bar.text, p + 1, strlen(p));
1631 drawbar();
1635 static void
1636 handle_editor(Client *c) {
1637 if (!copyreg.data && (copyreg.data = malloc(screen.history)))
1638 copyreg.size = screen.history;
1639 copyreg.len = 0;
1640 while (c->editor_fds[1] != -1 && copyreg.len < copyreg.size) {
1641 ssize_t len = read(c->editor_fds[1], copyreg.data + copyreg.len, copyreg.size - copyreg.len);
1642 if (len == -1) {
1643 if (errno == EINTR)
1644 continue;
1645 break;
1647 if (len == 0)
1648 break;
1649 copyreg.len += len;
1650 if (copyreg.len == copyreg.size) {
1651 copyreg.size *= 2;
1652 if (!(copyreg.data = realloc(copyreg.data, copyreg.size))) {
1653 copyreg.size = 0;
1654 copyreg.len = 0;
1658 c->editor_died = false;
1659 c->editor_fds[1] = -1;
1660 vt_destroy(c->editor);
1661 c->editor = NULL;
1662 c->term = c->app;
1663 vt_dirty(c->term);
1664 draw_content(c);
1665 wnoutrefresh(c->window);
1668 static int
1669 open_or_create_fifo(const char *name, const char **name_created) {
1670 struct stat info;
1671 int fd;
1673 do {
1674 if ((fd = open(name, O_RDWR|O_NONBLOCK)) == -1) {
1675 if (errno == ENOENT && !mkfifo(name, S_IRUSR|S_IWUSR)) {
1676 *name_created = name;
1677 continue;
1679 error("%s\n", strerror(errno));
1681 } while (fd == -1);
1683 if (fstat(fd, &info) == -1)
1684 error("%s\n", strerror(errno));
1685 if (!S_ISFIFO(info.st_mode))
1686 error("%s is not a named pipe\n", name);
1687 return fd;
1690 static void
1691 usage(void) {
1692 cleanup();
1693 eprint("usage: dvtm [-v] [-M] [-m mod] [-d delay] [-h lines] [-t title] "
1694 "[-s status-fifo] [-c cmd-fifo] [cmd...]\n");
1695 exit(EXIT_FAILURE);
1698 static bool
1699 parse_args(int argc, char *argv[]) {
1700 bool init = false;
1701 const char *name = argv[0];
1703 if (name && (name = strrchr(name, '/')))
1704 dvtm_name = name + 1;
1705 if (!getenv("ESCDELAY"))
1706 set_escdelay(100);
1707 for (int arg = 1; arg < argc; arg++) {
1708 if (argv[arg][0] != '-') {
1709 const char *args[] = { argv[arg], NULL, NULL };
1710 if (!init) {
1711 setup();
1712 init = true;
1714 create(args);
1715 continue;
1717 if (argv[arg][1] != 'v' && argv[arg][1] != 'M' && (arg + 1) >= argc)
1718 usage();
1719 switch (argv[arg][1]) {
1720 case 'v':
1721 puts("dvtm-"VERSION" © 2007-2016 Marc André Tanner");
1722 exit(EXIT_SUCCESS);
1723 case 'M':
1724 mouse_events_enabled = !mouse_events_enabled;
1725 break;
1726 case 'm': {
1727 char *mod = argv[++arg];
1728 if (mod[0] == '^' && mod[1])
1729 *mod = CTRL(mod[1]);
1730 for (unsigned int b = 0; b < LENGTH(bindings); b++)
1731 if (bindings[b].keys[0] == MOD)
1732 bindings[b].keys[0] = *mod;
1733 break;
1735 case 'd':
1736 set_escdelay(atoi(argv[++arg]));
1737 if (ESCDELAY < 50)
1738 set_escdelay(50);
1739 else if (ESCDELAY > 1000)
1740 set_escdelay(1000);
1741 break;
1742 case 'h':
1743 screen.history = atoi(argv[++arg]);
1744 break;
1745 case 't':
1746 title = argv[++arg];
1747 break;
1748 case 's':
1749 bar.fd = open_or_create_fifo(argv[++arg], &bar.file);
1750 updatebarpos();
1751 break;
1752 case 'c': {
1753 const char *fifo;
1754 cmdfifo.fd = open_or_create_fifo(argv[++arg], &cmdfifo.file);
1755 if (!(fifo = realpath(argv[arg], NULL)))
1756 error("%s\n", strerror(errno));
1757 setenv("DVTM_CMD_FIFO", fifo, 1);
1758 break;
1760 default:
1761 usage();
1764 return init;
1768 main(int argc, char *argv[]) {
1769 KeyCombo keys;
1770 unsigned int key_index = 0;
1771 memset(keys, 0, sizeof(keys));
1772 sigset_t emptyset, blockset;
1774 setenv("DVTM", VERSION, 1);
1775 if (!parse_args(argc, argv)) {
1776 setup();
1777 startup(NULL);
1780 sigemptyset(&emptyset);
1781 sigemptyset(&blockset);
1782 sigaddset(&blockset, SIGWINCH);
1783 sigaddset(&blockset, SIGCHLD);
1784 sigprocmask(SIG_BLOCK, &blockset, NULL);
1786 while (running) {
1787 int r, nfds = 0;
1788 fd_set rd;
1790 if (screen.need_resize) {
1791 resize_screen();
1792 screen.need_resize = false;
1795 FD_ZERO(&rd);
1796 FD_SET(STDIN_FILENO, &rd);
1798 if (cmdfifo.fd != -1) {
1799 FD_SET(cmdfifo.fd, &rd);
1800 nfds = cmdfifo.fd;
1803 if (bar.fd != -1) {
1804 FD_SET(bar.fd, &rd);
1805 nfds = MAX(nfds, bar.fd);
1808 for (Client *c = clients; c; ) {
1809 if (c->editor && c->editor_died)
1810 handle_editor(c);
1811 if (!c->editor && c->died) {
1812 Client *t = c->next;
1813 destroy(c);
1814 c = t;
1815 continue;
1817 int pty = c->editor ? vt_pty_get(c->editor) : vt_pty_get(c->app);
1818 FD_SET(pty, &rd);
1819 nfds = MAX(nfds, pty);
1820 c = c->next;
1823 doupdate();
1824 r = pselect(nfds + 1, &rd, NULL, NULL, NULL, &emptyset);
1826 if (r < 0) {
1827 if (errno == EINTR)
1828 continue;
1829 perror("select()");
1830 exit(EXIT_FAILURE);
1833 if (FD_ISSET(STDIN_FILENO, &rd)) {
1834 int code = getch();
1835 if (code >= 0) {
1836 keys[key_index++] = code;
1837 KeyBinding *binding = NULL;
1838 if (code == KEY_MOUSE) {
1839 key_index = 0;
1840 handle_mouse();
1841 } else if ((binding = keybinding(keys, key_index))) {
1842 unsigned int key_length = MAX_KEYS;
1843 while (key_length > 1 && !binding->keys[key_length-1])
1844 key_length--;
1845 if (key_index == key_length) {
1846 binding->action.cmd(binding->action.args);
1847 key_index = 0;
1848 memset(keys, 0, sizeof(keys));
1850 } else {
1851 key_index = 0;
1852 memset(keys, 0, sizeof(keys));
1853 keypress(code);
1856 if (r == 1) /* no data available on pty's */
1857 continue;
1860 if (cmdfifo.fd != -1 && FD_ISSET(cmdfifo.fd, &rd))
1861 handle_cmdfifo();
1863 if (bar.fd != -1 && FD_ISSET(bar.fd, &rd))
1864 handle_statusbar();
1866 for (Client *c = clients; c; c = c->next) {
1867 if (FD_ISSET(vt_pty_get(c->term), &rd)) {
1868 if (vt_process(c->term) < 0 && errno == EIO) {
1869 if (c->editor)
1870 c->editor_died = true;
1871 else
1872 c->died = true;
1873 continue;
1877 if (c != sel && is_content_visible(c)) {
1878 draw_content(c);
1879 wnoutrefresh(c->window);
1883 if (is_content_visible(sel)) {
1884 draw_content(sel);
1885 curs_set(vt_cursor_visible(sel->term));
1886 wnoutrefresh(sel->window);
1890 cleanup();
1891 return 0;