blob: f374e5c5311009dfb5075b170bece31f2adb1a5e [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,
69 const uint32_t* ch, size_t len,
David Sodmanbbcb0522014-09-19 10:34:07 -070070 unsigned int cwidth, unsigned int posx,
71 unsigned int posy,
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080072 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,
186 const char* subs, unsigned int sev, const char* format,
David Sodmanbbcb0522014-09-19 10:34:07 -0700187 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
David Sodmanf0a925a2015-05-04 11:19:19 -0700194terminal_t* term_init(bool interactive, video_t* video)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700195{
196 const int scrollback_size = 200;
197 uint32_t char_width, char_height;
David Sodmanbbcb0522014-09-19 10:34:07 -0700198 int status;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800199 terminal_t* new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700200
David Sodmanbbcb0522014-09-19 10:34:07 -0700201 new_terminal = (terminal_t*)calloc(1, sizeof(*new_terminal));
David Sodmanbf3f2842014-11-12 08:26:58 -0800202 if (!new_terminal)
203 return NULL;
David Sodmanbbcb0522014-09-19 10:34:07 -0700204
David Sodmanf0a925a2015-05-04 11:19:19 -0700205 new_terminal->background_valid = false;
206
David Sodman30a94ef2015-07-26 17:37:12 -0700207 if (video) {
David Sodmanf0a925a2015-05-04 11:19:19 -0700208 new_terminal->video = video;
David Sodman30a94ef2015-07-26 17:37:12 -0700209 video_addref(video);
210 }
David Sodmanf0a925a2015-05-04 11:19:19 -0700211 else
212 new_terminal->video = video_init();
213
David Sodmanbf3f2842014-11-12 08:26:58 -0800214 if (!new_terminal->video) {
215 term_close(new_terminal);
216 return NULL;
217 }
218
219 new_terminal->term = (struct term*)calloc(1, sizeof(*new_terminal->term));
220 if (!new_terminal->term) {
221 term_close(new_terminal);
222 return NULL;
223 }
224
David Sodman8ef20062015-01-06 09:23:40 -0800225 if (interactive)
226 new_terminal->exec = interactive_cmd_line;
227 else
228 new_terminal->exec = noninteractive_cmd_line;
229
David Sodmanbf3f2842014-11-12 08:26:58 -0800230 font_init(video_getscaling(new_terminal->video));
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700231 font_get_size(&char_width, &char_height);
232
David Sodmanbf3f2842014-11-12 08:26:58 -0800233 new_terminal->term->char_x =
234 video_getwidth(new_terminal->video) / char_width;
235 new_terminal->term->char_y =
236 video_getheight(new_terminal->video) / char_height;
237 new_terminal->term->pitch = video_getpitch(new_terminal->video);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700238
David Sodmanbbcb0522014-09-19 10:34:07 -0700239 status = tsm_screen_new(&new_terminal->term->screen,
240 log_tsm, new_terminal->term);
zhuo-hao471f1e22015-08-12 14:55:56 +0800241 if (status < 0) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700242 term_close(new_terminal);
243 return NULL;
244 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700245
David Sodmanbbcb0522014-09-19 10:34:07 -0700246 tsm_screen_set_max_sb(new_terminal->term->screen, scrollback_size);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700247
David Sodmanbbcb0522014-09-19 10:34:07 -0700248 status = tsm_vte_new(&new_terminal->term->vte, new_terminal->term->screen,
249 term_write_cb, new_terminal->term, log_tsm, new_terminal->term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700250
David Sodmanbbcb0522014-09-19 10:34:07 -0700251 if (status < 0) {
252 term_close(new_terminal);
253 return NULL;
254 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700255
David Sodmanbbcb0522014-09-19 10:34:07 -0700256 new_terminal->term->pty_bridge = shl_pty_bridge_new();
257 if (new_terminal->term->pty_bridge < 0) {
258 term_close(new_terminal);
259 return NULL;
260 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700261
David Sodmanbbcb0522014-09-19 10:34:07 -0700262 status = shl_pty_open(&new_terminal->term->pty,
263 term_read_cb, new_terminal, new_terminal->term->char_x,
264 new_terminal->term->char_y);
David Sodman8ef20062015-01-06 09:23:40 -0800265
David Sodmanbbcb0522014-09-19 10:34:07 -0700266 if (status < 0) {
267 term_close(new_terminal);
268 return NULL;
269 } else if (status == 0) {
David Sodman8ef20062015-01-06 09:23:40 -0800270 term_run_child(new_terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700271 exit(1);
272 }
273
David Sodmanbbcb0522014-09-19 10:34:07 -0700274 status = shl_pty_bridge_add(new_terminal->term->pty_bridge, new_terminal->term->pty);
275 if (status) {
276 shl_pty_close(new_terminal->term->pty);
277 term_close(new_terminal);
278 return NULL;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700279 }
280
David Sodmanbbcb0522014-09-19 10:34:07 -0700281 new_terminal->term->pid = shl_pty_get_child(new_terminal->term->pty);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700282
David Sodmanbbcb0522014-09-19 10:34:07 -0700283 status = tsm_screen_resize(new_terminal->term->screen,
284 new_terminal->term->char_x, new_terminal->term->char_y);
285 if (status < 0) {
286 shl_pty_close(new_terminal->term->pty);
287 term_close(new_terminal);
288 return NULL;
289 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700290
David Sodmanbbcb0522014-09-19 10:34:07 -0700291 status = shl_pty_resize(new_terminal->term->pty, new_terminal->term->char_x, new_terminal->term->char_y);
292 if (status < 0) {
293 shl_pty_close(new_terminal->term->pty);
294 term_close(new_terminal);
295 return NULL;
296 }
David Sodman8ef20062015-01-06 09:23:40 -0800297
298 if (interactive)
299 new_terminal->active = true;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700300
David Sodmanbbcb0522014-09-19 10:34:07 -0700301 return new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700302}
303
David Sodman8ef20062015-01-06 09:23:40 -0800304void term_activate(terminal_t* terminal)
305{
David Sodman8ef20062015-01-06 09:23:40 -0800306 input_set_current(terminal);
307 terminal->active = true;
308 video_setmode(terminal->video);
309 term_redraw(terminal);
310}
311
David Sodmanf0a925a2015-05-04 11:19:19 -0700312void term_deactivate(terminal_t* terminal)
313{
314 if (!terminal->active)
315 return;
316
David Sodmanf0a925a2015-05-04 11:19:19 -0700317 terminal->active = false;
318 video_release(terminal->video);
319}
320
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800321void term_set_dbus(terminal_t* term, dbus_t* dbus)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700322{
David Sodmanbbcb0522014-09-19 10:34:07 -0700323 term->dbus = dbus;
324}
325
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800326void term_close(terminal_t* term)
David Sodmanbbcb0522014-09-19 10:34:07 -0700327{
328 if (!term)
329 return;
330
David Sodmanbf3f2842014-11-12 08:26:58 -0800331 if (term->video) {
332 video_close(term->video);
333 term->video = NULL;
334 }
335
David Sodmanbbcb0522014-09-19 10:34:07 -0700336 if (term->term) {
337 free(term->term);
338 term->term = NULL;
339 }
340
Haixia Shi95d680e2015-04-27 20:29:17 -0700341 font_free();
David Sodmanbbcb0522014-09-19 10:34:07 -0700342 free(term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700343}
David Sodmanbf3f2842014-11-12 08:26:58 -0800344
345bool term_is_child_done(terminal_t* terminal)
346{
347 int status;
348 int ret;
349 ret = waitpid(terminal->term->pid, &status, WNOHANG);
350
David Sodmanf0a925a2015-05-04 11:19:19 -0700351 if ((ret == -1) && (errno == ECHILD)) {
352 return false;
353 }
David Sodmanbf3f2842014-11-12 08:26:58 -0800354 return ret != 0;
355}
356
357void term_page_up(terminal_t* terminal)
358{
359 tsm_screen_sb_page_up(terminal->term->screen, 1);
360 term_redraw(terminal);
361}
362
363void term_page_down(terminal_t* terminal)
364{
365 tsm_screen_sb_page_down(terminal->term->screen, 1);
366 term_redraw(terminal);
367}
368
369void term_line_up(terminal_t* terminal)
370{
371 tsm_screen_sb_up(terminal->term->screen, 1);
372 term_redraw(terminal);
373}
374
375void term_line_down(terminal_t* terminal)
376{
377 tsm_screen_sb_down(terminal->term->screen, 1);
378 term_redraw(terminal);
379}
380
381bool term_is_valid(terminal_t* terminal)
382{
383 return ((terminal != NULL) && (terminal->term != NULL));
384}
385
386int term_fd(terminal_t* terminal)
387{
388 if (term_is_valid(terminal))
389 return terminal->term->pty_bridge;
390 else
391 return -1;
392}
393
394void term_dispatch_io(terminal_t* terminal, fd_set* read_set)
395{
396 if (term_is_valid(terminal))
397 if (FD_ISSET(terminal->term->pty_bridge, read_set))
398 shl_pty_bridge_dispatch(terminal->term->pty_bridge, 0);
399}
400
401bool term_exception(terminal_t* terminal, fd_set* exception_set)
402{
403 if (term_is_valid(terminal)) {
404 if (terminal->term->pty_bridge >= 0) {
405 return FD_ISSET(terminal->term->pty_bridge,
406 exception_set);
407 }
408 }
409
410 return false;
411}
412
413bool term_is_active(terminal_t* terminal)
414{
415 if (term_is_valid(terminal))
416 return terminal->active;
417
418 return false;
419}
420
421void term_add_fd(terminal_t* terminal, fd_set* read_set, fd_set* exception_set)
422{
423 if (term_is_valid(terminal)) {
424 if (terminal->term->pty_bridge >= 0) {
425 FD_SET(terminal->term->pty_bridge, read_set);
426 FD_SET(terminal->term->pty_bridge, exception_set);
427 }
428 }
429}
David Sodman8ef20062015-01-06 09:23:40 -0800430
431const char* term_get_ptsname(terminal_t* terminal)
432{
433 return ptsname(shl_pty_get_fd(terminal->term->pty));
434}
David Sodmanf0a925a2015-05-04 11:19:19 -0700435
436void term_set_background(terminal_t* terminal, uint32_t bg)
437{
438 terminal->background = bg;
439 terminal->background_valid = true;
440}
441
442int term_show_image(terminal_t* terminal, image_t* image)
443{
444 return image_show(image, terminal->video);
445}
446
447void term_write_message(terminal_t* terminal, char* message)
448{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800449 FILE* fp;
David Sodmanf0a925a2015-05-04 11:19:19 -0700450
451 fp = fopen(term_get_ptsname(terminal), "w");
452 if (fp) {
453 fputs(message, fp);
454 fclose(fp);
455 }
456}
457
458void term_hide_cursor(terminal_t* terminal)
459{
460 term_write_message(terminal, "\033[?25l");
461}
462
463void term_show_cursor(terminal_t* terminal)
464{
465 term_write_message(terminal, "\033[?25h");
466}
David Sodman30a94ef2015-07-26 17:37:12 -0700467
468video_t* term_getvideo(terminal_t* terminal)
469{
470 return terminal->video;
471}