blob: 76de3f11831ae08cc37fd1c454591d9570478aee [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) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700222 switch (ev->code) {
223 case KEY_F1:
224 input_ungrab();
225 terminal->active = false;
Stéphane Marchesin66399132014-11-06 12:03:26 -0800226 (void)dbus_method_call0(terminal->dbus,
227 kLibCrosServiceName,
228 kLibCrosServicePath,
229 kLibCrosServiceInterface,
230 kTakeDisplayOwnership);
231 break;
David Sodmanbbcb0522014-09-19 10:34:07 -0700232 case KEY_F2:
233 case KEY_F3:
234 case KEY_F4:
235 case KEY_F5:
236 case KEY_F6:
237 case KEY_F7:
238 case KEY_F8:
239 case KEY_F9:
240 case KEY_F10:
Stéphane Marchesin66399132014-11-06 12:03:26 -0800241 (void)dbus_method_call0(terminal->dbus,
242 kLibCrosServiceName,
243 kLibCrosServicePath,
244 kLibCrosServiceInterface,
245 kReleaseDisplayOwnership);
246 break;
David Sodmanbbcb0522014-09-19 10:34:07 -0700247 }
David Sodman003faed2014-11-03 09:02:10 -0800248
Stéphane Marchesin66399132014-11-06 12:03:26 -0800249 if (ev->code == KEY_F2) {
250 terminal->active = true;
251 input_grab();
252 video_setmode(terminal->video);
253 term_redraw(terminal);
254 }
255 return 1;
David Sodman003faed2014-11-03 09:02:10 -0800256
David Sodmanbbcb0522014-09-19 10:34:07 -0700257 }
258
259
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700260 return 0;
261}
262
David Sodmanbbcb0522014-09-19 10:34:07 -0700263static void term_get_keysym_and_unicode(terminal_t* terminal,
264 struct input_key_event *event,
265 uint32_t *keysym, uint32_t *unicode)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700266{
267 struct {
268 uint32_t code;
269 uint32_t keysym;
270 } non_ascii_keys[] = {
271 { KEY_ESC, KEYSYM_ESC},
272 { KEY_HOME, KEYSYM_HOME},
273 { KEY_LEFT, KEYSYM_LEFT},
274 { KEY_UP, KEYSYM_UP},
275 { KEY_RIGHT, KEYSYM_RIGHT},
276 { KEY_DOWN, KEYSYM_DOWN},
277 { KEY_PAGEUP, KEYSYM_PAGEUP},
278 { KEY_PAGEDOWN, KEYSYM_PAGEDOWN},
279 { KEY_END, KEYSYM_END},
280 { KEY_INSERT, KEYSYM_INSERT},
281 { KEY_DELETE, KEYSYM_DELETE},
282 };
283
284 for (unsigned i = 0; i < ARRAY_SIZE(non_ascii_keys); i++) {
285 if (non_ascii_keys[i].code == event->code) {
286 *keysym = non_ascii_keys[i].keysym;
287 *unicode = -1;
288 return;
289 }
290 }
291
292 if (event->code >= ARRAY_SIZE(keysym_table) / 2) {
293 *keysym = '?';
294 } else {
David Sodmanbbcb0522014-09-19 10:34:07 -0700295 *keysym = keysym_table[event->code * 2 + terminal->term->shift_state];
296 if ((terminal->term->control_state) && isascii(*keysym))
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700297 *keysym = tolower(*keysym) - 'a' + 1;
298 }
299
300 *unicode = *keysym;
301}
302
David Sodmanbbcb0522014-09-19 10:34:07 -0700303int term_run(terminal_t* terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700304{
David Sodmanbbcb0522014-09-19 10:34:07 -0700305 int pty_fd = terminal->term->pty_bridge;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700306 fd_set read_set, exception_set;
307
David Sodmanbbcb0522014-09-19 10:34:07 -0700308 video_setmode(terminal->video);
309
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700310 while (1) {
311 FD_ZERO(&read_set);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700312 FD_ZERO(&exception_set);
Dominik Behr93899452014-08-18 22:16:21 -0700313 FD_SET(pty_fd, &read_set);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700314 FD_SET(pty_fd, &exception_set);
Dominik Behr93899452014-08-18 22:16:21 -0700315 int maxfd = input_setfds(&read_set, &exception_set);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700316
Dominik Behr93899452014-08-18 22:16:21 -0700317 maxfd = MAX(maxfd, pty_fd) + 1;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700318
319 select(maxfd, &read_set, NULL, &exception_set, NULL);
320
Dominik Behr93899452014-08-18 22:16:21 -0700321 if (FD_ISSET(pty_fd, &exception_set))
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700322 return -1;
323
Dominik Behr93899452014-08-18 22:16:21 -0700324 struct input_key_event *event;
325 event = input_get_event(&read_set, &exception_set);
326 if (event) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700327 if (!term_special_key(terminal, event) && event->value) {
Dominik Behr93899452014-08-18 22:16:21 -0700328 uint32_t keysym, unicode;
David Sodmanbbcb0522014-09-19 10:34:07 -0700329 if (terminal->active) {
330 term_get_keysym_and_unicode(terminal, event,
331 &keysym,
332 &unicode);
333 term_key_event(terminal, keysym, unicode);
334 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700335 }
Dominik Behr93899452014-08-18 22:16:21 -0700336
337 input_put_event(event);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700338 }
339
340 if (FD_ISSET(pty_fd, &read_set)) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700341 shl_pty_bridge_dispatch(terminal->term->pty_bridge, 0);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700342 }
343 }
344 return 0;
345}
346
David Sodmanbbcb0522014-09-19 10:34:07 -0700347terminal_t* term_init(video_t* video)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700348{
349 const int scrollback_size = 200;
350 uint32_t char_width, char_height;
David Sodmanbbcb0522014-09-19 10:34:07 -0700351 int status;
352 terminal_t *new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700353
David Sodmanbbcb0522014-09-19 10:34:07 -0700354 new_terminal = (terminal_t*)calloc(1, sizeof(*new_terminal));
355 new_terminal->video = video;
356 new_terminal->term = (struct term*)calloc(1, sizeof(*new_terminal->term));
357
358 font_init(video_getscaling(video));
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700359 font_get_size(&char_width, &char_height);
360
David Sodmanbbcb0522014-09-19 10:34:07 -0700361 new_terminal->term->char_x = video_getwidth(video) / char_width;
362 new_terminal->term->char_y = video_getheight(video) / char_height;
363 new_terminal->term->pitch = video_getpitch(video);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700364
David Sodmanbbcb0522014-09-19 10:34:07 -0700365 status = tsm_screen_new(&new_terminal->term->screen,
366 log_tsm, new_terminal->term);
367 if (new_terminal < 0) {
368 term_close(new_terminal);
369 return NULL;
370 }
371
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700372
David Sodmanbbcb0522014-09-19 10:34:07 -0700373 tsm_screen_set_max_sb(new_terminal->term->screen, scrollback_size);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700374
David Sodmanbbcb0522014-09-19 10:34:07 -0700375 status = tsm_vte_new(&new_terminal->term->vte, new_terminal->term->screen,
376 term_write_cb, new_terminal->term, log_tsm, new_terminal->term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700377
David Sodmanbbcb0522014-09-19 10:34:07 -0700378 if (status < 0) {
379 term_close(new_terminal);
380 return NULL;
381 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700382
David Sodmanbbcb0522014-09-19 10:34:07 -0700383 new_terminal->term->pty_bridge = shl_pty_bridge_new();
384 if (new_terminal->term->pty_bridge < 0) {
385 term_close(new_terminal);
386 return NULL;
387 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700388
David Sodmanbbcb0522014-09-19 10:34:07 -0700389 status = shl_pty_open(&new_terminal->term->pty,
390 term_read_cb, new_terminal, new_terminal->term->char_x,
391 new_terminal->term->char_y);
392 if (status < 0) {
393 term_close(new_terminal);
394 return NULL;
395 } else if (status == 0) {
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700396 term_run_child();
397 exit(1);
398 }
399
David Sodmanbbcb0522014-09-19 10:34:07 -0700400 status = shl_pty_bridge_add(new_terminal->term->pty_bridge, new_terminal->term->pty);
401 if (status) {
402 shl_pty_close(new_terminal->term->pty);
403 term_close(new_terminal);
404 return NULL;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700405 }
406
David Sodmanbbcb0522014-09-19 10:34:07 -0700407 new_terminal->term->pid = shl_pty_get_child(new_terminal->term->pty);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700408
David Sodmanbbcb0522014-09-19 10:34:07 -0700409 status = tsm_screen_resize(new_terminal->term->screen,
410 new_terminal->term->char_x, new_terminal->term->char_y);
411 if (status < 0) {
412 shl_pty_close(new_terminal->term->pty);
413 term_close(new_terminal);
414 return NULL;
415 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700416
David Sodmanbbcb0522014-09-19 10:34:07 -0700417 status = shl_pty_resize(new_terminal->term->pty, new_terminal->term->char_x, new_terminal->term->char_y);
418 if (status < 0) {
419 shl_pty_close(new_terminal->term->pty);
420 term_close(new_terminal);
421 return NULL;
422 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700423
David Sodmanbbcb0522014-09-19 10:34:07 -0700424 return new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700425}
426
David Sodmanbbcb0522014-09-19 10:34:07 -0700427void term_set_dbus(terminal_t *term, dbus_t* dbus)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700428{
David Sodmanbbcb0522014-09-19 10:34:07 -0700429 term->dbus = dbus;
430}
431
432void term_close(terminal_t *term)
433{
434 if (!term)
435 return;
436
437 if (term->term) {
438 free(term->term);
439 term->term = NULL;
440 }
441
442 free(term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700443}