blob: 35616f93b1184373cc7326c1c85f67fe1e88564d [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>
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -07009#include <stdio.h>
Dominik Behr93899452014-08-18 22:16:21 -070010#include <sys/select.h>
David Sodmanbf3f2842014-11-12 08:26:58 -080011#include <sys/types.h>
12#include <sys/wait.h>
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070013
Dominik Behr580462b2014-11-20 13:50:05 -080014#include "dbus_interface.h"
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070015#include "font.h"
16#include "input.h"
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070017#include "shl_pty.h"
18#include "term.h"
19#include "util.h"
20#include "video.h"
21
22struct term {
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080023 struct tsm_screen* screen;
24 struct tsm_vte* vte;
25 struct shl_pty* pty;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070026 int pty_bridge;
27 int pid;
28 tsm_age_t age;
29 int char_x, char_y;
30 int pitch;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080031 uint32_t* dst_image;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070032};
33
David Sodmanf0a925a2015-05-04 11:19:19 -070034struct _terminal_t {
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080035 uint32_t background;
36 bool background_valid;
37 video_t* video;
38 dbus_t* dbus;
39 struct term* term;
40 bool active;
41 char** exec;
David Sodmanf0a925a2015-05-04 11:19:19 -070042};
43
David Sodman8ef20062015-01-06 09:23:40 -080044
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080045static char* interactive_cmd_line[] = {
David Sodman8ef20062015-01-06 09:23:40 -080046 "/sbin/agetty",
47 "-",
48 "9600",
49 "xterm",
50 NULL
51};
52
53
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080054static char* noninteractive_cmd_line[] = {
David Sodman8ef20062015-01-06 09:23:40 -080055 "/bin/cat",
56 NULL
57};
58
59
60static void __attribute__ ((noreturn)) term_run_child(terminal_t* terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070061{
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070062 /* XXX figure out how to fix "top" for xterm-256color */
63 setenv("TERM", "xterm", 1);
David Sodman8ef20062015-01-06 09:23:40 -080064 execve(terminal->exec[0], terminal->exec, environ);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070065 exit(1);
66}
67
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080068static int term_draw_cell(struct tsm_screen* screen, uint32_t id,
Stéphane Marchesin8fc13522015-12-14 17:02:28 -080069 const uint32_t* ch, size_t len,
70 unsigned int cwidth, unsigned int posx,
71 unsigned int posy,
72 const struct tsm_screen_attr* attr,
73 tsm_age_t age, void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070074{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080075 terminal_t* terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070076 uint32_t front_color, back_color;
David Sodmanf0a925a2015-05-04 11:19:19 -070077 uint8_t br, bb, bg;
Stéphane Marchesinedece332015-12-14 15:10:58 -080078 uint32_t luminance;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070079
David Sodmanbbcb0522014-09-19 10:34:07 -070080 if (age && terminal->term->age && age <= terminal->term->age)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070081 return 0;
82
David Sodmanf0a925a2015-05-04 11:19:19 -070083 if (terminal->background_valid) {
84 br = (terminal->background >> 16) & 0xFF;
85 bg = (terminal->background >> 8) & 0xFF;
86 bb = (terminal->background) & 0xFF;
Stéphane Marchesinedece332015-12-14 15:10:58 -080087 luminance = (3 * br + bb + 4 * bg) >> 3;
David Sodmanf0a925a2015-05-04 11:19:19 -070088
Stéphane Marchesinedece332015-12-14 15:10:58 -080089 /*
90 * FIXME: black is chosen on a dark background, but it uses the
91 * default color for light backgrounds
92 */
93 if (luminance > 128) {
David Sodmanf0a925a2015-05-04 11:19:19 -070094 front_color = 0;
95 back_color = terminal->background;
96 } else {
97 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
98 back_color = terminal->background;
99 }
100 } else {
101 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
102 back_color = (attr->br << 16) | (attr->bg << 8) | attr->bb;
103 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700104
105 if (attr->inverse) {
106 uint32_t tmp = front_color;
107 front_color = back_color;
108 back_color = tmp;
109 }
110
111 if (len)
David Sodmanbbcb0522014-09-19 10:34:07 -0700112 font_render(terminal->term->dst_image, posx, posy, terminal->term->pitch, *ch,
113 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700114 else
David Sodmanbbcb0522014-09-19 10:34:07 -0700115 font_fillchar(terminal->term->dst_image, posx, posy, terminal->term->pitch,
116 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700117
118 return 0;
119}
120
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800121void term_redraw(terminal_t* terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700122{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800123 uint32_t* video_buffer;
David Sodmanbbcb0522014-09-19 10:34:07 -0700124 video_buffer = video_lock(terminal->video);
125 if (video_buffer != NULL) {
126 terminal->term->dst_image = video_buffer;
127 terminal->term->age =
128 tsm_screen_draw(terminal->term->screen, term_draw_cell, terminal);
129 video_unlock(terminal->video);
130 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700131}
132
David Sodmanbbcb0522014-09-19 10:34:07 -0700133void term_key_event(terminal_t* terminal, uint32_t keysym, int32_t unicode)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700134{
David Sodmanbbcb0522014-09-19 10:34:07 -0700135 if (tsm_vte_handle_keyboard(terminal->term->vte, keysym, 0, 0, unicode))
136 tsm_screen_sb_reset(terminal->term->screen);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700137
David Sodmanbbcb0522014-09-19 10:34:07 -0700138 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700139}
140
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800141static void term_read_cb(struct shl_pty* pty, char* u8, size_t len, void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700142{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800143 terminal_t* terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700144
David Sodmanbbcb0522014-09-19 10:34:07 -0700145 tsm_vte_input(terminal->term->vte, u8, len);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700146
David Sodmanbbcb0522014-09-19 10:34:07 -0700147 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700148}
149
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800150static void term_write_cb(struct tsm_vte* vte, const char* u8, size_t len,
151 void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700152{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800153 struct term* term = data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700154 int r;
155
156 r = shl_pty_write(term->pty, u8, len);
157 if (r < 0)
David Sodmanbbcb0522014-09-19 10:34:07 -0700158 LOG(ERROR, "OOM in pty-write (%d)", r);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700159
160 shl_pty_dispatch(term->pty);
161}
162
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800163static const char* sev2str_table[] = {
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700164 "FATAL",
165 "ALERT",
166 "CRITICAL",
167 "ERROR",
168 "WARNING",
169 "NOTICE",
170 "INFO",
171 "DEBUG"
172};
173
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800174static const char* sev2str(unsigned int sev)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700175{
176 if (sev > 7)
177 return "DEBUG";
178
179 return sev2str_table[sev];
180}
181
Dominik Behr00003502014-08-15 16:42:37 -0700182#ifdef __clang__
183__attribute__((__format__ (__printf__, 7, 0)))
184#endif
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800185static void log_tsm(void* data, const char* file, int line, const char* fn,
Stéphane Marchesin8fc13522015-12-14 17:02:28 -0800186 const char* subs, unsigned int sev, const char* format,
187 va_list args)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700188{
189 fprintf(stderr, "%s: %s: ", sev2str(sev), subs);
190 vfprintf(stderr, format, args);
191 fprintf(stderr, "\n");
192}
193
Stéphane Marchesin78310662016-01-06 16:09:39 -0800194static int term_resize(terminal_t* term)
195{
196 uint32_t char_width, char_height;
197 int status;
198
199 font_init(video_getscaling(term->video));
200 font_get_size(&char_width, &char_height);
201
202 term->term->char_x = video_getwidth(term->video) / char_width;
203 term->term->char_y = video_getheight(term->video) / char_height;
204 term->term->pitch = video_getpitch(term->video);
205
206 status = tsm_screen_resize(term->term->screen,
207 term->term->char_x, term->term->char_y);
208 if (status < 0) {
209 font_free();
210 return -1;
211 }
212
213 status = shl_pty_resize(term->term->pty, term->term->char_x,
214 term->term->char_y);
215 if (status < 0) {
216 font_free();
217 return -1;
218 }
219
220 return 0;
221}
222
David Sodmanf0a925a2015-05-04 11:19:19 -0700223terminal_t* term_init(bool interactive, video_t* video)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700224{
225 const int scrollback_size = 200;
David Sodmanbbcb0522014-09-19 10:34:07 -0700226 int status;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800227 terminal_t* new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700228
David Sodmanbbcb0522014-09-19 10:34:07 -0700229 new_terminal = (terminal_t*)calloc(1, sizeof(*new_terminal));
David Sodmanbf3f2842014-11-12 08:26:58 -0800230 if (!new_terminal)
231 return NULL;
David Sodmanbbcb0522014-09-19 10:34:07 -0700232
David Sodmanf0a925a2015-05-04 11:19:19 -0700233 new_terminal->background_valid = false;
234
David Sodman30a94ef2015-07-26 17:37:12 -0700235 if (video) {
David Sodmanf0a925a2015-05-04 11:19:19 -0700236 new_terminal->video = video;
David Sodman30a94ef2015-07-26 17:37:12 -0700237 video_addref(video);
238 }
David Sodmanf0a925a2015-05-04 11:19:19 -0700239 else
240 new_terminal->video = video_init();
241
David Sodmanbf3f2842014-11-12 08:26:58 -0800242 if (!new_terminal->video) {
243 term_close(new_terminal);
244 return NULL;
245 }
246
247 new_terminal->term = (struct term*)calloc(1, sizeof(*new_terminal->term));
248 if (!new_terminal->term) {
249 term_close(new_terminal);
250 return NULL;
251 }
252
David Sodman8ef20062015-01-06 09:23:40 -0800253 if (interactive)
254 new_terminal->exec = interactive_cmd_line;
255 else
256 new_terminal->exec = noninteractive_cmd_line;
257
David Sodmanbbcb0522014-09-19 10:34:07 -0700258 status = tsm_screen_new(&new_terminal->term->screen,
259 log_tsm, new_terminal->term);
zhuo-hao471f1e22015-08-12 14:55:56 +0800260 if (status < 0) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700261 term_close(new_terminal);
262 return NULL;
263 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700264
David Sodmanbbcb0522014-09-19 10:34:07 -0700265 tsm_screen_set_max_sb(new_terminal->term->screen, scrollback_size);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700266
David Sodmanbbcb0522014-09-19 10:34:07 -0700267 status = tsm_vte_new(&new_terminal->term->vte, new_terminal->term->screen,
268 term_write_cb, new_terminal->term, log_tsm, new_terminal->term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700269
David Sodmanbbcb0522014-09-19 10:34:07 -0700270 if (status < 0) {
271 term_close(new_terminal);
272 return NULL;
273 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700274
David Sodmanbbcb0522014-09-19 10:34:07 -0700275 new_terminal->term->pty_bridge = shl_pty_bridge_new();
276 if (new_terminal->term->pty_bridge < 0) {
277 term_close(new_terminal);
278 return NULL;
279 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700280
David Sodmanbbcb0522014-09-19 10:34:07 -0700281 status = shl_pty_open(&new_terminal->term->pty,
Stéphane Marchesin78310662016-01-06 16:09:39 -0800282 term_read_cb, new_terminal, 1, 1);
David Sodman8ef20062015-01-06 09:23:40 -0800283
David Sodmanbbcb0522014-09-19 10:34:07 -0700284 if (status < 0) {
285 term_close(new_terminal);
286 return NULL;
287 } else if (status == 0) {
David Sodman8ef20062015-01-06 09:23:40 -0800288 term_run_child(new_terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700289 exit(1);
290 }
291
David Sodmanbbcb0522014-09-19 10:34:07 -0700292 status = shl_pty_bridge_add(new_terminal->term->pty_bridge, new_terminal->term->pty);
293 if (status) {
294 shl_pty_close(new_terminal->term->pty);
295 term_close(new_terminal);
296 return NULL;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700297 }
298
David Sodmanbbcb0522014-09-19 10:34:07 -0700299 new_terminal->term->pid = shl_pty_get_child(new_terminal->term->pty);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700300
Stéphane Marchesin78310662016-01-06 16:09:39 -0800301 status = term_resize(new_terminal);
David Sodmanbbcb0522014-09-19 10:34:07 -0700302 if (status < 0) {
303 shl_pty_close(new_terminal->term->pty);
304 term_close(new_terminal);
305 return NULL;
306 }
David Sodman8ef20062015-01-06 09:23:40 -0800307
308 if (interactive)
309 new_terminal->active = true;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700310
David Sodmanbbcb0522014-09-19 10:34:07 -0700311 return new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700312}
313
David Sodman8ef20062015-01-06 09:23:40 -0800314void term_activate(terminal_t* terminal)
315{
David Sodman8ef20062015-01-06 09:23:40 -0800316 input_set_current(terminal);
317 terminal->active = true;
318 video_setmode(terminal->video);
319 term_redraw(terminal);
320}
321
David Sodmanf0a925a2015-05-04 11:19:19 -0700322void term_deactivate(terminal_t* terminal)
323{
324 if (!terminal->active)
325 return;
326
David Sodmanf0a925a2015-05-04 11:19:19 -0700327 terminal->active = false;
328 video_release(terminal->video);
329}
330
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800331void term_set_dbus(terminal_t* term, dbus_t* dbus)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700332{
David Sodmanbbcb0522014-09-19 10:34:07 -0700333 term->dbus = dbus;
334}
335
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800336void term_close(terminal_t* term)
David Sodmanbbcb0522014-09-19 10:34:07 -0700337{
338 if (!term)
339 return;
340
David Sodmanbf3f2842014-11-12 08:26:58 -0800341 if (term->video) {
342 video_close(term->video);
343 term->video = NULL;
344 }
345
David Sodmanbbcb0522014-09-19 10:34:07 -0700346 if (term->term) {
347 free(term->term);
348 term->term = NULL;
349 }
350
Haixia Shi95d680e2015-04-27 20:29:17 -0700351 font_free();
David Sodmanbbcb0522014-09-19 10:34:07 -0700352 free(term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700353}
David Sodmanbf3f2842014-11-12 08:26:58 -0800354
355bool term_is_child_done(terminal_t* terminal)
356{
357 int status;
358 int ret;
359 ret = waitpid(terminal->term->pid, &status, WNOHANG);
360
David Sodmanf0a925a2015-05-04 11:19:19 -0700361 if ((ret == -1) && (errno == ECHILD)) {
362 return false;
363 }
David Sodmanbf3f2842014-11-12 08:26:58 -0800364 return ret != 0;
365}
366
367void term_page_up(terminal_t* terminal)
368{
369 tsm_screen_sb_page_up(terminal->term->screen, 1);
370 term_redraw(terminal);
371}
372
373void term_page_down(terminal_t* terminal)
374{
375 tsm_screen_sb_page_down(terminal->term->screen, 1);
376 term_redraw(terminal);
377}
378
379void term_line_up(terminal_t* terminal)
380{
381 tsm_screen_sb_up(terminal->term->screen, 1);
382 term_redraw(terminal);
383}
384
385void term_line_down(terminal_t* terminal)
386{
387 tsm_screen_sb_down(terminal->term->screen, 1);
388 term_redraw(terminal);
389}
390
391bool term_is_valid(terminal_t* terminal)
392{
393 return ((terminal != NULL) && (terminal->term != NULL));
394}
395
396int term_fd(terminal_t* terminal)
397{
398 if (term_is_valid(terminal))
399 return terminal->term->pty_bridge;
400 else
401 return -1;
402}
403
404void term_dispatch_io(terminal_t* terminal, fd_set* read_set)
405{
406 if (term_is_valid(terminal))
407 if (FD_ISSET(terminal->term->pty_bridge, read_set))
408 shl_pty_bridge_dispatch(terminal->term->pty_bridge, 0);
409}
410
411bool term_exception(terminal_t* terminal, fd_set* exception_set)
412{
413 if (term_is_valid(terminal)) {
414 if (terminal->term->pty_bridge >= 0) {
415 return FD_ISSET(terminal->term->pty_bridge,
416 exception_set);
417 }
418 }
419
420 return false;
421}
422
423bool term_is_active(terminal_t* terminal)
424{
425 if (term_is_valid(terminal))
426 return terminal->active;
427
428 return false;
429}
430
431void term_add_fd(terminal_t* terminal, fd_set* read_set, fd_set* exception_set)
432{
433 if (term_is_valid(terminal)) {
434 if (terminal->term->pty_bridge >= 0) {
435 FD_SET(terminal->term->pty_bridge, read_set);
436 FD_SET(terminal->term->pty_bridge, exception_set);
437 }
438 }
439}
David Sodman8ef20062015-01-06 09:23:40 -0800440
441const char* term_get_ptsname(terminal_t* terminal)
442{
443 return ptsname(shl_pty_get_fd(terminal->term->pty));
444}
David Sodmanf0a925a2015-05-04 11:19:19 -0700445
446void term_set_background(terminal_t* terminal, uint32_t bg)
447{
448 terminal->background = bg;
449 terminal->background_valid = true;
450}
451
452int term_show_image(terminal_t* terminal, image_t* image)
453{
454 return image_show(image, terminal->video);
455}
456
457void term_write_message(terminal_t* terminal, char* message)
458{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800459 FILE* fp;
David Sodmanf0a925a2015-05-04 11:19:19 -0700460
461 fp = fopen(term_get_ptsname(terminal), "w");
462 if (fp) {
463 fputs(message, fp);
464 fclose(fp);
465 }
466}
467
468void term_hide_cursor(terminal_t* terminal)
469{
470 term_write_message(terminal, "\033[?25l");
471}
472
473void term_show_cursor(terminal_t* terminal)
474{
475 term_write_message(terminal, "\033[?25h");
476}
David Sodman30a94ef2015-07-26 17:37:12 -0700477
478video_t* term_getvideo(terminal_t* terminal)
479{
480 return terminal->video;
481}