blob: 7ecfdc5b079863de106409c2f2542d86c4c73975 [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
Dominik Behr00003502014-08-15 16:42:37 -0700140#ifdef __clang__
141__attribute__((__format__ (__printf__, 7, 0)))
142#endif
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700143static void log_tsm(void *data, const char *file, int line, const char *fn,
144 const char *subs, unsigned int sev, const char *format,
145 va_list args)
146{
147 fprintf(stderr, "%s: %s: ", sev2str(sev), subs);
148 vfprintf(stderr, format, args);
149 fprintf(stderr, "\n");
150}
151
152static int term_special_key(struct input_key_event *ev)
153{
154 switch (ev->code) {
155 case KEY_LEFTSHIFT:
156 case KEY_RIGHTSHIFT:
157 term.shift_state = ! !ev->value;
158 return 1;
159 case KEY_LEFTCTRL:
160 case KEY_RIGHTCTRL:
161 term.control_state = ! !ev->value;
162 return 1;
163 }
164
165 if (term.shift_state && ev->value) {
166 switch (ev->code) {
167 case KEY_PAGEUP:
168 tsm_screen_sb_page_up(term.screen, 1);
169 term_redraw();
170 return 1;
171 case KEY_PAGEDOWN:
172 tsm_screen_sb_page_down(term.screen, 1);
173 term_redraw();
174 return 1;
175 case KEY_UP:
176 tsm_screen_sb_up(term.screen, 1);
177 term_redraw();
178 return 1;
179 case KEY_DOWN:
180 tsm_screen_sb_down(term.screen, 1);
181 term_redraw();
182 return 1;
183 }
184 }
185
186 return 0;
187}
188
189static void term_get_keysym_and_unicode(struct input_key_event *event,
190 uint32_t * keysym, uint32_t * unicode)
191{
192 struct {
193 uint32_t code;
194 uint32_t keysym;
195 } non_ascii_keys[] = {
196 { KEY_ESC, KEYSYM_ESC},
197 { KEY_HOME, KEYSYM_HOME},
198 { KEY_LEFT, KEYSYM_LEFT},
199 { KEY_UP, KEYSYM_UP},
200 { KEY_RIGHT, KEYSYM_RIGHT},
201 { KEY_DOWN, KEYSYM_DOWN},
202 { KEY_PAGEUP, KEYSYM_PAGEUP},
203 { KEY_PAGEDOWN, KEYSYM_PAGEDOWN},
204 { KEY_END, KEYSYM_END},
205 { KEY_INSERT, KEYSYM_INSERT},
206 { KEY_DELETE, KEYSYM_DELETE},
207 };
208
209 for (unsigned i = 0; i < ARRAY_SIZE(non_ascii_keys); i++) {
210 if (non_ascii_keys[i].code == event->code) {
211 *keysym = non_ascii_keys[i].keysym;
212 *unicode = -1;
213 return;
214 }
215 }
216
217 if (event->code >= ARRAY_SIZE(keysym_table) / 2) {
218 *keysym = '?';
219 } else {
220 *keysym = keysym_table[event->code * 2 + term.shift_state];
221 if ((term.control_state) && isascii(*keysym))
222 *keysym = tolower(*keysym) - 'a' + 1;
223 }
224
225 *unicode = *keysym;
226}
227
228int term_run()
229{
230 int input_fd = input_get_fd();
231 int pty_fd = term.pty_bridge;
232 fd_set read_set, exception_set;
233
234 while (1) {
235 FD_ZERO(&read_set);
236 FD_SET(input_fd, &read_set);
237 FD_SET(pty_fd, &read_set);
238 FD_ZERO(&exception_set);
239 FD_SET(input_fd, &exception_set);
240 FD_SET(pty_fd, &exception_set);
241
242 int maxfd = MAX(input_fd, pty_fd) + 1;
243
244 select(maxfd, &read_set, NULL, &exception_set, NULL);
245
246 if (FD_ISSET(input_fd, &exception_set)
247 || FD_ISSET(pty_fd, &exception_set))
248 return -1;
249
250 if (FD_ISSET(input_fd, &read_set)) {
251 struct input_key_event *event;
252 event = input_get_event();
253 if (event) {
254 if (!term_special_key(event) && event->value) {
255 uint32_t keysym, unicode;
256 term_get_keysym_and_unicode(event,
257 &keysym,
258 &unicode);
259 term_key_event(keysym, unicode);
260 }
261
262 input_put_event(event);
263 }
264 }
265
266 if (FD_ISSET(pty_fd, &read_set)) {
267 shl_pty_bridge_dispatch(term.pty_bridge, 0);
268 }
269 }
270 return 0;
271}
272
Stéphane Marchesinaf4423b2014-08-13 16:39:24 -0700273int term_init(int32_t width, int32_t height, int32_t pitch, int32_t scaling)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700274{
275 const int scrollback_size = 200;
276 uint32_t char_width, char_height;
277 int ret;
278
Stéphane Marchesinaf4423b2014-08-13 16:39:24 -0700279 font_init(scaling);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700280 font_get_size(&char_width, &char_height);
281
282 term.char_x = width / char_width;
283 term.char_y = height / char_height;
284 term.pitch = pitch;
285
286 ret = tsm_screen_new(&term.screen, log_tsm, &term);
287 if (ret < 0)
288 return ret;
289
290 tsm_screen_set_max_sb(term.screen, scrollback_size);
291
292 ret = tsm_vte_new(&term.vte, term.screen, term_write_cb, &term, log_tsm,
293 &term);
294
295 if (ret < 0)
296 return ret;
297
298 term.pty_bridge = shl_pty_bridge_new();
299 if (term.pty_bridge < 0)
300 return term.pty_bridge;
301
302 ret = shl_pty_open(&term.pty, term_read_cb, &term, term.char_x,
303 term.char_y);
304 if (ret < 0) {
305 return ret;
306 } else if (!ret) {
307 term_run_child();
308 exit(1);
309 }
310
311 ret = shl_pty_bridge_add(term.pty_bridge, term.pty);
312 if (ret) {
313 shl_pty_close(term.pty);
314 return ret;
315 }
316
317 term.pid = shl_pty_get_child(term.pty);
318
319 ret = tsm_screen_resize(term.screen, term.char_x, term.char_y);
320 if (ret < 0)
321 return ret;
322
323 ret = shl_pty_resize(term.pty, term.char_x, term.char_y);
324 if (ret < 0)
325 return ret;
326
327 return 0;
328}
329
330void term_close()
331{
332}