76af9146ec6359744a719c820eea09f7e17f0f1a
[apps/madtty.git] / demo / boxshell.c
1 /* Just a simple example program that creates a terminal in a frame
2  * and lets the user interact with it.
3  *
4  * To compile:
5  *    gcc -o boxshell boxshell.c $(pkg-config madtty --cflags --libs)
6  */
7
8 #include <ncurses.h>
9
10 #include <fcntl.h>
11 #include <signal.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <sys/ioctl.h>
15 #include <sys/time.h>
16 #include <termios.h>
17 #include <time.h>
18
19 #include <madtty/madtty.h>
20
21 static int getout = 0, sigwinch = 0;
22 static int screen_w, screen_h;
23 static WINDOW *term_win;
24 static struct timeval const slice = { 0, 1000 * 1000 / 100 };
25
26 void handler(int signo)
27 {
28     switch (signo) {
29       case SIGCHLD:
30         getout = 1;
31         break;
32       case SIGWINCH:
33         sigwinch = 1;
34         break;
35     }
36 }
37
38 static struct timeval timeval_add(struct timeval a, struct timeval b)
39 {
40     int usec = a.tv_usec + b.tv_usec;
41     a.tv_sec += b.tv_sec;
42     while (usec > 1000 * 1000) {
43         a.tv_sec += 1;
44         usec -= 1000 * 1000;
45     }
46     a.tv_usec = usec;
47     return a;
48 }
49
50 static int is_expired(struct timeval now, struct timeval expiry)
51 {
52     return now.tv_sec > expiry.tv_sec
53         || (now.tv_sec == expiry.tv_sec && now.tv_usec > expiry.tv_usec);
54 }
55
56 int main(void)
57 {
58     madtty_t *rt;
59     int dirty = 0;
60     struct timeval next;
61
62     signal(SIGCHLD, handler);
63     signal(SIGWINCH, handler);
64
65     madtty_initialize();
66     getmaxyx(stdscr, screen_h, screen_w);
67
68     /* create a window with a frame */
69     term_win = newwin(screen_h - 2, screen_w - 2, 1, 1);
70     rt = madtty_create(screen_h - 2, screen_w -2);
71     {
72         const char *path = getenv("SHELL") ?: "/bin/sh";
73         const char *args[] = { path, "--login", NULL};
74
75         madtty_forkpty(rt, path, args);
76     }
77
78     /* keep reading keypresses from the user and passing them to the terminal;
79      * also, redraw the terminal to the window at each iteration */
80     gettimeofday(&next, NULL);
81     while (!getout) {
82         struct timeval tv = { 0, 1000 * 1000 / 100 };
83         fd_set rfds;
84         int ch;
85
86         FD_ZERO(&rfds);
87         FD_SET(0, &rfds);
88         FD_SET(rt->pty, &rfds);
89
90         if (select(rt->pty + 1, &rfds, NULL, NULL, &tv) > 0) {
91             if (FD_ISSET(rt->pty, &rfds)) {
92                 madtty_process(rt);
93                 dirty = 1;
94             }
95         }
96
97         if (sigwinch) {
98             int fd, cols = -1, rows = -1;
99             struct winsize w;
100
101             if ((fd = open("/dev/tty", O_RDONLY)) != -1) {
102                 if (ioctl(fd, TIOCGWINSZ, &w) != -1) {
103                     rows = w.ws_row;
104                     cols = w.ws_col;
105                 }
106                 close(fd);
107             }
108             if (rows <= 0) {
109                 rows = atoi(getenv("LINES") ?: "24");
110             }
111             if (cols <= 0) {
112                 cols = atoi(getenv("COLUMNS") ?: "80");
113             }
114
115             resizeterm(rows, cols);
116             madtty_resize(rt, rows - 2, cols - 2);
117             wresize(term_win, rows - 2, cols - 2);
118             sigwinch = 0;
119             erase();
120         }
121
122         while ((ch = getch()) != ERR) {
123             madtty_keypress(rt, ch); /* pass the keypress for handling */
124             dirty = 1;
125         }
126
127         gettimeofday(&tv, NULL);
128         if (dirty && is_expired(tv, next)) {
129             madtty_draw(rt, term_win, 0, 0);
130             wrefresh(term_win);
131             dirty = 0;
132             next = timeval_add(tv, slice);
133         }
134     }
135
136     endwin();
137     return 0;
138 }