blob: 8e1a6caa43a2f7dbcc38d638f184b03b2c0d062a [file] [log] [blame]
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -07001/*
2 * Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7#include <ctype.h>
8#include <libtsm.h>
9#include <paths.h>
10#include <stdio.h>
Dominik Behr93899452014-08-18 22:16:21 -070011#include <sys/select.h>
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070012
13#include "font.h"
14#include "input.h"
15#include "keysym.h"
16#include "shl_pty.h"
17#include "term.h"
18#include "util.h"
19#include "video.h"
20
21struct term {
22 struct tsm_screen *screen;
23 struct tsm_vte *vte;
24 struct shl_pty *pty;
25 int pty_bridge;
26 int pid;
27 tsm_age_t age;
28 int char_x, char_y;
29 int pitch;
30 uint32_t *dst_image;
31 int shift_state;
32 int control_state;
33};
34
35static struct term term;
36
37static void __attribute__ ((noreturn)) term_run_child()
38{
39 char **argv = (char *[]) {
40 getenv("SHELL") ? : _PATH_BSHELL,
41 "-il",
42 NULL
43 };
44
45 printf("Welcome to frecon!\n");
46 printf("running %s\n", argv[0]);
47 /* XXX figure out how to fix "top" for xterm-256color */
48 setenv("TERM", "xterm", 1);
49 execve(argv[0], argv, environ);
50 exit(1);
51}
52
53static int term_draw_cell(struct tsm_screen *screen, uint32_t id,
Stéphane Marchesinf8807af2014-08-18 10:34:41 -070054 const uint32_t *ch, size_t len,
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070055 unsigned int cwidth, unsigned int posx,
56 unsigned int posy,
57 const struct tsm_screen_attr *attr,
58 tsm_age_t age, void *data)
59{
60 uint32_t front_color, back_color;
61
62 if (age && term.age && age <= term.age)
63 return 0;
64
65 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
66 back_color = (attr->br << 16) | (attr->bg << 8) | attr->bb;
67
68 if (attr->inverse) {
69 uint32_t tmp = front_color;
70 front_color = back_color;
71 back_color = tmp;
72 }
73
74 if (len)
75 font_render(term.dst_image, posx, posy, term.pitch, *ch,
76 front_color, back_color);
77 else
78 font_fillchar(term.dst_image, posx, posy, term.pitch,
79 front_color, back_color);
80
81 return 0;
82}
83
84void term_redraw()
85{
86 term.dst_image = video_lock();
87 term.age = tsm_screen_draw(term.screen, term_draw_cell, NULL);
88 video_unlock();
89}
90
91void term_key_event(uint32_t keysym, int32_t unicode)
92{
93
94 if (tsm_vte_handle_keyboard(term.vte, keysym, 0, 0, unicode))
95 tsm_screen_sb_reset(term.screen);
96
97 term_redraw();
98}
99
100static void term_read_cb(struct shl_pty *pty, char *u8, size_t len, void *data)
101{
102 struct term *term = data;
103
104 tsm_vte_input(term->vte, u8, len);
105
106 term_redraw();
107}
108
109static void term_write_cb(struct tsm_vte *vte, const char *u8, size_t len,
110 void *data)
111{
112 struct term *term = data;
113 int r;
114
115 r = shl_pty_write(term->pty, u8, len);
116 if (r < 0)
117 printf("OOM in pty-write (%d)", r);
118
119 shl_pty_dispatch(term->pty);
120}
121
122static const char *sev2str_table[] = {
123 "FATAL",
124 "ALERT",
125 "CRITICAL",
126 "ERROR",
127 "WARNING",
128 "NOTICE",
129 "INFO",
130 "DEBUG"
131};
132
133static const char *sev2str(unsigned int sev)
134{
135 if (sev > 7)
136 return "DEBUG";
137
138 return sev2str_table[sev];
139}
140
Dominik Behr00003502014-08-15 16:42:37 -0700141#ifdef __clang__
142__attribute__((__format__ (__printf__, 7, 0)))
143#endif
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700144static void log_tsm(void *data, const char *file, int line, const char *fn,
145 const char *subs, unsigned int sev, const char *format,
146 va_list args)
147{
148 fprintf(stderr, "%s: %s: ", sev2str(sev), subs);
149 vfprintf(stderr, format, args);
150 fprintf(stderr, "\n");
151}
152
153static int term_special_key(struct input_key_event *ev)
154{
155 switch (ev->code) {
156 case KEY_LEFTSHIFT:
157 case KEY_RIGHTSHIFT:
158 term.shift_state = ! !ev->value;
159 return 1;
160 case KEY_LEFTCTRL:
161 case KEY_RIGHTCTRL:
162 term.control_state = ! !ev->value;
163 return 1;
164 }
165
166 if (term.shift_state && ev->value) {
167 switch (ev->code) {
168 case KEY_PAGEUP:
169 tsm_screen_sb_page_up(term.screen, 1);
170 term_redraw();
171 return 1;
172 case KEY_PAGEDOWN:
173 tsm_screen_sb_page_down(term.screen, 1);
174 term_redraw();
175 return 1;
176 case KEY_UP:
177 tsm_screen_sb_up(term.screen, 1);
178 term_redraw();
179 return 1;
180 case KEY_DOWN:
181 tsm_screen_sb_down(term.screen, 1);
182 term_redraw();
183 return 1;
184 }
185 }
186
187 return 0;
188}
189
190static void term_get_keysym_and_unicode(struct input_key_event *event,
Stéphane Marchesinf8807af2014-08-18 10:34:41 -0700191 uint32_t *keysym, uint32_t *unicode)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700192{
193 struct {
194 uint32_t code;
195 uint32_t keysym;
196 } non_ascii_keys[] = {
197 { KEY_ESC, KEYSYM_ESC},
198 { KEY_HOME, KEYSYM_HOME},
199 { KEY_LEFT, KEYSYM_LEFT},
200 { KEY_UP, KEYSYM_UP},
201 { KEY_RIGHT, KEYSYM_RIGHT},
202 { KEY_DOWN, KEYSYM_DOWN},
203 { KEY_PAGEUP, KEYSYM_PAGEUP},
204 { KEY_PAGEDOWN, KEYSYM_PAGEDOWN},
205 { KEY_END, KEYSYM_END},
206 { KEY_INSERT, KEYSYM_INSERT},
207 { KEY_DELETE, KEYSYM_DELETE},
208 };
209
210 for (unsigned i = 0; i < ARRAY_SIZE(non_ascii_keys); i++) {
211 if (non_ascii_keys[i].code == event->code) {
212 *keysym = non_ascii_keys[i].keysym;
213 *unicode = -1;
214 return;
215 }
216 }
217
218 if (event->code >= ARRAY_SIZE(keysym_table) / 2) {
219 *keysym = '?';
220 } else {
221 *keysym = keysym_table[event->code * 2 + term.shift_state];
222 if ((term.control_state) && isascii(*keysym))
223 *keysym = tolower(*keysym) - 'a' + 1;
224 }
225
226 *unicode = *keysym;
227}
228
229int term_run()
230{
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700231 int pty_fd = term.pty_bridge;
232 fd_set read_set, exception_set;
233
234 while (1) {
235 FD_ZERO(&read_set);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700236 FD_ZERO(&exception_set);
Dominik Behr93899452014-08-18 22:16:21 -0700237 FD_SET(pty_fd, &read_set);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700238 FD_SET(pty_fd, &exception_set);
Dominik Behr93899452014-08-18 22:16:21 -0700239 int maxfd = input_setfds(&read_set, &exception_set);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700240
Dominik Behr93899452014-08-18 22:16:21 -0700241 maxfd = MAX(maxfd, pty_fd) + 1;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700242
243 select(maxfd, &read_set, NULL, &exception_set, NULL);
244
Dominik Behr93899452014-08-18 22:16:21 -0700245 if (FD_ISSET(pty_fd, &exception_set))
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700246 return -1;
247
Dominik Behr93899452014-08-18 22:16:21 -0700248 struct input_key_event *event;
249 event = input_get_event(&read_set, &exception_set);
250 if (event) {
251 if (!term_special_key(event) && event->value) {
252 uint32_t keysym, unicode;
253 term_get_keysym_and_unicode(event,
254 &keysym,
255 &unicode);
256 term_key_event(keysym, unicode);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700257 }
Dominik Behr93899452014-08-18 22:16:21 -0700258
259 input_put_event(event);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700260 }
261
262 if (FD_ISSET(pty_fd, &read_set)) {
263 shl_pty_bridge_dispatch(term.pty_bridge, 0);
264 }
265 }
266 return 0;
267}
268
Stéphane Marchesinaf4423b2014-08-13 16:39:24 -0700269int term_init(int32_t width, int32_t height, int32_t pitch, int32_t scaling)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700270{
271 const int scrollback_size = 200;
272 uint32_t char_width, char_height;
273 int ret;
274
Stéphane Marchesinaf4423b2014-08-13 16:39:24 -0700275 font_init(scaling);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700276 font_get_size(&char_width, &char_height);
277
278 term.char_x = width / char_width;
279 term.char_y = height / char_height;
280 term.pitch = pitch;
281
282 ret = tsm_screen_new(&term.screen, log_tsm, &term);
283 if (ret < 0)
284 return ret;
285
286 tsm_screen_set_max_sb(term.screen, scrollback_size);
287
288 ret = tsm_vte_new(&term.vte, term.screen, term_write_cb, &term, log_tsm,
289 &term);
290
291 if (ret < 0)
292 return ret;
293
294 term.pty_bridge = shl_pty_bridge_new();
295 if (term.pty_bridge < 0)
296 return term.pty_bridge;
297
298 ret = shl_pty_open(&term.pty, term_read_cb, &term, term.char_x,
299 term.char_y);
300 if (ret < 0) {
301 return ret;
302 } else if (!ret) {
303 term_run_child();
304 exit(1);
305 }
306
307 ret = shl_pty_bridge_add(term.pty_bridge, term.pty);
308 if (ret) {
309 shl_pty_close(term.pty);
310 return ret;
311 }
312
313 term.pid = shl_pty_get_child(term.pty);
314
315 ret = tsm_screen_resize(term.screen, term.char_x, term.char_y);
316 if (ret < 0)
317 return ret;
318
319 ret = shl_pty_resize(term.pty, term.char_x, term.char_y);
320 if (ret < 0)
321 return ret;
322
323 return 0;
324}
325
326void term_close()
327{
328}