Remove Travis CI configuration
[dvtm.git] / dvtm.c
blob8382c83b9070194dd85aa976729f621d9203004d
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 <limits.h>
18 #include <libgen.h>
19 #include <sys/select.h>
20 #include <sys/stat.h>
21 #include <sys/ioctl.h>
22 #include <sys/wait.h>
23 #include <sys/time.h>
24 #include <sys/types.h>
25 #include <fcntl.h>
26 #include <curses.h>
27 #include <stdio.h>
28 #include <stdarg.h>
29 #include <signal.h>
30 #include <locale.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <stdbool.h>
34 #include <errno.h>
35 #include <pwd.h>
36 #if defined __CYGWIN__ || defined __sun
37 # include <termios.h>
38 #endif
39 #include "vt.h"
41 #ifdef PDCURSES
42 int ESCDELAY;
43 #endif
45 #ifndef NCURSES_REENTRANT
46 # define set_escdelay(d) (ESCDELAY = (d))
47 #endif
49 typedef struct {
50 float mfact;
51 unsigned int nmaster;
52 int history;
53 int w;
54 int h;
55 volatile sig_atomic_t need_resize;
56 } Screen;
58 typedef struct {
59 const char *symbol;
60 void (*arrange)(void);
61 } Layout;
63 typedef struct Client Client;
64 struct Client {
65 WINDOW *window;
66 Vt *term;
67 Vt *editor, *app;
68 int editor_fds[2];
69 volatile sig_atomic_t editor_died;
70 const char *cmd;
71 char title[255];
72 int order;
73 pid_t pid;
74 unsigned short int id;
75 unsigned short int x;
76 unsigned short int y;
77 unsigned short int w;
78 unsigned short int h;
79 bool has_title_line;
80 bool minimized;
81 bool urgent;
82 volatile sig_atomic_t died;
83 Client *next;
84 Client *prev;
85 Client *snext;
86 unsigned int tags;
89 typedef struct {
90 short fg;
91 short bg;
92 short fg256;
93 short bg256;
94 short pair;
95 } Color;
97 typedef struct {
98 const char *title;
99 attr_t attrs;
100 Color *color;
101 } ColorRule;
103 #define ALT(k) ((k) + (161 - 'a'))
104 #if defined CTRL && defined _AIX
105 #undef CTRL
106 #endif
107 #ifndef CTRL
108 #define CTRL(k) ((k) & 0x1F)
109 #endif
110 #define CTRL_ALT(k) ((k) + (129 - 'a'))
112 #define MAX_ARGS 8
114 typedef struct {
115 void (*cmd)(const char *args[]);
116 const char *args[3];
117 } Action;
119 #define MAX_KEYS 3
121 typedef unsigned int KeyCombo[MAX_KEYS];
123 typedef struct {
124 KeyCombo keys;
125 Action action;
126 } KeyBinding;
128 typedef struct {
129 mmask_t mask;
130 Action action;
131 } Button;
133 typedef struct {
134 const char *name;
135 Action action;
136 } Cmd;
138 enum { BAR_TOP, BAR_BOTTOM, BAR_OFF };
140 typedef struct {
141 int fd;
142 int pos, lastpos;
143 bool autohide;
144 unsigned short int h;
145 unsigned short int y;
146 char text[512];
147 const char *file;
148 } StatusBar;
150 typedef struct {
151 int fd;
152 const char *file;
153 unsigned short int id;
154 } CmdFifo;
156 typedef struct {
157 char *data;
158 size_t len;
159 size_t size;
160 } Register;
162 typedef struct {
163 char *name;
164 const char *argv[4];
165 bool filter;
166 bool color;
167 } Editor;
169 #define LENGTH(arr) (sizeof(arr) / sizeof((arr)[0]))
170 #define MAX(x, y) ((x) > (y) ? (x) : (y))
171 #define MIN(x, y) ((x) < (y) ? (x) : (y))
172 #define TAGMASK ((1 << LENGTH(tags)) - 1)
174 #ifdef NDEBUG
175 #define debug(format, args...)
176 #else
177 #define debug eprint
178 #endif
180 /* commands for use by keybindings */
181 static void create(const char *args[]);
182 static void copymode(const char *args[]);
183 static void focusn(const char *args[]);
184 static void focusid(const char *args[]);
185 static void focusnext(const char *args[]);
186 static void focusnextnm(const char *args[]);
187 static void focusprev(const char *args[]);
188 static void focusprevnm(const char *args[]);
189 static void focuslast(const char *args[]);
190 static void focusup(const char *args[]);
191 static void focusdown(const char *args[]);
192 static void focusleft(const char *args[]);
193 static void focusright(const char *args[]);
194 static void killclient(const char *args[]);
195 static void paste(const char *args[]);
196 static void quit(const char *args[]);
197 static void redraw(const char *args[]);
198 static void scrollback(const char *args[]);
199 static void send(const char *args[]);
200 static void setlayout(const char *args[]);
201 static void incnmaster(const char *args[]);
202 static void setmfact(const char *args[]);
203 static void startup(const char *args[]);
204 static void tag(const char *args[]);
205 static void tagid(const char *args[]);
206 static void togglebar(const char *args[]);
207 static void togglebarpos(const char *args[]);
208 static void toggleminimize(const char *args[]);
209 static void togglemouse(const char *args[]);
210 static void togglerunall(const char *args[]);
211 static void toggletag(const char *args[]);
212 static void toggleview(const char *args[]);
213 static void viewprevtag(const char *args[]);
214 static void view(const char *args[]);
215 static void zoom(const char *args[]);
217 /* commands for use by mouse bindings */
218 static void mouse_focus(const char *args[]);
219 static void mouse_fullscreen(const char *args[]);
220 static void mouse_minimize(const char *args[]);
221 static void mouse_zoom(const char *args[]);
223 /* functions and variables available to layouts via config.h */
224 static Client* nextvisible(Client *c);
225 static void focus(Client *c);
226 static void resize(Client *c, int x, int y, int w, int h);
227 extern Screen screen;
228 static unsigned int waw, wah, wax, way;
229 static Client *clients = NULL;
230 static char *title;
231 static KeyCombo keys;
233 #include "config.h"
235 /* global variables */
236 static const char *dvtm_name = "dvtm";
237 Screen screen = { .mfact = MFACT, .nmaster = NMASTER, .history = SCROLL_HISTORY };
238 static Client *stack = NULL;
239 static Client *sel = NULL;
240 static Client *lastsel = NULL;
241 static Client *msel = NULL;
242 static unsigned int seltags;
243 static unsigned int tagset[2] = { 1, 1 };
244 static bool mouse_events_enabled = ENABLE_MOUSE;
245 static Layout *layout = layouts;
246 static StatusBar bar = { .fd = -1, .lastpos = BAR_POS, .pos = BAR_POS, .autohide = BAR_AUTOHIDE, .h = 1 };
247 static CmdFifo cmdfifo = { .fd = -1 };
248 static const char *shell;
249 static Register copyreg;
250 static volatile sig_atomic_t running = true;
251 static bool runinall = false;
253 static void
254 eprint(const char *errstr, ...) {
255 va_list ap;
256 va_start(ap, errstr);
257 vfprintf(stderr, errstr, ap);
258 va_end(ap);
261 static void
262 error(const char *errstr, ...) {
263 va_list ap;
264 va_start(ap, errstr);
265 vfprintf(stderr, errstr, ap);
266 va_end(ap);
267 exit(EXIT_FAILURE);
270 static bool
271 isarrange(void (*func)()) {
272 return func == layout->arrange;
275 static bool
276 isvisible(Client *c) {
277 return c->tags & tagset[seltags];
280 static bool
281 is_content_visible(Client *c) {
282 if (!c)
283 return false;
284 if (isarrange(fullscreen))
285 return sel == c;
286 return isvisible(c) && !c->minimized;
289 static Client*
290 nextvisible(Client *c) {
291 for (; c && !isvisible(c); c = c->next);
292 return c;
295 static void
296 updatebarpos(void) {
297 bar.y = 0;
298 wax = 0;
299 way = 0;
300 wah = screen.h;
301 waw = screen.w;
302 if (bar.pos == BAR_TOP) {
303 wah -= bar.h;
304 way += bar.h;
305 } else if (bar.pos == BAR_BOTTOM) {
306 wah -= bar.h;
307 bar.y = wah;
311 static void
312 hidebar(void) {
313 if (bar.pos != BAR_OFF) {
314 bar.lastpos = bar.pos;
315 bar.pos = BAR_OFF;
319 static void
320 showbar(void) {
321 if (bar.pos == BAR_OFF)
322 bar.pos = bar.lastpos;
325 static void
326 drawbar(void) {
327 int sx, sy, x, y, width;
328 unsigned int occupied = 0, urgent = 0;
329 if (bar.pos == BAR_OFF)
330 return;
332 for (Client *c = clients; c; c = c->next) {
333 occupied |= c->tags;
334 if (c->urgent)
335 urgent |= c->tags;
338 getyx(stdscr, sy, sx);
339 attrset(BAR_ATTR);
340 move(bar.y, 0);
342 for (unsigned int i = 0; i < LENGTH(tags); i++){
343 if (tagset[seltags] & (1 << i))
344 attrset(TAG_SEL);
345 else if (urgent & (1 << i))
346 attrset(TAG_URGENT);
347 else if (occupied & (1 << i))
348 attrset(TAG_OCCUPIED);
349 else
350 attrset(TAG_NORMAL);
351 printw(TAG_SYMBOL, tags[i]);
354 attrset(runinall ? TAG_SEL : TAG_NORMAL);
355 addstr(layout->symbol);
356 attrset(TAG_NORMAL);
358 for (unsigned int i = 0; i < MAX_KEYS && keys[i]; i++) {
359 if (keys[i] < ' ')
360 printw("^%c", 'A' - 1 + keys[i]);
361 else
362 printw("%c", keys[i]);
365 getyx(stdscr, y, x);
366 (void)y;
367 int maxwidth = screen.w - x - 2;
369 addch(BAR_BEGIN);
370 attrset(BAR_ATTR);
372 wchar_t wbuf[sizeof bar.text];
373 size_t numchars = mbstowcs(wbuf, bar.text, sizeof bar.text);
375 if (numchars != (size_t)-1 && (width = wcswidth(wbuf, maxwidth)) != -1) {
376 int pos;
377 for (pos = 0; pos + width < maxwidth; pos++)
378 addch(' ');
380 for (size_t i = 0; i < numchars; i++) {
381 pos += wcwidth(wbuf[i]);
382 if (pos > maxwidth)
383 break;
384 addnwstr(wbuf+i, 1);
387 clrtoeol();
390 attrset(TAG_NORMAL);
391 mvaddch(bar.y, screen.w - 1, BAR_END);
392 attrset(NORMAL_ATTR);
393 move(sy, sx);
394 wnoutrefresh(stdscr);
397 static int
398 show_border(void) {
399 return (bar.pos != BAR_OFF) || (clients && clients->next);
402 static void
403 draw_border(Client *c) {
404 char t = '\0';
405 int x, y, maxlen, attrs = NORMAL_ATTR;
407 if (!show_border())
408 return;
409 if (sel != c && c->urgent)
410 attrs = URGENT_ATTR;
411 if (sel == c || (runinall && !c->minimized))
412 attrs = SELECTED_ATTR;
414 wattrset(c->window, attrs);
415 getyx(c->window, y, x);
416 mvwhline(c->window, 0, 0, ACS_HLINE, c->w);
417 maxlen = c->w - 10;
418 if (maxlen < 0)
419 maxlen = 0;
420 if ((size_t)maxlen < sizeof(c->title)) {
421 t = c->title[maxlen];
422 c->title[maxlen] = '\0';
425 mvwprintw(c->window, 0, 2, "[%s%s#%d]",
426 *c->title ? c->title : "",
427 *c->title ? " | " : "",
428 c->order);
429 if (t)
430 c->title[maxlen] = t;
431 wmove(c->window, y, x);
434 static void
435 draw_content(Client *c) {
436 vt_draw(c->term, c->window, c->has_title_line, 0);
439 static void
440 draw(Client *c) {
441 if (is_content_visible(c)) {
442 redrawwin(c->window);
443 draw_content(c);
445 if (!isarrange(fullscreen) || sel == c)
446 draw_border(c);
447 wnoutrefresh(c->window);
450 static void
451 draw_all(void) {
452 if (!nextvisible(clients)) {
453 sel = NULL;
454 curs_set(0);
455 erase();
456 drawbar();
457 doupdate();
458 return;
461 if (!isarrange(fullscreen)) {
462 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
463 if (c != sel)
464 draw(c);
467 /* as a last step the selected window is redrawn,
468 * this has the effect that the cursor position is
469 * accurate
471 if (sel)
472 draw(sel);
475 static void
476 arrange(void) {
477 unsigned int m = 0, n = 0;
478 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
479 c->order = ++n;
480 if (c->minimized)
481 m++;
483 erase();
484 attrset(NORMAL_ATTR);
485 if (bar.fd == -1 && bar.autohide) {
486 if ((!clients || !clients->next) && n == 1)
487 hidebar();
488 else
489 showbar();
490 updatebarpos();
492 if (m && !isarrange(fullscreen))
493 wah--;
494 layout->arrange();
495 if (m && !isarrange(fullscreen)) {
496 unsigned int i = 0, nw = waw / m, nx = wax;
497 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
498 if (c->minimized) {
499 resize(c, nx, way+wah, ++i == m ? waw - nx : nw, 1);
500 nx += nw;
503 wah++;
505 focus(NULL);
506 wnoutrefresh(stdscr);
507 drawbar();
508 draw_all();
511 static void
512 attach(Client *c) {
513 if (clients)
514 clients->prev = c;
515 c->next = clients;
516 c->prev = NULL;
517 clients = c;
518 for (int o = 1; c; c = nextvisible(c->next), o++)
519 c->order = o;
522 static void
523 attachafter(Client *c, Client *a) { /* attach c after a */
524 if (c == a)
525 return;
526 if (!a)
527 for (a = clients; a && a->next; a = a->next);
529 if (a) {
530 if (a->next)
531 a->next->prev = c;
532 c->next = a->next;
533 c->prev = a;
534 a->next = c;
535 for (int o = a->order; c; c = nextvisible(c->next))
536 c->order = ++o;
540 static void
541 attachstack(Client *c) {
542 c->snext = stack;
543 stack = c;
546 static void
547 detach(Client *c) {
548 Client *d;
549 if (c->prev)
550 c->prev->next = c->next;
551 if (c->next) {
552 c->next->prev = c->prev;
553 for (d = nextvisible(c->next); d; d = nextvisible(d->next))
554 --d->order;
556 if (c == clients)
557 clients = c->next;
558 c->next = c->prev = NULL;
561 static void
562 settitle(Client *c) {
563 char *term, *t = title;
564 if (!t && sel == c && *c->title)
565 t = c->title;
566 if (t && (term = getenv("TERM")) && !strstr(term, "linux")) {
567 printf("\033]0;%s\007", t);
568 fflush(stdout);
569 wnoutrefresh(c->window);
573 static void
574 detachstack(Client *c) {
575 Client **tc;
576 for (tc = &stack; *tc && *tc != c; tc = &(*tc)->snext);
577 *tc = c->snext;
580 static void
581 focus(Client *c) {
582 if (!c)
583 for (c = stack; c && !isvisible(c); c = c->snext);
584 if (sel == c)
585 return;
586 lastsel = sel;
587 sel = c;
588 if (lastsel) {
589 lastsel->urgent = false;
590 if (!isarrange(fullscreen)) {
591 draw_border(lastsel);
592 wnoutrefresh(lastsel->window);
596 if (c) {
597 detachstack(c);
598 attachstack(c);
599 settitle(c);
600 c->urgent = false;
601 if (isarrange(fullscreen)) {
602 draw(c);
603 } else {
604 draw_border(c);
605 wnoutrefresh(c->window);
608 curs_set(c && !c->minimized && vt_cursor_visible(c->term));
611 static void
612 applycolorrules(Client *c) {
613 const ColorRule *r = colorrules;
614 short fg = r->color->fg, bg = r->color->bg;
615 attr_t attrs = r->attrs;
617 for (unsigned int i = 1; i < LENGTH(colorrules); i++) {
618 r = &colorrules[i];
619 if (strstr(c->title, r->title)) {
620 attrs = r->attrs;
621 fg = r->color->fg;
622 bg = r->color->bg;
623 break;
627 vt_default_colors_set(c->term, attrs, fg, bg);
630 static void
631 term_title_handler(Vt *term, const char *title) {
632 Client *c = (Client *)vt_data_get(term);
633 if (title)
634 strncpy(c->title, title, sizeof(c->title) - 1);
635 c->title[title ? sizeof(c->title) - 1 : 0] = '\0';
636 settitle(c);
637 if (!isarrange(fullscreen) || sel == c)
638 draw_border(c);
639 applycolorrules(c);
642 static void
643 term_urgent_handler(Vt *term) {
644 Client *c = (Client *)vt_data_get(term);
645 c->urgent = true;
646 printf("\a");
647 fflush(stdout);
648 drawbar();
649 if (!isarrange(fullscreen) && sel != c && isvisible(c))
650 draw_border(c);
653 static void
654 move_client(Client *c, int x, int y) {
655 if (c->x == x && c->y == y)
656 return;
657 debug("moving, x: %d y: %d\n", x, y);
658 if (mvwin(c->window, y, x) == ERR) {
659 eprint("error moving, x: %d y: %d\n", x, y);
660 } else {
661 c->x = x;
662 c->y = y;
666 static void
667 resize_client(Client *c, int w, int h) {
668 bool has_title_line = show_border();
669 bool resize_window = c->w != w || c->h != h;
670 if (resize_window) {
671 debug("resizing, w: %d h: %d\n", w, h);
672 if (wresize(c->window, h, w) == ERR) {
673 eprint("error resizing, w: %d h: %d\n", w, h);
674 } else {
675 c->w = w;
676 c->h = h;
679 if (resize_window || c->has_title_line != has_title_line) {
680 c->has_title_line = has_title_line;
681 vt_resize(c->app, h - has_title_line, w);
682 if (c->editor)
683 vt_resize(c->editor, h - has_title_line, w);
687 static void
688 resize(Client *c, int x, int y, int w, int h) {
689 resize_client(c, w, h);
690 move_client(c, x, y);
693 static Client*
694 get_client_by_coord(unsigned int x, unsigned int y) {
695 if (y < way || y >= way+wah)
696 return NULL;
697 if (isarrange(fullscreen))
698 return sel;
699 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
700 if (x >= c->x && x < c->x + c->w && y >= c->y && y < c->y + c->h) {
701 debug("mouse event, x: %d y: %d client: %d\n", x, y, c->order);
702 return c;
705 return NULL;
708 static void
709 sigchld_handler(int sig) {
710 int errsv = errno;
711 int status;
712 pid_t pid;
714 while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
715 if (pid == -1) {
716 if (errno == ECHILD) {
717 /* no more child processes */
718 break;
720 eprint("waitpid: %s\n", strerror(errno));
721 break;
724 debug("child with pid %d died\n", pid);
726 for (Client *c = clients; c; c = c->next) {
727 if (c->pid == pid) {
728 c->died = true;
729 break;
731 if (c->editor && vt_pid_get(c->editor) == pid) {
732 c->editor_died = true;
733 break;
738 errno = errsv;
741 static void
742 sigwinch_handler(int sig) {
743 screen.need_resize = true;
746 static void
747 sigterm_handler(int sig) {
748 running = false;
751 static void
752 resize_screen(void) {
753 struct winsize ws;
755 if (ioctl(0, TIOCGWINSZ, &ws) == -1) {
756 getmaxyx(stdscr, screen.h, screen.w);
757 } else {
758 screen.w = ws.ws_col;
759 screen.h = ws.ws_row;
762 debug("resize_screen(), w: %d h: %d\n", screen.w, screen.h);
764 resizeterm(screen.h, screen.w);
765 wresize(stdscr, screen.h, screen.w);
766 updatebarpos();
767 clear();
768 arrange();
771 static KeyBinding*
772 keybinding(KeyCombo keys, unsigned int keycount) {
773 for (unsigned int b = 0; b < LENGTH(bindings); b++) {
774 for (unsigned int k = 0; k < keycount; k++) {
775 if (keys[k] != bindings[b].keys[k])
776 break;
777 if (k == keycount - 1)
778 return &bindings[b];
781 return NULL;
784 static unsigned int
785 bitoftag(const char *tag) {
786 unsigned int i;
787 if (!tag)
788 return ~0;
789 for (i = 0; (i < LENGTH(tags)) && strcmp(tags[i], tag); i++);
790 return (i < LENGTH(tags)) ? (1 << i) : 0;
793 static void
794 tagschanged() {
795 bool allminimized = true;
796 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
797 if (!c->minimized) {
798 allminimized = false;
799 break;
802 if (allminimized && nextvisible(clients)) {
803 focus(NULL);
804 toggleminimize(NULL);
806 arrange();
809 static void
810 tag(const char *args[]) {
811 if (!sel)
812 return;
813 sel->tags = bitoftag(args[0]) & TAGMASK;
814 tagschanged();
817 static void
818 tagid(const char *args[]) {
819 if (!args[0] || !args[1])
820 return;
822 const int win_id = atoi(args[0]);
823 for (Client *c = clients; c; c = c->next) {
824 if (c->id == win_id) {
825 unsigned int ntags = c->tags;
826 for (unsigned int i = 1; i < MAX_ARGS && args[i]; i++) {
827 if (args[i][0] == '+')
828 ntags |= bitoftag(args[i]+1);
829 else if (args[i][0] == '-')
830 ntags &= ~bitoftag(args[i]+1);
831 else
832 ntags = bitoftag(args[i]);
834 ntags &= TAGMASK;
835 if (ntags) {
836 c->tags = ntags;
837 tagschanged();
839 return;
844 static void
845 toggletag(const char *args[]) {
846 if (!sel)
847 return;
848 unsigned int newtags = sel->tags ^ (bitoftag(args[0]) & TAGMASK);
849 if (newtags) {
850 sel->tags = newtags;
851 tagschanged();
855 static void
856 toggleview(const char *args[]) {
857 unsigned int newtagset = tagset[seltags] ^ (bitoftag(args[0]) & TAGMASK);
858 if (newtagset) {
859 tagset[seltags] = newtagset;
860 tagschanged();
864 static void
865 view(const char *args[]) {
866 unsigned int newtagset = bitoftag(args[0]) & TAGMASK;
867 if (tagset[seltags] != newtagset && newtagset) {
868 seltags ^= 1; /* toggle sel tagset */
869 tagset[seltags] = newtagset;
870 tagschanged();
874 static void
875 viewprevtag(const char *args[]) {
876 seltags ^= 1;
877 tagschanged();
880 static void
881 keypress(int code) {
882 int key = -1;
883 unsigned int len = 1;
884 char buf[8] = { '\e' };
886 if (code == '\e') {
887 /* pass characters following escape to the underlying app */
888 nodelay(stdscr, TRUE);
889 for (int t; len < sizeof(buf) && (t = getch()) != ERR; len++) {
890 if (t > 255) {
891 key = t;
892 break;
894 buf[len] = t;
896 nodelay(stdscr, FALSE);
899 for (Client *c = runinall ? nextvisible(clients) : sel; c; c = nextvisible(c->next)) {
900 if (is_content_visible(c)) {
901 c->urgent = false;
902 if (code == '\e')
903 vt_write(c->term, buf, len);
904 else
905 vt_keypress(c->term, code);
906 if (key != -1)
907 vt_keypress(c->term, key);
909 if (!runinall)
910 break;
914 static void
915 mouse_setup(void) {
916 #ifdef CONFIG_MOUSE
917 mmask_t mask = 0;
919 if (mouse_events_enabled) {
920 mask = BUTTON1_CLICKED | BUTTON2_CLICKED;
921 for (unsigned int i = 0; i < LENGTH(buttons); i++)
922 mask |= buttons[i].mask;
924 mousemask(mask, NULL);
925 #endif /* CONFIG_MOUSE */
928 static bool
929 checkshell(const char *shell) {
930 if (shell == NULL || *shell == '\0' || *shell != '/')
931 return false;
932 if (!strcmp(strrchr(shell, '/')+1, dvtm_name))
933 return false;
934 if (access(shell, X_OK))
935 return false;
936 return true;
939 static const char *
940 getshell(void) {
941 const char *shell = getenv("SHELL");
942 struct passwd *pw;
944 if (checkshell(shell))
945 return shell;
946 if ((pw = getpwuid(getuid())) && checkshell(pw->pw_shell))
947 return pw->pw_shell;
948 return "/bin/sh";
951 static void
952 setup(void) {
953 shell = getshell();
954 setlocale(LC_CTYPE, "");
955 initscr();
956 start_color();
957 noecho();
958 nonl();
959 keypad(stdscr, TRUE);
960 mouse_setup();
961 raw();
962 vt_init();
963 vt_keytable_set(keytable, LENGTH(keytable));
964 for (unsigned int i = 0; i < LENGTH(colors); i++) {
965 if (COLORS == 256) {
966 if (colors[i].fg256)
967 colors[i].fg = colors[i].fg256;
968 if (colors[i].bg256)
969 colors[i].bg = colors[i].bg256;
971 colors[i].pair = vt_color_reserve(colors[i].fg, colors[i].bg);
973 resize_screen();
974 struct sigaction sa;
975 memset(&sa, 0, sizeof sa);
976 sa.sa_flags = 0;
977 sigemptyset(&sa.sa_mask);
978 sa.sa_handler = sigwinch_handler;
979 sigaction(SIGWINCH, &sa, NULL);
980 sa.sa_handler = sigchld_handler;
981 sigaction(SIGCHLD, &sa, NULL);
982 sa.sa_handler = sigterm_handler;
983 sigaction(SIGTERM, &sa, NULL);
984 sa.sa_handler = SIG_IGN;
985 sigaction(SIGPIPE, &sa, NULL);
988 static void
989 destroy(Client *c) {
990 if (sel == c)
991 focusnextnm(NULL);
992 detach(c);
993 detachstack(c);
994 if (sel == c) {
995 Client *next = nextvisible(clients);
996 if (next) {
997 focus(next);
998 toggleminimize(NULL);
999 } else {
1000 sel = NULL;
1003 if (lastsel == c)
1004 lastsel = NULL;
1005 werase(c->window);
1006 wnoutrefresh(c->window);
1007 vt_destroy(c->term);
1008 delwin(c->window);
1009 if (!clients && LENGTH(actions)) {
1010 if (!strcmp(c->cmd, shell))
1011 quit(NULL);
1012 else
1013 create(NULL);
1015 free(c);
1016 arrange();
1019 static void
1020 cleanup(void) {
1021 while (clients)
1022 destroy(clients);
1023 vt_shutdown();
1024 endwin();
1025 free(copyreg.data);
1026 if (bar.fd > 0)
1027 close(bar.fd);
1028 if (bar.file)
1029 unlink(bar.file);
1030 if (cmdfifo.fd > 0)
1031 close(cmdfifo.fd);
1032 if (cmdfifo.file)
1033 unlink(cmdfifo.file);
1036 static char *getcwd_by_pid(Client *c) {
1037 if (!c)
1038 return NULL;
1039 char buf[32];
1040 snprintf(buf, sizeof buf, "/proc/%d/cwd", c->pid);
1041 return realpath(buf, NULL);
1044 static void
1045 create(const char *args[]) {
1046 const char *pargs[4] = { shell, NULL };
1047 char buf[8], *cwd = NULL;
1048 const char *env[] = {
1049 "DVTM_WINDOW_ID", buf,
1050 NULL
1053 if (args && args[0]) {
1054 pargs[1] = "-c";
1055 pargs[2] = args[0];
1056 pargs[3] = NULL;
1058 Client *c = calloc(1, sizeof(Client));
1059 if (!c)
1060 return;
1061 c->tags = tagset[seltags];
1062 c->id = ++cmdfifo.id;
1063 snprintf(buf, sizeof buf, "%d", c->id);
1065 if (!(c->window = newwin(wah, waw, way, wax))) {
1066 free(c);
1067 return;
1070 c->term = c->app = vt_create(screen.h, screen.w, screen.history);
1071 if (!c->term) {
1072 delwin(c->window);
1073 free(c);
1074 return;
1077 if (args && args[0]) {
1078 c->cmd = args[0];
1079 char name[PATH_MAX];
1080 strncpy(name, args[0], sizeof(name));
1081 name[sizeof(name)-1] = '\0';
1082 strncpy(c->title, basename(name), sizeof(c->title));
1083 } else {
1084 c->cmd = shell;
1087 if (args && args[1])
1088 strncpy(c->title, args[1], sizeof(c->title));
1089 c->title[sizeof(c->title)-1] = '\0';
1091 if (args && args[2])
1092 cwd = !strcmp(args[2], "$CWD") ? getcwd_by_pid(sel) : (char*)args[2];
1093 c->pid = vt_forkpty(c->term, shell, pargs, cwd, env, NULL, NULL);
1094 if (args && args[2] && !strcmp(args[2], "$CWD"))
1095 free(cwd);
1096 vt_data_set(c->term, c);
1097 vt_title_handler_set(c->term, term_title_handler);
1098 vt_urgent_handler_set(c->term, term_urgent_handler);
1099 applycolorrules(c);
1100 c->x = wax;
1101 c->y = way;
1102 debug("client with pid %d forked\n", c->pid);
1103 attach(c);
1104 focus(c);
1105 arrange();
1108 static void
1109 copymode(const char *args[]) {
1110 if (!args || !args[0] || !sel || sel->editor)
1111 return;
1113 bool colored = strstr(args[0], "pager") != NULL;
1115 if (!(sel->editor = vt_create(sel->h - sel->has_title_line, sel->w, 0)))
1116 return;
1118 int *to = &sel->editor_fds[0];
1119 int *from = strstr(args[0], "editor") ? &sel->editor_fds[1] : NULL;
1120 sel->editor_fds[0] = sel->editor_fds[1] = -1;
1122 const char *argv[3] = { args[0], NULL, NULL };
1123 char argline[32];
1124 int line = vt_content_start(sel->app);
1125 snprintf(argline, sizeof(argline), "+%d", line);
1126 argv[1] = argline;
1128 char *cwd = getcwd_by_pid(sel);
1129 if (vt_forkpty(sel->editor, args[0], argv, cwd, NULL, to, from) < 0) {
1130 vt_destroy(sel->editor);
1131 sel->editor = NULL;
1132 return;
1135 sel->term = sel->editor;
1137 if (sel->editor_fds[0] != -1) {
1138 char *buf = NULL;
1139 size_t len = vt_content_get(sel->app, &buf, colored);
1140 char *cur = buf;
1141 while (len > 0) {
1142 ssize_t res = write(sel->editor_fds[0], cur, len);
1143 if (res < 0) {
1144 if (errno == EAGAIN || errno == EINTR)
1145 continue;
1146 break;
1148 cur += res;
1149 len -= res;
1151 free(buf);
1152 close(sel->editor_fds[0]);
1153 sel->editor_fds[0] = -1;
1156 if (args[1])
1157 vt_write(sel->editor, args[1], strlen(args[1]));
1160 static void
1161 focusn(const char *args[]) {
1162 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
1163 if (c->order == atoi(args[0])) {
1164 focus(c);
1165 if (c->minimized)
1166 toggleminimize(NULL);
1167 return;
1172 static void
1173 focusid(const char *args[]) {
1174 if (!args[0])
1175 return;
1177 const int win_id = atoi(args[0]);
1178 for (Client *c = clients; c; c = c->next) {
1179 if (c->id == win_id) {
1180 focus(c);
1181 if (c->minimized)
1182 toggleminimize(NULL);
1183 if (!isvisible(c)) {
1184 c->tags |= tagset[seltags];
1185 tagschanged();
1187 return;
1192 static void
1193 focusnext(const char *args[]) {
1194 Client *c;
1195 if (!sel)
1196 return;
1197 for (c = sel->next; c && !isvisible(c); c = c->next);
1198 if (!c)
1199 for (c = clients; c && !isvisible(c); c = c->next);
1200 if (c)
1201 focus(c);
1204 static void
1205 focusnextnm(const char *args[]) {
1206 if (!sel)
1207 return;
1208 Client *c = sel;
1209 do {
1210 c = nextvisible(c->next);
1211 if (!c)
1212 c = nextvisible(clients);
1213 } while (c->minimized && c != sel);
1214 focus(c);
1217 static void
1218 focusprev(const char *args[]) {
1219 Client *c;
1220 if (!sel)
1221 return;
1222 for (c = sel->prev; c && !isvisible(c); c = c->prev);
1223 if (!c) {
1224 for (c = clients; c && c->next; c = c->next);
1225 for (; c && !isvisible(c); c = c->prev);
1227 if (c)
1228 focus(c);
1231 static void
1232 focusprevnm(const char *args[]) {
1233 if (!sel)
1234 return;
1235 Client *c = sel;
1236 do {
1237 for (c = c->prev; c && !isvisible(c); c = c->prev);
1238 if (!c) {
1239 for (c = clients; c && c->next; c = c->next);
1240 for (; c && !isvisible(c); c = c->prev);
1242 } while (c && c != sel && c->minimized);
1243 focus(c);
1246 static void
1247 focuslast(const char *args[]) {
1248 if (lastsel)
1249 focus(lastsel);
1252 static void
1253 focusup(const char *args[]) {
1254 if (!sel)
1255 return;
1256 /* avoid vertical separator, hence +1 in x direction */
1257 Client *c = get_client_by_coord(sel->x + 1, sel->y - 1);
1258 if (c)
1259 focus(c);
1260 else
1261 focusprev(args);
1264 static void
1265 focusdown(const char *args[]) {
1266 if (!sel)
1267 return;
1268 Client *c = get_client_by_coord(sel->x, sel->y + sel->h);
1269 if (c)
1270 focus(c);
1271 else
1272 focusnext(args);
1275 static void
1276 focusleft(const char *args[]) {
1277 if (!sel)
1278 return;
1279 Client *c = get_client_by_coord(sel->x - 2, sel->y);
1280 if (c)
1281 focus(c);
1282 else
1283 focusprev(args);
1286 static void
1287 focusright(const char *args[]) {
1288 if (!sel)
1289 return;
1290 Client *c = get_client_by_coord(sel->x + sel->w + 1, sel->y);
1291 if (c)
1292 focus(c);
1293 else
1294 focusnext(args);
1297 static void
1298 killclient(const char *args[]) {
1299 if (!sel)
1300 return;
1301 debug("killing client with pid: %d\n", sel->pid);
1302 kill(-sel->pid, SIGKILL);
1305 static void
1306 paste(const char *args[]) {
1307 if (sel && copyreg.data)
1308 vt_write(sel->term, copyreg.data, copyreg.len);
1311 static void
1312 quit(const char *args[]) {
1313 cleanup();
1314 exit(EXIT_SUCCESS);
1317 static void
1318 redraw(const char *args[]) {
1319 for (Client *c = clients; c; c = c->next) {
1320 if (!c->minimized) {
1321 vt_dirty(c->term);
1322 wclear(c->window);
1323 wnoutrefresh(c->window);
1326 resize_screen();
1329 static void
1330 scrollback(const char *args[]) {
1331 if (!is_content_visible(sel))
1332 return;
1334 if (!args[0] || atoi(args[0]) < 0)
1335 vt_scroll(sel->term, -sel->h/2);
1336 else
1337 vt_scroll(sel->term, sel->h/2);
1339 draw(sel);
1340 curs_set(vt_cursor_visible(sel->term));
1343 static void
1344 send(const char *args[]) {
1345 if (sel && args && args[0])
1346 vt_write(sel->term, args[0], strlen(args[0]));
1349 static void
1350 setlayout(const char *args[]) {
1351 unsigned int i;
1353 if (!args || !args[0]) {
1354 if (++layout == &layouts[LENGTH(layouts)])
1355 layout = &layouts[0];
1356 } else {
1357 for (i = 0; i < LENGTH(layouts); i++)
1358 if (!strcmp(args[0], layouts[i].symbol))
1359 break;
1360 if (i == LENGTH(layouts))
1361 return;
1362 layout = &layouts[i];
1364 arrange();
1367 static void
1368 incnmaster(const char *args[]) {
1369 int delta;
1371 if (isarrange(fullscreen) || isarrange(grid))
1372 return;
1373 /* arg handling, manipulate nmaster */
1374 if (args[0] == NULL) {
1375 screen.nmaster = NMASTER;
1376 } else if (sscanf(args[0], "%d", &delta) == 1) {
1377 if (args[0][0] == '+' || args[0][0] == '-')
1378 screen.nmaster += delta;
1379 else
1380 screen.nmaster = delta;
1381 if (screen.nmaster < 1)
1382 screen.nmaster = 1;
1384 arrange();
1387 static void
1388 setmfact(const char *args[]) {
1389 float delta;
1391 if (isarrange(fullscreen) || isarrange(grid))
1392 return;
1393 /* arg handling, manipulate mfact */
1394 if (args[0] == NULL) {
1395 screen.mfact = MFACT;
1396 } else if (sscanf(args[0], "%f", &delta) == 1) {
1397 if (args[0][0] == '+' || args[0][0] == '-')
1398 screen.mfact += delta;
1399 else
1400 screen.mfact = delta;
1401 if (screen.mfact < 0.1)
1402 screen.mfact = 0.1;
1403 else if (screen.mfact > 0.9)
1404 screen.mfact = 0.9;
1406 arrange();
1409 static void
1410 startup(const char *args[]) {
1411 for (unsigned int i = 0; i < LENGTH(actions); i++)
1412 actions[i].cmd(actions[i].args);
1415 static void
1416 togglebar(const char *args[]) {
1417 if (bar.pos == BAR_OFF)
1418 showbar();
1419 else
1420 hidebar();
1421 bar.autohide = false;
1422 updatebarpos();
1423 redraw(NULL);
1426 static void
1427 togglebarpos(const char *args[]) {
1428 switch (bar.pos == BAR_OFF ? bar.lastpos : bar.pos) {
1429 case BAR_TOP:
1430 bar.pos = BAR_BOTTOM;
1431 break;
1432 case BAR_BOTTOM:
1433 bar.pos = BAR_TOP;
1434 break;
1436 updatebarpos();
1437 redraw(NULL);
1440 static void
1441 toggleminimize(const char *args[]) {
1442 Client *c, *m, *t;
1443 unsigned int n;
1444 if (!sel)
1445 return;
1446 /* the last window can't be minimized */
1447 if (!sel->minimized) {
1448 for (n = 0, c = nextvisible(clients); c; c = nextvisible(c->next))
1449 if (!c->minimized)
1450 n++;
1451 if (n == 1)
1452 return;
1454 sel->minimized = !sel->minimized;
1455 m = sel;
1456 /* check whether the master client was minimized */
1457 if (sel == nextvisible(clients) && sel->minimized) {
1458 c = nextvisible(sel->next);
1459 detach(c);
1460 attach(c);
1461 focus(c);
1462 detach(m);
1463 for (; c && (t = nextvisible(c->next)) && !t->minimized; c = t);
1464 attachafter(m, c);
1465 } else if (m->minimized) {
1466 /* non master window got minimized move it above all other
1467 * minimized ones */
1468 focusnextnm(NULL);
1469 detach(m);
1470 for (c = nextvisible(clients); c && (t = nextvisible(c->next)) && !t->minimized; c = t);
1471 attachafter(m, c);
1472 } else { /* window is no longer minimized, move it to the master area */
1473 vt_dirty(m->term);
1474 detach(m);
1475 attach(m);
1477 arrange();
1480 static void
1481 togglemouse(const char *args[]) {
1482 mouse_events_enabled = !mouse_events_enabled;
1483 mouse_setup();
1486 static void
1487 togglerunall(const char *args[]) {
1488 runinall = !runinall;
1489 drawbar();
1490 draw_all();
1493 static void
1494 zoom(const char *args[]) {
1495 Client *c;
1497 if (!sel)
1498 return;
1499 if (args && args[0])
1500 focusn(args);
1501 if ((c = sel) == nextvisible(clients))
1502 if (!(c = nextvisible(c->next)))
1503 return;
1504 detach(c);
1505 attach(c);
1506 focus(c);
1507 if (c->minimized)
1508 toggleminimize(NULL);
1509 arrange();
1512 /* commands for use by mouse bindings */
1513 static void
1514 mouse_focus(const char *args[]) {
1515 focus(msel);
1516 if (msel->minimized)
1517 toggleminimize(NULL);
1520 static void
1521 mouse_fullscreen(const char *args[]) {
1522 mouse_focus(NULL);
1523 setlayout(isarrange(fullscreen) ? NULL : args);
1526 static void
1527 mouse_minimize(const char *args[]) {
1528 focus(msel);
1529 toggleminimize(NULL);
1532 static void
1533 mouse_zoom(const char *args[]) {
1534 focus(msel);
1535 zoom(NULL);
1538 static Cmd *
1539 get_cmd_by_name(const char *name) {
1540 for (unsigned int i = 0; i < LENGTH(commands); i++) {
1541 if (!strcmp(name, commands[i].name))
1542 return &commands[i];
1544 return NULL;
1547 static void
1548 handle_cmdfifo(void) {
1549 int r;
1550 char *p, *s, cmdbuf[512], c;
1551 Cmd *cmd;
1553 r = read(cmdfifo.fd, cmdbuf, sizeof cmdbuf - 1);
1554 if (r <= 0) {
1555 cmdfifo.fd = -1;
1556 return;
1559 cmdbuf[r] = '\0';
1560 p = cmdbuf;
1561 while (*p) {
1562 /* find the command name */
1563 for (; *p == ' ' || *p == '\n'; p++);
1564 for (s = p; *p && *p != ' ' && *p != '\n'; p++);
1565 if ((c = *p))
1566 *p++ = '\0';
1567 if (*s && (cmd = get_cmd_by_name(s)) != NULL) {
1568 bool quote = false;
1569 int argc = 0;
1570 const char *args[MAX_ARGS], *arg;
1571 memset(args, 0, sizeof(args));
1572 /* if arguments were specified in config.h ignore the one given via
1573 * the named pipe and thus skip everything until we find a new line
1575 if (cmd->action.args[0] || c == '\n') {
1576 debug("execute %s", s);
1577 cmd->action.cmd(cmd->action.args);
1578 while (*p && *p != '\n')
1579 p++;
1580 continue;
1582 /* no arguments were given in config.h so we parse the command line */
1583 while (*p == ' ')
1584 p++;
1585 arg = p;
1586 for (; (c = *p); p++) {
1587 switch (*p) {
1588 case '\\':
1589 /* remove the escape character '\\' move every
1590 * following character to the left by one position
1592 switch (p[1]) {
1593 case '\\':
1594 case '\'':
1595 case '\"': {
1596 char *t = p+1;
1597 do {
1598 t[-1] = *t;
1599 } while (*t++);
1602 break;
1603 case '\'':
1604 case '\"':
1605 quote = !quote;
1606 break;
1607 case ' ':
1608 if (!quote) {
1609 case '\n':
1610 /* remove trailing quote if there is one */
1611 if (*(p - 1) == '\'' || *(p - 1) == '\"')
1612 *(p - 1) = '\0';
1613 *p++ = '\0';
1614 /* remove leading quote if there is one */
1615 if (*arg == '\'' || *arg == '\"')
1616 arg++;
1617 if (argc < MAX_ARGS)
1618 args[argc++] = arg;
1620 while (*p == ' ')
1621 ++p;
1622 arg = p--;
1624 break;
1627 if (c == '\n' || *p == '\n') {
1628 if (!*p)
1629 p++;
1630 debug("execute %s", s);
1631 for(int i = 0; i < argc; i++)
1632 debug(" %s", args[i]);
1633 debug("\n");
1634 cmd->action.cmd(args);
1635 break;
1642 static void
1643 handle_mouse(void) {
1644 #ifdef CONFIG_MOUSE
1645 MEVENT event;
1646 unsigned int i;
1647 if (getmouse(&event) != OK)
1648 return;
1649 msel = get_client_by_coord(event.x, event.y);
1651 if (!msel)
1652 return;
1654 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);
1656 vt_mouse(msel->term, event.x - msel->x, event.y - msel->y, event.bstate);
1658 for (i = 0; i < LENGTH(buttons); i++) {
1659 if (event.bstate & buttons[i].mask)
1660 buttons[i].action.cmd(buttons[i].action.args);
1663 msel = NULL;
1664 #endif /* CONFIG_MOUSE */
1667 static void
1668 handle_statusbar(void) {
1669 char *p;
1670 int r;
1671 switch (r = read(bar.fd, bar.text, sizeof bar.text - 1)) {
1672 case -1:
1673 strncpy(bar.text, strerror(errno), sizeof bar.text - 1);
1674 bar.text[sizeof bar.text - 1] = '\0';
1675 bar.fd = -1;
1676 break;
1677 case 0:
1678 bar.fd = -1;
1679 break;
1680 default:
1681 bar.text[r] = '\0';
1682 p = bar.text + r - 1;
1683 for (; p >= bar.text && *p == '\n'; *p-- = '\0');
1684 for (; p >= bar.text && *p != '\n'; --p);
1685 if (p >= bar.text)
1686 memmove(bar.text, p + 1, strlen(p));
1687 drawbar();
1691 static void
1692 handle_editor(Client *c) {
1693 if (!copyreg.data && (copyreg.data = malloc(screen.history)))
1694 copyreg.size = screen.history;
1695 copyreg.len = 0;
1696 while (c->editor_fds[1] != -1 && copyreg.len < copyreg.size) {
1697 ssize_t len = read(c->editor_fds[1], copyreg.data + copyreg.len, copyreg.size - copyreg.len);
1698 if (len == -1) {
1699 if (errno == EINTR)
1700 continue;
1701 break;
1703 if (len == 0)
1704 break;
1705 copyreg.len += len;
1706 if (copyreg.len == copyreg.size) {
1707 copyreg.size *= 2;
1708 if (!(copyreg.data = realloc(copyreg.data, copyreg.size))) {
1709 copyreg.size = 0;
1710 copyreg.len = 0;
1714 c->editor_died = false;
1715 c->editor_fds[1] = -1;
1716 vt_destroy(c->editor);
1717 c->editor = NULL;
1718 c->term = c->app;
1719 vt_dirty(c->term);
1720 draw_content(c);
1721 wnoutrefresh(c->window);
1724 static int
1725 open_or_create_fifo(const char *name, const char **name_created) {
1726 struct stat info;
1727 int fd;
1729 do {
1730 if ((fd = open(name, O_RDWR|O_NONBLOCK)) == -1) {
1731 if (errno == ENOENT && !mkfifo(name, S_IRUSR|S_IWUSR)) {
1732 *name_created = name;
1733 continue;
1735 error("%s\n", strerror(errno));
1737 } while (fd == -1);
1739 if (fstat(fd, &info) == -1)
1740 error("%s\n", strerror(errno));
1741 if (!S_ISFIFO(info.st_mode))
1742 error("%s is not a named pipe\n", name);
1743 return fd;
1746 static void
1747 usage(void) {
1748 cleanup();
1749 eprint("usage: dvtm [-v] [-M] [-m mod] [-d delay] [-h lines] [-t title] "
1750 "[-s status-fifo] [-c cmd-fifo] [cmd...]\n");
1751 exit(EXIT_FAILURE);
1754 static bool
1755 parse_args(int argc, char *argv[]) {
1756 bool init = false;
1757 const char *name = argv[0];
1759 if (name && (name = strrchr(name, '/')))
1760 dvtm_name = name + 1;
1761 if (!getenv("ESCDELAY"))
1762 set_escdelay(100);
1763 for (int arg = 1; arg < argc; arg++) {
1764 if (argv[arg][0] != '-') {
1765 const char *args[] = { argv[arg], NULL, NULL };
1766 if (!init) {
1767 setup();
1768 init = true;
1770 create(args);
1771 continue;
1773 if (argv[arg][1] != 'v' && argv[arg][1] != 'M' && (arg + 1) >= argc)
1774 usage();
1775 switch (argv[arg][1]) {
1776 case 'v':
1777 puts("dvtm-"VERSION" © 2007-2016 Marc André Tanner");
1778 exit(EXIT_SUCCESS);
1779 case 'M':
1780 mouse_events_enabled = !mouse_events_enabled;
1781 break;
1782 case 'm': {
1783 char *mod = argv[++arg];
1784 if (mod[0] == '^' && mod[1])
1785 *mod = CTRL(mod[1]);
1786 for (unsigned int b = 0; b < LENGTH(bindings); b++)
1787 if (bindings[b].keys[0] == MOD)
1788 bindings[b].keys[0] = *mod;
1789 break;
1791 case 'd':
1792 set_escdelay(atoi(argv[++arg]));
1793 if (ESCDELAY < 50)
1794 set_escdelay(50);
1795 else if (ESCDELAY > 1000)
1796 set_escdelay(1000);
1797 break;
1798 case 'h':
1799 screen.history = atoi(argv[++arg]);
1800 break;
1801 case 't':
1802 title = argv[++arg];
1803 break;
1804 case 's':
1805 bar.fd = open_or_create_fifo(argv[++arg], &bar.file);
1806 updatebarpos();
1807 break;
1808 case 'c': {
1809 char *fifo;
1810 cmdfifo.fd = open_or_create_fifo(argv[++arg], &cmdfifo.file);
1811 if (!(fifo = realpath(argv[arg], NULL)))
1812 error("%s\n", strerror(errno));
1813 setenv("DVTM_CMD_FIFO", fifo, 1);
1814 free(fifo);
1815 break;
1817 default:
1818 usage();
1821 return init;
1825 main(int argc, char *argv[]) {
1826 unsigned int key_index = 0;
1827 memset(keys, 0, sizeof(keys));
1828 sigset_t emptyset, blockset;
1830 setenv("DVTM", VERSION, 1);
1831 if (!parse_args(argc, argv)) {
1832 setup();
1833 startup(NULL);
1836 sigemptyset(&emptyset);
1837 sigemptyset(&blockset);
1838 sigaddset(&blockset, SIGWINCH);
1839 sigaddset(&blockset, SIGCHLD);
1840 sigprocmask(SIG_BLOCK, &blockset, NULL);
1842 while (running) {
1843 int r, nfds = 0;
1844 fd_set rd;
1846 if (screen.need_resize) {
1847 resize_screen();
1848 screen.need_resize = false;
1851 FD_ZERO(&rd);
1852 FD_SET(STDIN_FILENO, &rd);
1854 if (cmdfifo.fd != -1) {
1855 FD_SET(cmdfifo.fd, &rd);
1856 nfds = cmdfifo.fd;
1859 if (bar.fd != -1) {
1860 FD_SET(bar.fd, &rd);
1861 nfds = MAX(nfds, bar.fd);
1864 for (Client *c = clients; c; ) {
1865 if (c->editor && c->editor_died)
1866 handle_editor(c);
1867 if (!c->editor && c->died) {
1868 Client *t = c->next;
1869 destroy(c);
1870 c = t;
1871 continue;
1873 int pty = c->editor ? vt_pty_get(c->editor) : vt_pty_get(c->app);
1874 FD_SET(pty, &rd);
1875 nfds = MAX(nfds, pty);
1876 c = c->next;
1879 doupdate();
1880 r = pselect(nfds + 1, &rd, NULL, NULL, NULL, &emptyset);
1882 if (r < 0) {
1883 if (errno == EINTR)
1884 continue;
1885 perror("select()");
1886 exit(EXIT_FAILURE);
1889 if (FD_ISSET(STDIN_FILENO, &rd)) {
1890 int code = getch();
1891 if (code >= 0) {
1892 keys[key_index++] = code;
1893 KeyBinding *binding = NULL;
1894 if (code == KEY_MOUSE) {
1895 key_index = 0;
1896 handle_mouse();
1897 } else if ((binding = keybinding(keys, key_index))) {
1898 unsigned int key_length = MAX_KEYS;
1899 while (key_length > 1 && !binding->keys[key_length-1])
1900 key_length--;
1901 if (key_index == key_length) {
1902 binding->action.cmd(binding->action.args);
1903 key_index = 0;
1904 memset(keys, 0, sizeof(keys));
1906 } else {
1907 key_index = 0;
1908 memset(keys, 0, sizeof(keys));
1909 keypress(code);
1911 drawbar();
1912 if (is_content_visible(sel))
1913 wnoutrefresh(sel->window);
1915 if (r == 1) /* no data available on pty's */
1916 continue;
1919 if (cmdfifo.fd != -1 && FD_ISSET(cmdfifo.fd, &rd))
1920 handle_cmdfifo();
1922 if (bar.fd != -1 && FD_ISSET(bar.fd, &rd))
1923 handle_statusbar();
1925 for (Client *c = clients; c; c = c->next) {
1926 if (FD_ISSET(vt_pty_get(c->term), &rd)) {
1927 if (vt_process(c->term) < 0 && errno == EIO) {
1928 if (c->editor)
1929 c->editor_died = true;
1930 else
1931 c->died = true;
1932 continue;
1936 if (c != sel && is_content_visible(c)) {
1937 draw_content(c);
1938 wnoutrefresh(c->window);
1942 if (is_content_visible(sel)) {
1943 draw_content(sel);
1944 curs_set(vt_cursor_visible(sel->term));
1945 wnoutrefresh(sel->window);
1949 cleanup();
1950 return 0;