blob: 298f574f5591b304b3bbb8bee1de7ef9a53dddde [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
David Sodman30a94ef2015-07-26 17:37:12 -0700204 if (video) {
David Sodmanf0a925a2015-05-04 11:19:19 -0700205 new_terminal->video = video;
David Sodman30a94ef2015-07-26 17:37:12 -0700206 video_addref(video);
207 }
David Sodmanf0a925a2015-05-04 11:19:19 -0700208 else
209 new_terminal->video = video_init();
210
David Sodmanbf3f2842014-11-12 08:26:58 -0800211 if (!new_terminal->video) {
212 term_close(new_terminal);
213 return NULL;
214 }
215
216 new_terminal->term = (struct term*)calloc(1, sizeof(*new_terminal->term));
217 if (!new_terminal->term) {
218 term_close(new_terminal);
219 return NULL;
220 }
221
David Sodman8ef20062015-01-06 09:23:40 -0800222 if (interactive)
223 new_terminal->exec = interactive_cmd_line;
224 else
225 new_terminal->exec = noninteractive_cmd_line;
226
David Sodmanbf3f2842014-11-12 08:26:58 -0800227 font_init(video_getscaling(new_terminal->video));
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700228 font_get_size(&char_width, &char_height);
229
David Sodmanbf3f2842014-11-12 08:26:58 -0800230 new_terminal->term->char_x =
231 video_getwidth(new_terminal->video) / char_width;
232 new_terminal->term->char_y =
233 video_getheight(new_terminal->video) / char_height;
234 new_terminal->term->pitch = video_getpitch(new_terminal->video);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700235
David Sodmanbbcb0522014-09-19 10:34:07 -0700236 status = tsm_screen_new(&new_terminal->term->screen,
237 log_tsm, new_terminal->term);
238 if (new_terminal < 0) {
239 term_close(new_terminal);
240 return NULL;
241 }
242
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700243
David Sodmanbbcb0522014-09-19 10:34:07 -0700244 tsm_screen_set_max_sb(new_terminal->term->screen, scrollback_size);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700245
David Sodmanbbcb0522014-09-19 10:34:07 -0700246 status = tsm_vte_new(&new_terminal->term->vte, new_terminal->term->screen,
247 term_write_cb, new_terminal->term, log_tsm, new_terminal->term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700248
David Sodmanbbcb0522014-09-19 10:34:07 -0700249 if (status < 0) {
250 term_close(new_terminal);
251 return NULL;
252 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700253
David Sodmanbbcb0522014-09-19 10:34:07 -0700254 new_terminal->term->pty_bridge = shl_pty_bridge_new();
255 if (new_terminal->term->pty_bridge < 0) {
256 term_close(new_terminal);
257 return NULL;
258 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700259
David Sodmanbbcb0522014-09-19 10:34:07 -0700260 status = shl_pty_open(&new_terminal->term->pty,
261 term_read_cb, new_terminal, new_terminal->term->char_x,
262 new_terminal->term->char_y);
David Sodman8ef20062015-01-06 09:23:40 -0800263
David Sodmanbbcb0522014-09-19 10:34:07 -0700264 if (status < 0) {
265 term_close(new_terminal);
266 return NULL;
267 } else if (status == 0) {
David Sodman8ef20062015-01-06 09:23:40 -0800268 term_run_child(new_terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700269 exit(1);
270 }
271
David Sodmanbbcb0522014-09-19 10:34:07 -0700272 status = shl_pty_bridge_add(new_terminal->term->pty_bridge, new_terminal->term->pty);
273 if (status) {
274 shl_pty_close(new_terminal->term->pty);
275 term_close(new_terminal);
276 return NULL;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700277 }
278
David Sodmanbbcb0522014-09-19 10:34:07 -0700279 new_terminal->term->pid = shl_pty_get_child(new_terminal->term->pty);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700280
David Sodmanbbcb0522014-09-19 10:34:07 -0700281 status = tsm_screen_resize(new_terminal->term->screen,
282 new_terminal->term->char_x, new_terminal->term->char_y);
283 if (status < 0) {
284 shl_pty_close(new_terminal->term->pty);
285 term_close(new_terminal);
286 return NULL;
287 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700288
David Sodmanbbcb0522014-09-19 10:34:07 -0700289 status = shl_pty_resize(new_terminal->term->pty, new_terminal->term->char_x, new_terminal->term->char_y);
290 if (status < 0) {
291 shl_pty_close(new_terminal->term->pty);
292 term_close(new_terminal);
293 return NULL;
294 }
David Sodman8ef20062015-01-06 09:23:40 -0800295
296 if (interactive)
297 new_terminal->active = true;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700298
David Sodmanbbcb0522014-09-19 10:34:07 -0700299 return new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700300}
301
David Sodman8ef20062015-01-06 09:23:40 -0800302void term_activate(terminal_t* terminal)
303{
David Sodman8ef20062015-01-06 09:23:40 -0800304 input_set_current(terminal);
305 terminal->active = true;
306 video_setmode(terminal->video);
307 term_redraw(terminal);
308}
309
David Sodmanf0a925a2015-05-04 11:19:19 -0700310void term_deactivate(terminal_t* terminal)
311{
312 if (!terminal->active)
313 return;
314
David Sodmanf0a925a2015-05-04 11:19:19 -0700315 terminal->active = false;
316 video_release(terminal->video);
317}
318
David Sodmanbbcb0522014-09-19 10:34:07 -0700319void term_set_dbus(terminal_t *term, dbus_t* dbus)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700320{
David Sodmanbbcb0522014-09-19 10:34:07 -0700321 term->dbus = dbus;
322}
323
324void term_close(terminal_t *term)
325{
326 if (!term)
327 return;
328
David Sodmanbf3f2842014-11-12 08:26:58 -0800329 if (term->video) {
330 video_close(term->video);
331 term->video = NULL;
332 }
333
David Sodmanbbcb0522014-09-19 10:34:07 -0700334 if (term->term) {
335 free(term->term);
336 term->term = NULL;
337 }
338
Haixia Shi95d680e2015-04-27 20:29:17 -0700339 font_free();
David Sodmanbbcb0522014-09-19 10:34:07 -0700340 free(term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700341}
David Sodmanbf3f2842014-11-12 08:26:58 -0800342
343bool term_is_child_done(terminal_t* terminal)
344{
345 int status;
346 int ret;
347 ret = waitpid(terminal->term->pid, &status, WNOHANG);
348
David Sodmanf0a925a2015-05-04 11:19:19 -0700349 if ((ret == -1) && (errno == ECHILD)) {
350 return false;
351 }
David Sodmanbf3f2842014-11-12 08:26:58 -0800352 return ret != 0;
353}
354
355void term_page_up(terminal_t* terminal)
356{
357 tsm_screen_sb_page_up(terminal->term->screen, 1);
358 term_redraw(terminal);
359}
360
361void term_page_down(terminal_t* terminal)
362{
363 tsm_screen_sb_page_down(terminal->term->screen, 1);
364 term_redraw(terminal);
365}
366
367void term_line_up(terminal_t* terminal)
368{
369 tsm_screen_sb_up(terminal->term->screen, 1);
370 term_redraw(terminal);
371}
372
373void term_line_down(terminal_t* terminal)
374{
375 tsm_screen_sb_down(terminal->term->screen, 1);
376 term_redraw(terminal);
377}
378
379bool term_is_valid(terminal_t* terminal)
380{
381 return ((terminal != NULL) && (terminal->term != NULL));
382}
383
384int term_fd(terminal_t* terminal)
385{
386 if (term_is_valid(terminal))
387 return terminal->term->pty_bridge;
388 else
389 return -1;
390}
391
392void term_dispatch_io(terminal_t* terminal, fd_set* read_set)
393{
394 if (term_is_valid(terminal))
395 if (FD_ISSET(terminal->term->pty_bridge, read_set))
396 shl_pty_bridge_dispatch(terminal->term->pty_bridge, 0);
397}
398
399bool term_exception(terminal_t* terminal, fd_set* exception_set)
400{
401 if (term_is_valid(terminal)) {
402 if (terminal->term->pty_bridge >= 0) {
403 return FD_ISSET(terminal->term->pty_bridge,
404 exception_set);
405 }
406 }
407
408 return false;
409}
410
411bool term_is_active(terminal_t* terminal)
412{
413 if (term_is_valid(terminal))
414 return terminal->active;
415
416 return false;
417}
418
419void term_add_fd(terminal_t* terminal, fd_set* read_set, fd_set* exception_set)
420{
421 if (term_is_valid(terminal)) {
422 if (terminal->term->pty_bridge >= 0) {
423 FD_SET(terminal->term->pty_bridge, read_set);
424 FD_SET(terminal->term->pty_bridge, exception_set);
425 }
426 }
427}
David Sodman8ef20062015-01-06 09:23:40 -0800428
429const char* term_get_ptsname(terminal_t* terminal)
430{
431 return ptsname(shl_pty_get_fd(terminal->term->pty));
432}
David Sodmanf0a925a2015-05-04 11:19:19 -0700433
434void term_set_background(terminal_t* terminal, uint32_t bg)
435{
436 terminal->background = bg;
437 terminal->background_valid = true;
438}
439
440int term_show_image(terminal_t* terminal, image_t* image)
441{
442 return image_show(image, terminal->video);
443}
444
445void term_write_message(terminal_t* terminal, char* message)
446{
447 FILE *fp;
448
449 fp = fopen(term_get_ptsname(terminal), "w");
450 if (fp) {
451 fputs(message, fp);
452 fclose(fp);
453 }
454}
455
456void term_hide_cursor(terminal_t* terminal)
457{
458 term_write_message(terminal, "\033[?25l");
459}
460
461void term_show_cursor(terminal_t* terminal)
462{
463 term_write_message(terminal, "\033[?25h");
464}
David Sodman30a94ef2015-07-26 17:37:12 -0700465
466video_t* term_getvideo(terminal_t* terminal)
467{
468 return terminal->video;
469}