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