blob: eb427edf90b8b4ec4aa149dbd0cb79cce5cf1958 [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"
David Sodmanbbcb0522014-09-19 10:34:07 -070020#include "dbus_interface.h"
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070021
22struct term {
23 struct tsm_screen *screen;
24 struct tsm_vte *vte;
25 struct shl_pty *pty;
26 int pty_bridge;
27 int pid;
28 tsm_age_t age;
29 int char_x, char_y;
30 int pitch;
31 uint32_t *dst_image;
32 int shift_state;
33 int control_state;
David Sodmanbbcb0522014-09-19 10:34:07 -070034 int alt_state;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070035};
36
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070037
38static void __attribute__ ((noreturn)) term_run_child()
39{
40 char **argv = (char *[]) {
41 getenv("SHELL") ? : _PATH_BSHELL,
42 "-il",
43 NULL
44 };
45
46 printf("Welcome to frecon!\n");
47 printf("running %s\n", argv[0]);
48 /* XXX figure out how to fix "top" for xterm-256color */
49 setenv("TERM", "xterm", 1);
50 execve(argv[0], argv, environ);
51 exit(1);
52}
53
54static int term_draw_cell(struct tsm_screen *screen, uint32_t id,
David Sodmanbbcb0522014-09-19 10:34:07 -070055 const uint32_t *ch, size_t len,
56 unsigned int cwidth, unsigned int posx,
57 unsigned int posy,
58 const struct tsm_screen_attr *attr,
59 tsm_age_t age, void *data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070060{
David Sodmanbbcb0522014-09-19 10:34:07 -070061 terminal_t *terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070062 uint32_t front_color, back_color;
63
David Sodmanbbcb0522014-09-19 10:34:07 -070064 if (age && terminal->term->age && age <= terminal->term->age)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070065 return 0;
66
67 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
68 back_color = (attr->br << 16) | (attr->bg << 8) | attr->bb;
69
70 if (attr->inverse) {
71 uint32_t tmp = front_color;
72 front_color = back_color;
73 back_color = tmp;
74 }
75
76 if (len)
David Sodmanbbcb0522014-09-19 10:34:07 -070077 font_render(terminal->term->dst_image, posx, posy, terminal->term->pitch, *ch,
78 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070079 else
David Sodmanbbcb0522014-09-19 10:34:07 -070080 font_fillchar(terminal->term->dst_image, posx, posy, terminal->term->pitch,
81 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070082
83 return 0;
84}
85
David Sodmanbbcb0522014-09-19 10:34:07 -070086void term_redraw(terminal_t *terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070087{
David Sodmanbbcb0522014-09-19 10:34:07 -070088 uint32_t *video_buffer;
89 video_buffer = video_lock(terminal->video);
90 if (video_buffer != NULL) {
91 terminal->term->dst_image = video_buffer;
92 terminal->term->age =
93 tsm_screen_draw(terminal->term->screen, term_draw_cell, terminal);
94 video_unlock(terminal->video);
95 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070096}
97
David Sodmanbbcb0522014-09-19 10:34:07 -070098void term_key_event(terminal_t* terminal, uint32_t keysym, int32_t unicode)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070099{
100
David Sodmanbbcb0522014-09-19 10:34:07 -0700101 if (tsm_vte_handle_keyboard(terminal->term->vte, keysym, 0, 0, unicode))
102 tsm_screen_sb_reset(terminal->term->screen);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700103
David Sodmanbbcb0522014-09-19 10:34:07 -0700104 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700105}
106
107static void term_read_cb(struct shl_pty *pty, char *u8, size_t len, void *data)
108{
David Sodmanbbcb0522014-09-19 10:34:07 -0700109 terminal_t *terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700110
David Sodmanbbcb0522014-09-19 10:34:07 -0700111 tsm_vte_input(terminal->term->vte, u8, len);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700112
David Sodmanbbcb0522014-09-19 10:34:07 -0700113 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700114}
115
116static void term_write_cb(struct tsm_vte *vte, const char *u8, size_t len,
David Sodmanbbcb0522014-09-19 10:34:07 -0700117 void *data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700118{
119 struct term *term = data;
120 int r;
121
122 r = shl_pty_write(term->pty, u8, len);
123 if (r < 0)
David Sodmanbbcb0522014-09-19 10:34:07 -0700124 LOG(ERROR, "OOM in pty-write (%d)", r);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700125
126 shl_pty_dispatch(term->pty);
127}
128
129static const char *sev2str_table[] = {
130 "FATAL",
131 "ALERT",
132 "CRITICAL",
133 "ERROR",
134 "WARNING",
135 "NOTICE",
136 "INFO",
137 "DEBUG"
138};
139
140static const char *sev2str(unsigned int sev)
141{
142 if (sev > 7)
143 return "DEBUG";
144
145 return sev2str_table[sev];
146}
147
Dominik Behr00003502014-08-15 16:42:37 -0700148#ifdef __clang__
149__attribute__((__format__ (__printf__, 7, 0)))
150#endif
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700151static void log_tsm(void *data, const char *file, int line, const char *fn,
David Sodmanbbcb0522014-09-19 10:34:07 -0700152 const char *subs, unsigned int sev, const char *format,
153 va_list args)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700154{
155 fprintf(stderr, "%s: %s: ", sev2str(sev), subs);
156 vfprintf(stderr, format, args);
157 fprintf(stderr, "\n");
158}
159
David Sodmanbbcb0522014-09-19 10:34:07 -0700160static int term_special_key(terminal_t *terminal, struct input_key_event *ev)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700161{
Dominik Behr98514382014-09-24 01:17:50 -0700162 unsigned int i;
163
164 uint32_t ignore_keys[] = {
165 BTN_TOUCH, // touchpad events
166 BTN_TOOL_FINGER,
167 BTN_TOOL_DOUBLETAP,
168 BTN_TOOL_TRIPLETAP,
169 BTN_TOOL_QUADTAP,
170 BTN_TOOL_QUINTTAP,
171 BTN_LEFT, // mouse buttons
172 BTN_RIGHT,
173 BTN_MIDDLE,
174 BTN_SIDE,
175 BTN_EXTRA,
176 BTN_FORWARD,
177 BTN_BACK,
178 BTN_TASK
179 };
180
181 for (i = 0; i < ARRAY_SIZE(ignore_keys); i++)
182 if (ev->code == ignore_keys[i])
183 return 1;
184
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700185 switch (ev->code) {
186 case KEY_LEFTSHIFT:
187 case KEY_RIGHTSHIFT:
David Sodmanbbcb0522014-09-19 10:34:07 -0700188 terminal->term->shift_state = ! !ev->value;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700189 return 1;
190 case KEY_LEFTCTRL:
191 case KEY_RIGHTCTRL:
David Sodmanbbcb0522014-09-19 10:34:07 -0700192 terminal->term->control_state = ! !ev->value;
193 return 1;
194 case KEY_LEFTALT:
195 case KEY_RIGHTALT:
196 terminal->term->alt_state = ! !ev->value;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700197 return 1;
198 }
199
David Sodmanbbcb0522014-09-19 10:34:07 -0700200 if (terminal->term->shift_state && ev->value) {
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700201 switch (ev->code) {
202 case KEY_PAGEUP:
David Sodmanbbcb0522014-09-19 10:34:07 -0700203 tsm_screen_sb_page_up(terminal->term->screen, 1);
204 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700205 return 1;
206 case KEY_PAGEDOWN:
David Sodmanbbcb0522014-09-19 10:34:07 -0700207 tsm_screen_sb_page_down(terminal->term->screen, 1);
208 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700209 return 1;
210 case KEY_UP:
David Sodmanbbcb0522014-09-19 10:34:07 -0700211 tsm_screen_sb_up(terminal->term->screen, 1);
212 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700213 return 1;
214 case KEY_DOWN:
David Sodmanbbcb0522014-09-19 10:34:07 -0700215 tsm_screen_sb_down(terminal->term->screen, 1);
216 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700217 return 1;
218 }
219 }
220
David Sodmanbbcb0522014-09-19 10:34:07 -0700221 if (terminal->term->alt_state && terminal->term->control_state && ev->value) {
222 int console_id;
223 switch (ev->code) {
224 case KEY_F1:
225 input_ungrab();
226 terminal->active = false;
227 case KEY_F2:
228 case KEY_F3:
229 case KEY_F4:
230 case KEY_F5:
231 case KEY_F6:
232 case KEY_F7:
233 case KEY_F8:
234 case KEY_F9:
235 case KEY_F10:
236 console_id = ev->code - KEY_F1 + 1;
237
238 (void)dbus_method_call(terminal->dbus,
239 kLibCrosServiceName,
240 kLibCrosServicePath,
241 kLibCrosServiceInterface,
242 kActivateConsoleMethod,
243 &console_id);
244
245 if (ev->code == KEY_F2) {
246 terminal->active = true;
247 input_grab();
248 video_setmode(terminal->video);
249 term_redraw(terminal);
250 }
251 return 1;
252
253 }
254 }
255
256
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700257 return 0;
258}
259
David Sodmanbbcb0522014-09-19 10:34:07 -0700260static void term_get_keysym_and_unicode(terminal_t* terminal,
261 struct input_key_event *event,
262 uint32_t *keysym, uint32_t *unicode)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700263{
264 struct {
265 uint32_t code;
266 uint32_t keysym;
267 } non_ascii_keys[] = {
268 { KEY_ESC, KEYSYM_ESC},
269 { KEY_HOME, KEYSYM_HOME},
270 { KEY_LEFT, KEYSYM_LEFT},
271 { KEY_UP, KEYSYM_UP},
272 { KEY_RIGHT, KEYSYM_RIGHT},
273 { KEY_DOWN, KEYSYM_DOWN},
274 { KEY_PAGEUP, KEYSYM_PAGEUP},
275 { KEY_PAGEDOWN, KEYSYM_PAGEDOWN},
276 { KEY_END, KEYSYM_END},
277 { KEY_INSERT, KEYSYM_INSERT},
278 { KEY_DELETE, KEYSYM_DELETE},
279 };
280
281 for (unsigned i = 0; i < ARRAY_SIZE(non_ascii_keys); i++) {
282 if (non_ascii_keys[i].code == event->code) {
283 *keysym = non_ascii_keys[i].keysym;
284 *unicode = -1;
285 return;
286 }
287 }
288
289 if (event->code >= ARRAY_SIZE(keysym_table) / 2) {
290 *keysym = '?';
291 } else {
David Sodmanbbcb0522014-09-19 10:34:07 -0700292 *keysym = keysym_table[event->code * 2 + terminal->term->shift_state];
293 if ((terminal->term->control_state) && isascii(*keysym))
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700294 *keysym = tolower(*keysym) - 'a' + 1;
295 }
296
297 *unicode = *keysym;
298}
299
David Sodmanbbcb0522014-09-19 10:34:07 -0700300int term_run(terminal_t* terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700301{
David Sodmanbbcb0522014-09-19 10:34:07 -0700302 int pty_fd = terminal->term->pty_bridge;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700303 fd_set read_set, exception_set;
304
David Sodmanbbcb0522014-09-19 10:34:07 -0700305 video_setmode(terminal->video);
306
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700307 while (1) {
308 FD_ZERO(&read_set);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700309 FD_ZERO(&exception_set);
Dominik Behr93899452014-08-18 22:16:21 -0700310 FD_SET(pty_fd, &read_set);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700311 FD_SET(pty_fd, &exception_set);
Dominik Behr93899452014-08-18 22:16:21 -0700312 int maxfd = input_setfds(&read_set, &exception_set);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700313
Dominik Behr93899452014-08-18 22:16:21 -0700314 maxfd = MAX(maxfd, pty_fd) + 1;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700315
316 select(maxfd, &read_set, NULL, &exception_set, NULL);
317
Dominik Behr93899452014-08-18 22:16:21 -0700318 if (FD_ISSET(pty_fd, &exception_set))
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700319 return -1;
320
Dominik Behr93899452014-08-18 22:16:21 -0700321 struct input_key_event *event;
322 event = input_get_event(&read_set, &exception_set);
323 if (event) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700324 if (!term_special_key(terminal, event) && event->value) {
Dominik Behr93899452014-08-18 22:16:21 -0700325 uint32_t keysym, unicode;
David Sodmanbbcb0522014-09-19 10:34:07 -0700326 if (terminal->active) {
327 term_get_keysym_and_unicode(terminal, event,
328 &keysym,
329 &unicode);
330 term_key_event(terminal, keysym, unicode);
331 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700332 }
Dominik Behr93899452014-08-18 22:16:21 -0700333
334 input_put_event(event);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700335 }
336
337 if (FD_ISSET(pty_fd, &read_set)) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700338 shl_pty_bridge_dispatch(terminal->term->pty_bridge, 0);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700339 }
340 }
341 return 0;
342}
343
David Sodmanbbcb0522014-09-19 10:34:07 -0700344terminal_t* term_init(video_t* video)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700345{
346 const int scrollback_size = 200;
347 uint32_t char_width, char_height;
David Sodmanbbcb0522014-09-19 10:34:07 -0700348 int status;
349 terminal_t *new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700350
David Sodmanbbcb0522014-09-19 10:34:07 -0700351 new_terminal = (terminal_t*)calloc(1, sizeof(*new_terminal));
352 new_terminal->video = video;
353 new_terminal->term = (struct term*)calloc(1, sizeof(*new_terminal->term));
354
355 font_init(video_getscaling(video));
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700356 font_get_size(&char_width, &char_height);
357
David Sodmanbbcb0522014-09-19 10:34:07 -0700358 new_terminal->term->char_x = video_getwidth(video) / char_width;
359 new_terminal->term->char_y = video_getheight(video) / char_height;
360 new_terminal->term->pitch = video_getpitch(video);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700361
David Sodmanbbcb0522014-09-19 10:34:07 -0700362 status = tsm_screen_new(&new_terminal->term->screen,
363 log_tsm, new_terminal->term);
364 if (new_terminal < 0) {
365 term_close(new_terminal);
366 return NULL;
367 }
368
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700369
David Sodmanbbcb0522014-09-19 10:34:07 -0700370 tsm_screen_set_max_sb(new_terminal->term->screen, scrollback_size);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700371
David Sodmanbbcb0522014-09-19 10:34:07 -0700372 status = tsm_vte_new(&new_terminal->term->vte, new_terminal->term->screen,
373 term_write_cb, new_terminal->term, log_tsm, new_terminal->term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700374
David Sodmanbbcb0522014-09-19 10:34:07 -0700375 if (status < 0) {
376 term_close(new_terminal);
377 return NULL;
378 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700379
David Sodmanbbcb0522014-09-19 10:34:07 -0700380 new_terminal->term->pty_bridge = shl_pty_bridge_new();
381 if (new_terminal->term->pty_bridge < 0) {
382 term_close(new_terminal);
383 return NULL;
384 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700385
David Sodmanbbcb0522014-09-19 10:34:07 -0700386 status = shl_pty_open(&new_terminal->term->pty,
387 term_read_cb, new_terminal, new_terminal->term->char_x,
388 new_terminal->term->char_y);
389 if (status < 0) {
390 term_close(new_terminal);
391 return NULL;
392 } else if (status == 0) {
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700393 term_run_child();
394 exit(1);
395 }
396
David Sodmanbbcb0522014-09-19 10:34:07 -0700397 status = shl_pty_bridge_add(new_terminal->term->pty_bridge, new_terminal->term->pty);
398 if (status) {
399 shl_pty_close(new_terminal->term->pty);
400 term_close(new_terminal);
401 return NULL;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700402 }
403
David Sodmanbbcb0522014-09-19 10:34:07 -0700404 new_terminal->term->pid = shl_pty_get_child(new_terminal->term->pty);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700405
David Sodmanbbcb0522014-09-19 10:34:07 -0700406 status = tsm_screen_resize(new_terminal->term->screen,
407 new_terminal->term->char_x, new_terminal->term->char_y);
408 if (status < 0) {
409 shl_pty_close(new_terminal->term->pty);
410 term_close(new_terminal);
411 return NULL;
412 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700413
David Sodmanbbcb0522014-09-19 10:34:07 -0700414 status = shl_pty_resize(new_terminal->term->pty, new_terminal->term->char_x, new_terminal->term->char_y);
415 if (status < 0) {
416 shl_pty_close(new_terminal->term->pty);
417 term_close(new_terminal);
418 return NULL;
419 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700420
David Sodmanbbcb0522014-09-19 10:34:07 -0700421 return new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700422}
423
David Sodmanbbcb0522014-09-19 10:34:07 -0700424void term_set_dbus(terminal_t *term, dbus_t* dbus)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700425{
David Sodmanbbcb0522014-09-19 10:34:07 -0700426 term->dbus = dbus;
427}
428
429void term_close(terminal_t *term)
430{
431 if (!term)
432 return;
433
434 if (term->term) {
435 free(term->term);
436 term->term = NULL;
437 }
438
439 free(term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700440}