blob: 7d686e434a425c73b7d4e14dec33c88ddaf49509 [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 {
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;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070032};
33
David Sodmanf0a925a2015-05-04 11:19:19 -070034struct _terminal_t {
35 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;
42};
43
David Sodman8ef20062015-01-06 09:23:40 -080044
45static char *interactive_cmd_line[] = {
46 "/sbin/agetty",
47 "-",
48 "9600",
49 "xterm",
50 NULL
51};
52
53
54static char *noninteractive_cmd_line[] = {
55 "/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
68static int term_draw_cell(struct tsm_screen *screen, uint32_t id,
David Sodmanbbcb0522014-09-19 10:34:07 -070069 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{
David Sodmanbbcb0522014-09-19 10:34:07 -070075 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;
78 uint32_t Y;
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;
87 Y = (3*br + bb + 4*bg) >> 3;
88
89 if (Y > 128) {
90 front_color = 0;
91 back_color = terminal->background;
92 } else {
93 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
94 back_color = terminal->background;
95 }
96 } else {
97 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
98 back_color = (attr->br << 16) | (attr->bg << 8) | attr->bb;
99 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700100
101 if (attr->inverse) {
102 uint32_t tmp = front_color;
103 front_color = back_color;
104 back_color = tmp;
105 }
106
107 if (len)
David Sodmanbbcb0522014-09-19 10:34:07 -0700108 font_render(terminal->term->dst_image, posx, posy, terminal->term->pitch, *ch,
109 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700110 else
David Sodmanbbcb0522014-09-19 10:34:07 -0700111 font_fillchar(terminal->term->dst_image, posx, posy, terminal->term->pitch,
112 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700113
114 return 0;
115}
116
David Sodmanbbcb0522014-09-19 10:34:07 -0700117void term_redraw(terminal_t *terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700118{
David Sodmanbbcb0522014-09-19 10:34:07 -0700119 uint32_t *video_buffer;
120 video_buffer = video_lock(terminal->video);
121 if (video_buffer != NULL) {
122 terminal->term->dst_image = video_buffer;
123 terminal->term->age =
124 tsm_screen_draw(terminal->term->screen, term_draw_cell, terminal);
125 video_unlock(terminal->video);
126 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700127}
128
David Sodmanbbcb0522014-09-19 10:34:07 -0700129void term_key_event(terminal_t* terminal, uint32_t keysym, int32_t unicode)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700130{
131
David Sodmanbbcb0522014-09-19 10:34:07 -0700132 if (tsm_vte_handle_keyboard(terminal->term->vte, keysym, 0, 0, unicode))
133 tsm_screen_sb_reset(terminal->term->screen);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700134
David Sodmanbbcb0522014-09-19 10:34:07 -0700135 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700136}
137
138static void term_read_cb(struct shl_pty *pty, char *u8, size_t len, void *data)
139{
David Sodmanbbcb0522014-09-19 10:34:07 -0700140 terminal_t *terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700141
David Sodmanbbcb0522014-09-19 10:34:07 -0700142 tsm_vte_input(terminal->term->vte, u8, len);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700143
David Sodmanbbcb0522014-09-19 10:34:07 -0700144 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700145}
146
147static void term_write_cb(struct tsm_vte *vte, const char *u8, size_t len,
David Sodmanbbcb0522014-09-19 10:34:07 -0700148 void *data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700149{
150 struct term *term = data;
151 int r;
152
153 r = shl_pty_write(term->pty, u8, len);
154 if (r < 0)
David Sodmanbbcb0522014-09-19 10:34:07 -0700155 LOG(ERROR, "OOM in pty-write (%d)", r);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700156
157 shl_pty_dispatch(term->pty);
158}
159
160static const char *sev2str_table[] = {
161 "FATAL",
162 "ALERT",
163 "CRITICAL",
164 "ERROR",
165 "WARNING",
166 "NOTICE",
167 "INFO",
168 "DEBUG"
169};
170
171static const char *sev2str(unsigned int sev)
172{
173 if (sev > 7)
174 return "DEBUG";
175
176 return sev2str_table[sev];
177}
178
Dominik Behr00003502014-08-15 16:42:37 -0700179#ifdef __clang__
180__attribute__((__format__ (__printf__, 7, 0)))
181#endif
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700182static void log_tsm(void *data, const char *file, int line, const char *fn,
David Sodmanbbcb0522014-09-19 10:34:07 -0700183 const char *subs, unsigned int sev, const char *format,
184 va_list args)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700185{
186 fprintf(stderr, "%s: %s: ", sev2str(sev), subs);
187 vfprintf(stderr, format, args);
188 fprintf(stderr, "\n");
189}
190
David Sodmanf0a925a2015-05-04 11:19:19 -0700191terminal_t* term_init(bool interactive, video_t* video)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700192{
193 const int scrollback_size = 200;
194 uint32_t char_width, char_height;
David Sodmanbbcb0522014-09-19 10:34:07 -0700195 int status;
196 terminal_t *new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700197
David Sodmanbbcb0522014-09-19 10:34:07 -0700198 new_terminal = (terminal_t*)calloc(1, sizeof(*new_terminal));
David Sodmanbf3f2842014-11-12 08:26:58 -0800199 if (!new_terminal)
200 return NULL;
David Sodmanbbcb0522014-09-19 10:34:07 -0700201
David Sodmanf0a925a2015-05-04 11:19:19 -0700202 new_terminal->background_valid = false;
203
204 if (video)
205 new_terminal->video = video;
206 else
207 new_terminal->video = video_init();
208
David Sodmanbf3f2842014-11-12 08:26:58 -0800209 if (!new_terminal->video) {
210 term_close(new_terminal);
211 return NULL;
212 }
213
214 new_terminal->term = (struct term*)calloc(1, sizeof(*new_terminal->term));
215 if (!new_terminal->term) {
216 term_close(new_terminal);
217 return NULL;
218 }
219
David Sodman8ef20062015-01-06 09:23:40 -0800220 if (interactive)
221 new_terminal->exec = interactive_cmd_line;
222 else
223 new_terminal->exec = noninteractive_cmd_line;
224
David Sodmanbf3f2842014-11-12 08:26:58 -0800225 font_init(video_getscaling(new_terminal->video));
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700226 font_get_size(&char_width, &char_height);
227
David Sodmanbf3f2842014-11-12 08:26:58 -0800228 new_terminal->term->char_x =
229 video_getwidth(new_terminal->video) / char_width;
230 new_terminal->term->char_y =
231 video_getheight(new_terminal->video) / char_height;
232 new_terminal->term->pitch = video_getpitch(new_terminal->video);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700233
David Sodmanbbcb0522014-09-19 10:34:07 -0700234 status = tsm_screen_new(&new_terminal->term->screen,
235 log_tsm, new_terminal->term);
236 if (new_terminal < 0) {
237 term_close(new_terminal);
238 return NULL;
239 }
240
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700241
David Sodmanbbcb0522014-09-19 10:34:07 -0700242 tsm_screen_set_max_sb(new_terminal->term->screen, scrollback_size);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700243
David Sodmanbbcb0522014-09-19 10:34:07 -0700244 status = tsm_vte_new(&new_terminal->term->vte, new_terminal->term->screen,
245 term_write_cb, new_terminal->term, log_tsm, new_terminal->term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700246
David Sodmanbbcb0522014-09-19 10:34:07 -0700247 if (status < 0) {
248 term_close(new_terminal);
249 return NULL;
250 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700251
David Sodmanbbcb0522014-09-19 10:34:07 -0700252 new_terminal->term->pty_bridge = shl_pty_bridge_new();
253 if (new_terminal->term->pty_bridge < 0) {
254 term_close(new_terminal);
255 return NULL;
256 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700257
David Sodmanbbcb0522014-09-19 10:34:07 -0700258 status = shl_pty_open(&new_terminal->term->pty,
259 term_read_cb, new_terminal, new_terminal->term->char_x,
260 new_terminal->term->char_y);
David Sodman8ef20062015-01-06 09:23:40 -0800261
David Sodmanbbcb0522014-09-19 10:34:07 -0700262 if (status < 0) {
263 term_close(new_terminal);
264 return NULL;
265 } else if (status == 0) {
David Sodman8ef20062015-01-06 09:23:40 -0800266 term_run_child(new_terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700267 exit(1);
268 }
269
David Sodmanbbcb0522014-09-19 10:34:07 -0700270 status = shl_pty_bridge_add(new_terminal->term->pty_bridge, new_terminal->term->pty);
271 if (status) {
272 shl_pty_close(new_terminal->term->pty);
273 term_close(new_terminal);
274 return NULL;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700275 }
276
David Sodmanbbcb0522014-09-19 10:34:07 -0700277 new_terminal->term->pid = shl_pty_get_child(new_terminal->term->pty);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700278
David Sodmanbbcb0522014-09-19 10:34:07 -0700279 status = tsm_screen_resize(new_terminal->term->screen,
280 new_terminal->term->char_x, new_terminal->term->char_y);
281 if (status < 0) {
282 shl_pty_close(new_terminal->term->pty);
283 term_close(new_terminal);
284 return NULL;
285 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700286
David Sodmanbbcb0522014-09-19 10:34:07 -0700287 status = shl_pty_resize(new_terminal->term->pty, new_terminal->term->char_x, new_terminal->term->char_y);
288 if (status < 0) {
289 shl_pty_close(new_terminal->term->pty);
290 term_close(new_terminal);
291 return NULL;
292 }
David Sodman8ef20062015-01-06 09:23:40 -0800293
294 if (interactive)
295 new_terminal->active = true;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700296
David Sodmanbbcb0522014-09-19 10:34:07 -0700297 return new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700298}
299
David Sodman8ef20062015-01-06 09:23:40 -0800300void term_activate(terminal_t* terminal)
301{
David Sodman8ef20062015-01-06 09:23:40 -0800302 input_set_current(terminal);
303 terminal->active = true;
304 video_setmode(terminal->video);
305 term_redraw(terminal);
306}
307
David Sodmanf0a925a2015-05-04 11:19:19 -0700308void term_deactivate(terminal_t* terminal)
309{
310 if (!terminal->active)
311 return;
312
David Sodmanf0a925a2015-05-04 11:19:19 -0700313 terminal->active = false;
314 video_release(terminal->video);
315}
316
David Sodmanbbcb0522014-09-19 10:34:07 -0700317void term_set_dbus(terminal_t *term, dbus_t* dbus)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700318{
David Sodmanbbcb0522014-09-19 10:34:07 -0700319 term->dbus = dbus;
320}
321
322void term_close(terminal_t *term)
323{
324 if (!term)
325 return;
326
David Sodmanbf3f2842014-11-12 08:26:58 -0800327 if (term->video) {
328 video_close(term->video);
329 term->video = NULL;
330 }
331
David Sodmanbbcb0522014-09-19 10:34:07 -0700332 if (term->term) {
333 free(term->term);
334 term->term = NULL;
335 }
336
Haixia Shi95d680e2015-04-27 20:29:17 -0700337 font_free();
David Sodmanbbcb0522014-09-19 10:34:07 -0700338 free(term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700339}
David Sodmanbf3f2842014-11-12 08:26:58 -0800340
341bool term_is_child_done(terminal_t* terminal)
342{
343 int status;
344 int ret;
345 ret = waitpid(terminal->term->pid, &status, WNOHANG);
346
David Sodmanf0a925a2015-05-04 11:19:19 -0700347 if ((ret == -1) && (errno == ECHILD)) {
348 return false;
349 }
David Sodmanbf3f2842014-11-12 08:26:58 -0800350 return ret != 0;
351}
352
353void term_page_up(terminal_t* terminal)
354{
355 tsm_screen_sb_page_up(terminal->term->screen, 1);
356 term_redraw(terminal);
357}
358
359void term_page_down(terminal_t* terminal)
360{
361 tsm_screen_sb_page_down(terminal->term->screen, 1);
362 term_redraw(terminal);
363}
364
365void term_line_up(terminal_t* terminal)
366{
367 tsm_screen_sb_up(terminal->term->screen, 1);
368 term_redraw(terminal);
369}
370
371void term_line_down(terminal_t* terminal)
372{
373 tsm_screen_sb_down(terminal->term->screen, 1);
374 term_redraw(terminal);
375}
376
377bool term_is_valid(terminal_t* terminal)
378{
379 return ((terminal != NULL) && (terminal->term != NULL));
380}
381
382int term_fd(terminal_t* terminal)
383{
384 if (term_is_valid(terminal))
385 return terminal->term->pty_bridge;
386 else
387 return -1;
388}
389
390void term_dispatch_io(terminal_t* terminal, fd_set* read_set)
391{
392 if (term_is_valid(terminal))
393 if (FD_ISSET(terminal->term->pty_bridge, read_set))
394 shl_pty_bridge_dispatch(terminal->term->pty_bridge, 0);
395}
396
397bool term_exception(terminal_t* terminal, fd_set* exception_set)
398{
399 if (term_is_valid(terminal)) {
400 if (terminal->term->pty_bridge >= 0) {
401 return FD_ISSET(terminal->term->pty_bridge,
402 exception_set);
403 }
404 }
405
406 return false;
407}
408
409bool term_is_active(terminal_t* terminal)
410{
411 if (term_is_valid(terminal))
412 return terminal->active;
413
414 return false;
415}
416
417void term_add_fd(terminal_t* terminal, fd_set* read_set, fd_set* exception_set)
418{
419 if (term_is_valid(terminal)) {
420 if (terminal->term->pty_bridge >= 0) {
421 FD_SET(terminal->term->pty_bridge, read_set);
422 FD_SET(terminal->term->pty_bridge, exception_set);
423 }
424 }
425}
David Sodman8ef20062015-01-06 09:23:40 -0800426
427const char* term_get_ptsname(terminal_t* terminal)
428{
429 return ptsname(shl_pty_get_fd(terminal->term->pty));
430}
David Sodmanf0a925a2015-05-04 11:19:19 -0700431
432void term_set_background(terminal_t* terminal, uint32_t bg)
433{
434 terminal->background = bg;
435 terminal->background_valid = true;
436}
437
438int term_show_image(terminal_t* terminal, image_t* image)
439{
440 return image_show(image, terminal->video);
441}
442
443void term_write_message(terminal_t* terminal, char* message)
444{
445 FILE *fp;
446
447 fp = fopen(term_get_ptsname(terminal), "w");
448 if (fp) {
449 fputs(message, fp);
450 fclose(fp);
451 }
452}
453
454void term_hide_cursor(terminal_t* terminal)
455{
456 term_write_message(terminal, "\033[?25l");
457}
458
459void term_show_cursor(terminal_t* terminal)
460{
461 term_write_message(terminal, "\033[?25h");
462}