Unlink any named pipes which we created
[dvtm.git] / dvtm.c
blobbfe03abda22be8d9eb8da884fbc6c4921380e6b3
1 /*
2 * The initial "port" of dwm to curses was done by
3 * (c) 2007-2011 Marc Andre Tanner <mat at brain-dump dot org>
5 * It is highly inspired by the original X11 dwm and
6 * reuses some code of it which is mostly
8 * (c) 2006-2007 Anselm R. Garbe <garbeam at gmail dot com>
10 * See LICENSE for details.
13 #define _GNU_SOURCE
14 #include <sys/stat.h>
15 #include <sys/ioctl.h>
16 #ifdef _AIX
17 # include <fcntl.h>
18 #else
19 # include <sys/fcntl.h>
20 #endif
21 #include <sys/wait.h>
22 #include <sys/time.h>
23 #include <sys/types.h>
24 #include <ncurses.h>
25 #include <stdio.h>
26 #include <signal.h>
27 #include <locale.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <stdbool.h>
31 #include <errno.h>
32 #ifdef __CYGWIN__
33 # include <termios.h>
34 #endif
35 #include "madtty.h"
37 typedef struct {
38 double mfact;
39 int history;
40 int w;
41 int h;
42 bool need_resize;
43 } Screen;
45 typedef struct {
46 const char *symbol;
47 void (*arrange)(void);
48 } Layout;
50 typedef struct Client Client;
51 struct Client {
52 WINDOW *window;
53 madtty_t *term;
54 const char *cmd;
55 char title[256];
56 uint8_t order;
57 pid_t pid;
58 int pty;
59 unsigned short int id;
60 unsigned short int x;
61 unsigned short int y;
62 unsigned short int w;
63 unsigned short int h;
64 bool minimized;
65 bool died;
66 Client *next;
67 Client *prev;
70 typedef struct {
71 const char *title;
72 unsigned attrs;
73 short fg;
74 short bg;
75 } ColorRule;
77 #define ALT(k) ((k) + (161 - 'a'))
78 #ifndef CTRL
79 #define CTRL(k) ((k) & 0x1F)
80 #endif
81 #define CTRL_ALT(k) ((k) + (129 - 'a'))
83 #define MAX_ARGS 2
85 typedef struct {
86 void (*cmd)(const char *args[]);
87 /* needed to avoid an error about initialization
88 * of nested flexible array members */
89 const char *args[MAX_ARGS + 1];
90 } Action;
92 typedef struct {
93 unsigned int mod;
94 unsigned int code;
95 Action action;
96 } Key;
98 typedef struct {
99 mmask_t mask;
100 Action action;
101 } Button;
103 typedef struct {
104 const char *name;
105 Action action;
106 } Cmd;
108 enum BarPos { BarTop, BarBot, BarOff };
109 enum { ALIGN_LEFT, ALIGN_RIGHT };
111 typedef struct {
112 int fd;
113 int pos;
114 unsigned short int h;
115 unsigned short int y;
116 char text[512];
117 const char *file;
118 } StatusBar;
120 typedef struct {
121 int fd;
122 const char *file;
123 unsigned short int id;
124 } CmdFifo;
126 #define countof(arr) (sizeof(arr) / sizeof((arr)[0]))
127 #define sstrlen(str) (sizeof(str) - 1)
128 #define max(x, y) ((x) > (y) ? (x) : (y))
130 #ifdef NDEBUG
131 #define debug(format, args...)
132 #else
133 #define debug eprint
134 #endif
136 /* commands for use by keybindings */
137 static void quit(const char *args[]);
138 static void create(const char *args[]);
139 static void startup(const char *args[]);
140 static void escapekey(const char *args[]);
141 static void killclient(const char *args[]);
142 static void focusn(const char *args[]);
143 static void focusnext(const char *args[]);
144 static void focusnextnm(const char *args[]);
145 static void focusprev(const char *args[]);
146 static void focusprevnm(const char *args[]);
147 static void togglebell(const char *key[]);
148 static void toggleminimize(const char *args[]);
149 static void setmfact(const char *args[]);
150 static void setlayout(const char *args[]);
151 static void scrollback(const char *args[]);
152 static void redraw(const char *args[]);
153 static void zoom(const char *args[]);
154 static void lock(const char *key[]);
155 static void togglerunall(const char *args[]);
157 static void togglebar(const char *args[]);
159 static void mouse_focus(const char *args[]);
160 static void mouse_fullscreen(const char *args[]);
161 static void mouse_minimize(const char *args[]);
162 static void mouse_zoom(const char *args[]);
163 static void mouse_toggle();
165 static void clear_workspace();
166 static void draw(Client *c);
167 static void draw_all(bool border);
168 static void draw_border(Client *c);
169 static void resize(Client *c, int x, int y, int w, int h);
170 static void resize_screen();
171 static void eprint(const char *errstr, ...);
172 static bool isarrange(void (*func)());
173 static void arrange();
174 static void focus(Client *c);
175 static void keypress(int code);
177 static unsigned int waw, wah, wax, way;
178 static Client *clients = NULL;
179 extern Screen screen;
181 #include "config.h"
183 Screen screen = { MFACT, SCROLL_HISTORY };
184 static Client *sel = NULL;
185 static Layout *layout = layouts;
186 static StatusBar bar = { -1, BARPOS, 1 };
187 static CmdFifo cmdfifo = { -1 };
188 static const char *shell;
189 static bool running = true;
190 static bool runinall = false;
192 #include "mouse.c"
193 #include "cmdfifo.c"
194 #include "statusbar.c"
196 static void
197 eprint(const char *errstr, ...) {
198 va_list ap;
199 va_start(ap, errstr);
200 vfprintf(stderr, errstr, ap);
201 va_end(ap);
204 static void
205 error(const char *errstr, ...) {
206 va_list ap;
207 va_start(ap, errstr);
208 vfprintf(stderr, errstr, ap);
209 va_end(ap);
210 exit(EXIT_FAILURE);
213 static void
214 attach(Client *c) {
215 uint8_t order;
216 if (clients)
217 clients->prev = c;
218 c->next = clients;
219 c->prev = NULL;
220 clients = c;
221 for (order = 1; c; c = c->next, order++)
222 c->order = order;
225 static void
226 attachafter(Client *c, Client *a) { /* attach c after a */
227 uint8_t o;
228 if (c == a)
229 return;
230 if (!a)
231 for (a = clients; a && a->next; a = a->next);
233 if (a) {
234 if (a->next)
235 a->next->prev = c;
236 c->next = a->next;
237 c->prev = a;
238 a->next = c;
239 for (o = a->order; c; c = c->next)
240 c->order = ++o;
244 static void
245 detach(Client *c) {
246 Client *d;
247 if (c->prev)
248 c->prev->next = c->next;
249 if (c->next) {
250 c->next->prev = c->prev;
251 for (d = c->next; d; d = d->next)
252 --d->order;
254 if (c == clients)
255 clients = c->next;
256 c->next = c->prev = NULL;
259 static void
260 arrange() {
261 clear_workspace();
262 attrset(NORMAL_ATTR);
263 color_set(madtty_color_get(NORMAL_FG, NORMAL_BG), NULL);
264 layout->arrange();
265 wnoutrefresh(stdscr);
266 draw_all(true);
269 static bool
270 isarrange(void (*func)()) {
271 return func == layout->arrange;
274 static void
275 focus(Client *c) {
276 Client *tmp = sel;
277 if (sel == c)
278 return;
279 sel = c;
280 if (tmp) {
281 draw_border(tmp);
282 wrefresh(tmp->window);
284 if (isarrange(fullscreen))
285 redrawwin(c->window);
286 draw_border(c);
287 wrefresh(c->window);
290 static void
291 focusn(const char *args[]) {
292 Client *c;
294 for (c = clients; c; c = c->next) {
295 if (c->order == atoi(args[0])) {
296 focus(c);
297 if (c->minimized)
298 toggleminimize(NULL);
299 return;
304 static void
305 focusnext(const char *args[]) {
306 Client *c;
308 if (!sel)
309 return;
311 c = sel->next;
312 if (!c)
313 c = clients;
314 if (c)
315 focus(c);
318 static void
319 focusnextnm(const char *args[]) {
320 Client *c;
322 if (!sel)
323 return;
324 c = sel;
325 do {
326 c = c->next;
327 if (!c)
328 c = clients;
329 } while (c->minimized && c != sel);
330 focus(c);
333 static void
334 focusprev(const char *args[]) {
335 Client *c;
337 if (!sel)
338 return;
339 c = sel->prev;
340 if (!c)
341 for (c = clients; c && c->next; c = c->next);
342 if (c)
343 focus(c);
346 static void
347 focusprevnm(const char *args[]) {
348 Client *c;
350 if (!sel)
351 return;
352 c = sel;
353 do {
354 c = c->prev;
355 if (!c)
356 for (c = clients; c && c->next; c = c->next);
357 } while (c->minimized && c != sel);
358 focus(c);
361 static void
362 zoom(const char *args[]) {
363 Client *c;
365 if (!sel)
366 return;
367 if ((c = sel) == clients)
368 if (!(c = c->next))
369 return;
370 detach(c);
371 attach(c);
372 focus(c);
373 if (c->minimized)
374 toggleminimize(NULL);
375 arrange();
378 static void
379 togglebell(const char *args[]) {
380 madtty_togglebell(sel->term);
383 static void
384 toggleminimize(const char *args[]) {
385 Client *c, *m;
386 unsigned int n;
387 if (!sel)
388 return;
389 /* the last window can't be minimized */
390 if (!sel->minimized) {
391 for (n = 0, c = clients; c; c = c->next)
392 if (!c->minimized)
393 n++;
394 if (n == 1)
395 return;
397 sel->minimized = !sel->minimized;
398 m = sel;
399 /* check whether the master client was minimized */
400 if (sel == clients && sel->minimized) {
401 c = sel->next;
402 detach(c);
403 attach(c);
404 focus(c);
405 detach(m);
406 for (; c && c->next && !c->next->minimized; c = c->next);
407 attachafter(m, c);
408 } else if (m->minimized) {
409 /* non master window got minimized move it above all other
410 * minimized ones */
411 focusnextnm(NULL);
412 detach(m);
413 for (c = clients; c && c->next && !c->next->minimized; c = c->next);
414 attachafter(m, c);
415 } else { /* window is no longer minimized, move it to the master area */
416 madtty_dirty(m->term);
417 detach(m);
418 attach(m);
420 arrange();
423 static void
424 setlayout(const char *args[]) {
425 unsigned int i;
427 if (!args || !args[0]) {
428 if (++layout == &layouts[countof(layouts)])
429 layout = &layouts[0];
430 } else {
431 for (i = 0; i < countof(layouts); i++)
432 if (!strcmp(args[0], layouts[i].symbol))
433 break;
434 if (i == countof(layouts))
435 return;
436 layout = &layouts[i];
438 arrange();
441 static void
442 setmfact(const char *args[]) {
443 double delta;
445 if (isarrange(fullscreen) || isarrange(grid))
446 return;
447 /* arg handling, manipulate mfact */
448 if (args[0] == NULL)
449 screen.mfact = MFACT;
450 else if (1 == sscanf(args[0], "%lf", &delta)) {
451 if (args[0][0] == '+' || args[0][0] == '-')
452 screen.mfact += delta;
453 else
454 screen.mfact = delta;
455 if (screen.mfact < 0.1)
456 screen.mfact = 0.1;
457 else if (screen.mfact > 0.9)
458 screen.mfact = 0.9;
460 arrange();
463 static void
464 scrollback(const char *args[]) {
465 if (!sel) return;
467 if (!args[0] || atoi(args[0]) < 0)
468 madtty_scroll(sel->term, -sel->h/2);
469 else
470 madtty_scroll(sel->term, sel->h/2);
472 draw(sel);
475 static void
476 redraw(const char *args[]) {
477 wrefresh(curscr);
478 resize_screen();
479 draw_all(true);
482 static void
483 draw_border(Client *c) {
484 char *s, t = '\0';
485 int x, y, o;
486 if (sel == c) {
487 wattrset(c->window, SELECTED_ATTR);
488 wcolor_set(c->window, madtty_color_get(SELECTED_FG, SELECTED_BG), NULL);
489 } else {
490 wattrset(c->window, NORMAL_ATTR);
491 wcolor_set(c->window, madtty_color_get(NORMAL_FG, NORMAL_BG), NULL);
493 getyx(c->window, y, x);
494 curs_set(0);
495 mvwhline(c->window, 0, 0, ACS_HLINE, c->w);
496 o = c->w - (4 + sstrlen(TITLE) - 5 + sstrlen(SEPARATOR));
497 if (o < 0)
498 o = 0;
499 if ((size_t)o < sizeof(c->title)) {
500 t = *(s = &c->title[o]);
501 *s = '\0';
503 mvwprintw(c->window, 0, 2, TITLE,
504 *c->title ? c->title : "",
505 *c->title ? SEPARATOR : "",
506 c->order);
507 if (t)
508 *s = t;
509 wmove(c->window, y, x);
510 if (!c->minimized)
511 curs_set(madtty_cursor(c->term));
514 static void
515 draw_content(Client *c) {
516 if (!c->minimized || isarrange(fullscreen)) {
517 madtty_draw(c->term, c->window, 1, 0);
518 if (c != sel)
519 curs_set(0);
523 static void
524 draw(Client *c) {
525 draw_content(c);
526 draw_border(c);
527 wrefresh(c->window);
530 static void
531 clear_workspace() {
532 for (unsigned int y = 0; y < wah; y++)
533 mvhline(way + y, 0, ' ', waw);
534 wnoutrefresh(stdscr);
537 static void
538 draw_all(bool border) {
539 Client *c;
540 curs_set(0);
541 for (c = clients; c; c = c->next) {
542 redrawwin(c->window);
543 if (c == sel)
544 continue;
545 draw_content(c);
546 if (border)
547 draw_border(c);
548 wnoutrefresh(c->window);
550 /* as a last step the selected window is redrawn,
551 * this has the effect that the cursor position is
552 * accurate
554 refresh();
555 if (sel) {
556 draw_content(sel);
557 if (border)
558 draw_border(sel);
559 wrefresh(sel->window);
563 static void
564 escapekey(const char *args[]) {
565 int key;
566 if ((key = getch()) >= 0) {
567 debug("escaping key `%c'\n", key);
568 keypress(CTRL(key));
573 * Lock the screen until the correct password is entered.
574 * The password can either be specified in config.h which is
575 * not recommended because `strings dvtm` will contain it. If
576 * no password is specified in the configuration file it is read
577 * from the keyboard before the screen is locked.
579 * NOTE: this function doesn't handle the input from clients. All
580 * foreground operations are temporarily suspended since the
581 * function doesn't return.
583 static void
584 lock(const char *args[]) {
585 size_t len = 0, i = 0;
586 char buf[16], *pass = buf;
587 int c;
589 erase();
590 curs_set(0);
592 if (args && args[0]) {
593 len = strlen(args[0]);
594 pass = (char *)args[0];
595 } else {
596 mvprintw(LINES / 2, COLS / 2 - 7, "Enter password");
597 while (len < sizeof buf && (c = getch()) != '\n')
598 if (c != ERR)
599 buf[len++] = c;
602 mvprintw(LINES / 2, COLS / 2 - 7, "Screen locked!");
604 while (i != len) {
605 for(i = 0; i < len; i++) {
606 if (getch() != pass[i])
607 break;
611 arrange();
614 static void
615 togglerunall(const char *args[]) {
616 runinall = !runinall;
619 static void
620 killclient(const char *args[]) {
621 if (!sel)
622 return;
623 debug("killing client with pid: %d\n", sel->pid);
624 kill(-sel->pid, SIGKILL);
627 static void
628 applycolorrules(madtty_t *term, char *title) {
629 unsigned int i;
630 unsigned attrs = A_NORMAL;
631 short fg = -1, bg = -1;
632 const ColorRule *r;
634 for (i = 0; i < countof(colorrules); i++) {
635 r = &colorrules[i];
636 if (strstr(title, r->title)) {
637 attrs = r->attrs;
638 fg = r->fg;
639 bg = r->bg;
640 break;
643 madtty_set_default_colors(term, attrs, fg, bg);
646 static int
647 title_escape_seq_handler(madtty_t *term, char *es) {
648 Client *c;
649 unsigned int l;
650 if (es[0] != ']' || (es[1] && (es[1] < '0' || es[1] > '9')) || (es[2] && es[2] != ';'))
651 return MADTTY_HANDLER_NOWAY;
652 if ((l = strlen(es)) < 3 || es[l - 1] != '\07')
653 return MADTTY_HANDLER_NOTYET;
654 es[l - 1] = '\0';
655 c = (Client *)madtty_get_data(term);
656 strncpy(c->title, es + 3, sizeof(c->title));
657 draw_border(c);
658 debug("window title: %s\n", c->title);
659 applycolorrules(term, c->title);
660 return MADTTY_HANDLER_OK;
663 static void
664 create(const char *args[]) {
665 Client *c = calloc(sizeof(Client), 1);
666 if (!c)
667 return;
668 const char *cmd = (args && args[0]) ? args[0] : shell;
669 const char *pargs[] = { "/bin/sh", "-c", cmd, NULL };
670 c->id = ++cmdfifo.id;
671 char buf[8];
672 snprintf(buf, sizeof buf, "%d", c->id);
673 const char *env[] = {
674 "DVTM", VERSION,
675 "DVTM_WINDOW_ID", buf,
676 NULL
679 c->window = newwin(wah, waw, way, wax);
680 c->term = madtty_create(screen.h - 1, screen.w, screen.history);
681 c->cmd = cmd;
682 if (args && args[1])
683 strncpy(c->title, args[1], sizeof(c->title));
684 c->pid = madtty_forkpty(c->term, "/bin/sh", pargs, env, &c->pty);
685 madtty_set_data(c->term, c);
686 madtty_set_handler(c->term, title_escape_seq_handler);
687 c->w = screen.w;
688 c->h = screen.h;
689 c->x = wax;
690 c->y = way;
691 c->order = 0;
692 c->minimized = false;
693 debug("client with pid %d forked\n", c->pid);
694 attach(c);
695 focus(c);
696 arrange();
699 static void
700 destroy(Client *c) {
701 if (sel == c)
702 focusnextnm(NULL);
703 detach(c);
704 if (sel == c) {
705 if (clients) {
706 focus(clients);
707 toggleminimize(NULL);
708 } else
709 sel = NULL;
711 werase(c->window);
712 wrefresh(c->window);
713 madtty_destroy(c->term);
714 delwin(c->window);
715 if (!clients && countof(actions)) {
716 if (!strcmp(c->cmd, shell))
717 quit(NULL);
718 else
719 create(NULL);
721 free(c);
722 arrange();
725 static void
726 move_client(Client *c, int x, int y) {
727 if (c->x == x && c->y == y)
728 return;
729 debug("moving, x: %d y: %d\n", x, y);
730 if (mvwin(c->window, y, x) == ERR)
731 eprint("error moving, x: %d y: %d\n", x, y);
732 else {
733 c->x = x;
734 c->y = y;
738 static void
739 resize_client(Client *c, int w, int h) {
740 if (c->w == w && c->h == h)
741 return;
742 debug("resizing, w: %d h: %d\n", w, h);
743 if (wresize(c->window, h, w) == ERR)
744 eprint("error resizing, w: %d h: %d\n", w, h);
745 else {
746 c->w = w;
747 c->h = h;
749 madtty_resize(c->term, h - 1, w);
752 static void
753 resize(Client *c, int x, int y, int w, int h) {
754 resize_client(c, w, h);
755 move_client(c, x, y);
758 static bool
759 is_modifier(unsigned int mod) {
760 unsigned int i;
761 for (i = 0; i < countof(keys); i++) {
762 if (keys[i].mod == mod)
763 return true;
765 return false;
768 static Key*
769 keybinding(unsigned int mod, unsigned int code) {
770 unsigned int i;
771 for (i = 0; i < countof(keys); i++) {
772 if (keys[i].mod == mod && keys[i].code == code)
773 return &keys[i];
775 return NULL;
778 static Client*
779 get_client_by_pid(pid_t pid) {
780 Client *c;
781 for (c = clients; c; c = c->next) {
782 if (c->pid == pid)
783 return c;
785 return NULL;
788 static void
789 sigchld_handler(int sig) {
790 int errsv = errno;
791 int status;
792 pid_t pid;
793 Client *c;
795 while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
796 if (pid == -1) {
797 if (errno == ECHILD) {
798 /* no more child processes */
799 break;
801 eprint("waitpid: %s\n", strerror(errno));
802 break;
804 debug("child with pid %d died\n", pid);
805 if ((c = get_client_by_pid(pid)))
806 c->died = true;
809 signal(SIGCHLD, sigchld_handler);
811 errno = errsv;
814 static void
815 sigwinch_handler(int sig) {
816 signal(SIGWINCH, sigwinch_handler);
817 screen.need_resize = true;
820 static void
821 sigterm_handler(int sig) {
822 running = false;
825 static void
826 resize_screen() {
827 struct winsize ws;
829 if (ioctl(0, TIOCGWINSZ, &ws) == -1)
830 return;
832 screen.w = ws.ws_col;
833 screen.h = ws.ws_row;
835 debug("resize_screen(), w: %d h: %d\n", screen.w, screen.h);
837 #if defined(__OpenBSD__) || defined(__NetBSD__)
838 resizeterm(screen.h, screen.w);
839 #else
840 resize_term(screen.h, screen.w);
841 #endif
842 wresize(stdscr, screen.h, screen.w);
843 wrefresh(curscr);
844 refresh();
846 waw = screen.w;
847 wah = screen.h;
848 updatebarpos();
849 drawbar();
850 arrange();
853 static void
854 startup(const char *args[]) {
855 for (unsigned int i = 0; i < countof(actions); i++)
856 actions[i].cmd(actions[i].args);
859 static void
860 setup() {
861 if (!(shell = getenv("SHELL")))
862 shell = "/bin/sh";
863 setlocale(LC_CTYPE, "");
864 initscr();
865 start_color();
866 noecho();
867 keypad(stdscr, TRUE);
868 mouse_setup();
869 raw();
870 madtty_init();
871 getmaxyx(stdscr, screen.h, screen.w);
872 resize_screen();
873 signal(SIGWINCH, sigwinch_handler);
874 signal(SIGCHLD, sigchld_handler);
875 signal(SIGTERM, sigterm_handler);
878 static void
879 cleanup() {
880 endwin();
881 if (bar.fd > 0)
882 close(bar.fd);
883 if (bar.file)
884 unlink(bar.file);
885 if (cmdfifo.fd > 0)
886 close(cmdfifo.fd);
887 if (cmdfifo.file)
888 unlink(cmdfifo.file);
891 static void
892 quit(const char *args[]) {
893 cleanup();
894 exit(EXIT_SUCCESS);
897 static void
898 usage() {
899 cleanup();
900 eprint("usage: dvtm [-v] [-m mod] [-d escdelay] [-h n] "
901 "[-s status-fifo] "
902 "[-c cmd-fifo] "
903 "[cmd...]\n");
904 exit(EXIT_FAILURE);
907 static int
908 open_or_create_fifo(const char *name, const char **name_created) {
909 struct stat info;
910 int fd;
912 do {
913 if ((fd = open(name, O_RDWR|O_NONBLOCK)) == -1) {
914 if (errno == ENOENT && !mkfifo(name, S_IRUSR|S_IWUSR)) {
915 *name_created = name;
916 continue;
918 error("%s\n", strerror(errno));
920 } while (fd == -1);
922 if (fstat(fd, &info) == -1)
923 error("%s\n", strerror(errno));
924 if (!S_ISFIFO(info.st_mode))
925 error("%s is not a named pipe\n", name);
926 return fd;
929 static bool
930 parse_args(int argc, char *argv[]) {
931 int arg;
932 bool init = false;
934 if (!getenv("ESCDELAY"))
935 ESCDELAY = 100;
936 for (arg = 1; arg < argc; arg++) {
937 if (argv[arg][0] != '-') {
938 const char *args[] = { argv[arg], NULL };
939 if (!init) {
940 setup();
941 init = true;
943 create(args);
944 continue;
946 if (argv[arg][1] != 'v' && (arg + 1) >= argc)
947 usage();
948 switch (argv[arg][1]) {
949 case 'v':
950 puts("dvtm-"VERSION" (c) 2007-2011 Marc Andre Tanner");
951 exit(EXIT_SUCCESS);
952 case 'm': {
953 char *mod = argv[++arg];
954 if (mod[0] == '^' && mod[1])
955 *mod = CTRL(mod[1]);
956 for (unsigned int i = 0; i < countof(keys); i++)
957 keys[i].mod = *mod;
958 break;
960 case 'd':
961 ESCDELAY = atoi(argv[++arg]);
962 if (ESCDELAY < 50)
963 ESCDELAY = 50;
964 else if (ESCDELAY > 1000)
965 ESCDELAY = 1000;
966 break;
967 case 'h':
968 screen.history = atoi(argv[++arg]);
969 break;
970 case 's':
971 bar.fd = open_or_create_fifo(argv[++arg], &bar.file);
972 updatebarpos();
973 break;
974 case 'c': {
975 const char *fifo;
976 cmdfifo.fd = open_or_create_fifo(argv[++arg], &cmdfifo.file);
977 if (!(fifo = get_realpath(argv[arg])))
978 error("%s\n", strerror(errno));
979 setenv("DVTM_CMD_FIFO", fifo, 1);
980 break;
982 default:
983 usage();
986 return init;
989 void
990 keypress(int code) {
991 Client *c;
992 unsigned int len = 1;
993 char buf[8] = { '\e' };
995 if (code == '\e') {
996 /* pass characters following escape to the underlying app */
997 nodelay(stdscr, TRUE);
998 for (int t; len < sizeof(buf) && (t = getch()) != ERR; len++)
999 buf[len] = t;
1000 nodelay(stdscr, FALSE);
1003 for (c = runinall ? clients : sel; c; c = c->next) {
1004 if (!c->minimized || isarrange(fullscreen)) {
1005 if (code == '\e')
1006 madtty_write(c->term, buf, len);
1007 else
1008 madtty_keypress(c->term, code);
1010 if (!runinall)
1011 break;
1016 main(int argc, char *argv[]) {
1017 if (!parse_args(argc, argv)) {
1018 setup();
1019 startup(NULL);
1022 while (running) {
1023 Client *c, *t;
1024 int r, nfds = 0;
1025 fd_set rd;
1027 if (screen.need_resize) {
1028 resize_screen();
1029 screen.need_resize = false;
1032 FD_ZERO(&rd);
1033 FD_SET(STDIN_FILENO, &rd);
1035 if (cmdfifo.fd != -1) {
1036 FD_SET(cmdfifo.fd, &rd);
1037 nfds = cmdfifo.fd;
1040 if (bar.fd != -1) {
1041 FD_SET(bar.fd, &rd);
1042 nfds = max(nfds, bar.fd);
1045 for (c = clients; c; ) {
1046 if (c->died) {
1047 t = c->next;
1048 destroy(c);
1049 c = t;
1050 continue;
1052 FD_SET(c->pty, &rd);
1053 nfds = max(nfds, c->pty);
1054 c = c->next;
1056 r = select(nfds + 1, &rd, NULL, NULL, NULL);
1058 if (r == -1 && errno == EINTR)
1059 continue;
1061 if (r < 0) {
1062 perror("select()");
1063 exit(EXIT_FAILURE);
1066 if (FD_ISSET(STDIN_FILENO, &rd)) {
1067 int code = getch();
1068 Key *key;
1069 if (code >= 0) {
1070 if (code == KEY_MOUSE) {
1071 handle_mouse();
1072 } else if (is_modifier(code)) {
1073 int mod = code;
1074 code = getch();
1075 if (code >= 0) {
1076 if (code == mod)
1077 keypress(code);
1078 else if ((key = keybinding(mod, code)))
1079 key->action.cmd(key->action.args);
1081 } else if ((key = keybinding(0, code))) {
1082 key->action.cmd(key->action.args);
1083 } else {
1084 keypress(code);
1087 if (r == 1) /* no data available on pty's */
1088 continue;
1091 if (cmdfifo.fd != -1 && FD_ISSET(cmdfifo.fd, &rd))
1092 handle_cmdfifo();
1094 if (bar.fd != -1 && FD_ISSET(bar.fd, &rd))
1095 handle_statusbar();
1097 for (c = clients; c; ) {
1098 if (FD_ISSET(c->pty, &rd)) {
1099 if (madtty_process(c->term) < 0 && errno == EIO) {
1100 /* client probably terminated */
1101 t = c->next;
1102 destroy(c);
1103 c = t;
1104 continue;
1106 if (c != sel) {
1107 draw_content(c);
1108 if (!isarrange(fullscreen))
1109 wnoutrefresh(c->window);
1112 c = c->next;
1115 if (sel) {
1116 draw_content(sel);
1117 wnoutrefresh(sel->window);
1119 doupdate();
1122 cleanup();
1123 return 0;