blob: 85e1d381f67e443bf7a5f089e952d8c904b18895 [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 Sodman8ef20062015-01-06 09:23:40 -080034
35static char *interactive_cmd_line[] = {
36 "/sbin/agetty",
37 "-",
38 "9600",
39 "xterm",
40 NULL
41};
42
43
44static char *noninteractive_cmd_line[] = {
45 "/bin/cat",
46 NULL
47};
48
49
50static void __attribute__ ((noreturn)) term_run_child(terminal_t* terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070051{
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070052 /* XXX figure out how to fix "top" for xterm-256color */
53 setenv("TERM", "xterm", 1);
David Sodman8ef20062015-01-06 09:23:40 -080054 execve(terminal->exec[0], terminal->exec, environ);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070055 exit(1);
56}
57
58static int term_draw_cell(struct tsm_screen *screen, uint32_t id,
David Sodmanbbcb0522014-09-19 10:34:07 -070059 const uint32_t *ch, size_t len,
60 unsigned int cwidth, unsigned int posx,
61 unsigned int posy,
62 const struct tsm_screen_attr *attr,
63 tsm_age_t age, void *data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070064{
David Sodmanbbcb0522014-09-19 10:34:07 -070065 terminal_t *terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070066 uint32_t front_color, back_color;
67
David Sodmanbbcb0522014-09-19 10:34:07 -070068 if (age && terminal->term->age && age <= terminal->term->age)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070069 return 0;
70
71 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
72 back_color = (attr->br << 16) | (attr->bg << 8) | attr->bb;
73
74 if (attr->inverse) {
75 uint32_t tmp = front_color;
76 front_color = back_color;
77 back_color = tmp;
78 }
79
80 if (len)
David Sodmanbbcb0522014-09-19 10:34:07 -070081 font_render(terminal->term->dst_image, posx, posy, terminal->term->pitch, *ch,
82 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070083 else
David Sodmanbbcb0522014-09-19 10:34:07 -070084 font_fillchar(terminal->term->dst_image, posx, posy, terminal->term->pitch,
85 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070086
87 return 0;
88}
89
David Sodmanbbcb0522014-09-19 10:34:07 -070090void term_redraw(terminal_t *terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070091{
David Sodmanbbcb0522014-09-19 10:34:07 -070092 uint32_t *video_buffer;
93 video_buffer = video_lock(terminal->video);
94 if (video_buffer != NULL) {
95 terminal->term->dst_image = video_buffer;
96 terminal->term->age =
97 tsm_screen_draw(terminal->term->screen, term_draw_cell, terminal);
98 video_unlock(terminal->video);
99 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700100}
101
David Sodmanbbcb0522014-09-19 10:34:07 -0700102void term_key_event(terminal_t* terminal, uint32_t keysym, int32_t unicode)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700103{
104
David Sodmanbbcb0522014-09-19 10:34:07 -0700105 if (tsm_vte_handle_keyboard(terminal->term->vte, keysym, 0, 0, unicode))
106 tsm_screen_sb_reset(terminal->term->screen);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700107
David Sodmanbbcb0522014-09-19 10:34:07 -0700108 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700109}
110
111static void term_read_cb(struct shl_pty *pty, char *u8, size_t len, void *data)
112{
David Sodmanbbcb0522014-09-19 10:34:07 -0700113 terminal_t *terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700114
David Sodmanbbcb0522014-09-19 10:34:07 -0700115 tsm_vte_input(terminal->term->vte, u8, len);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700116
David Sodmanbbcb0522014-09-19 10:34:07 -0700117 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700118}
119
120static void term_write_cb(struct tsm_vte *vte, const char *u8, size_t len,
David Sodmanbbcb0522014-09-19 10:34:07 -0700121 void *data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700122{
123 struct term *term = data;
124 int r;
125
126 r = shl_pty_write(term->pty, u8, len);
127 if (r < 0)
David Sodmanbbcb0522014-09-19 10:34:07 -0700128 LOG(ERROR, "OOM in pty-write (%d)", r);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700129
130 shl_pty_dispatch(term->pty);
131}
132
133static const char *sev2str_table[] = {
134 "FATAL",
135 "ALERT",
136 "CRITICAL",
137 "ERROR",
138 "WARNING",
139 "NOTICE",
140 "INFO",
141 "DEBUG"
142};
143
144static const char *sev2str(unsigned int sev)
145{
146 if (sev > 7)
147 return "DEBUG";
148
149 return sev2str_table[sev];
150}
151
Dominik Behr00003502014-08-15 16:42:37 -0700152#ifdef __clang__
153__attribute__((__format__ (__printf__, 7, 0)))
154#endif
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700155static void log_tsm(void *data, const char *file, int line, const char *fn,
David Sodmanbbcb0522014-09-19 10:34:07 -0700156 const char *subs, unsigned int sev, const char *format,
157 va_list args)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700158{
159 fprintf(stderr, "%s: %s: ", sev2str(sev), subs);
160 vfprintf(stderr, format, args);
161 fprintf(stderr, "\n");
162}
163
David Sodman8ef20062015-01-06 09:23:40 -0800164terminal_t* term_init(bool interactive)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700165{
166 const int scrollback_size = 200;
167 uint32_t char_width, char_height;
David Sodmanbbcb0522014-09-19 10:34:07 -0700168 int status;
169 terminal_t *new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700170
David Sodmanbbcb0522014-09-19 10:34:07 -0700171 new_terminal = (terminal_t*)calloc(1, sizeof(*new_terminal));
David Sodmanbf3f2842014-11-12 08:26:58 -0800172 if (!new_terminal)
173 return NULL;
David Sodmanbbcb0522014-09-19 10:34:07 -0700174
David Sodmanbf3f2842014-11-12 08:26:58 -0800175 new_terminal->video = video_init();
176 if (!new_terminal->video) {
177 term_close(new_terminal);
178 return NULL;
179 }
180
181 new_terminal->term = (struct term*)calloc(1, sizeof(*new_terminal->term));
182 if (!new_terminal->term) {
183 term_close(new_terminal);
184 return NULL;
185 }
186
David Sodman8ef20062015-01-06 09:23:40 -0800187 if (interactive)
188 new_terminal->exec = interactive_cmd_line;
189 else
190 new_terminal->exec = noninteractive_cmd_line;
191
David Sodmanbf3f2842014-11-12 08:26:58 -0800192 font_init(video_getscaling(new_terminal->video));
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700193 font_get_size(&char_width, &char_height);
194
David Sodmanbf3f2842014-11-12 08:26:58 -0800195 new_terminal->term->char_x =
196 video_getwidth(new_terminal->video) / char_width;
197 new_terminal->term->char_y =
198 video_getheight(new_terminal->video) / char_height;
199 new_terminal->term->pitch = video_getpitch(new_terminal->video);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700200
David Sodmanbbcb0522014-09-19 10:34:07 -0700201 status = tsm_screen_new(&new_terminal->term->screen,
202 log_tsm, new_terminal->term);
203 if (new_terminal < 0) {
204 term_close(new_terminal);
205 return NULL;
206 }
207
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700208
David Sodmanbbcb0522014-09-19 10:34:07 -0700209 tsm_screen_set_max_sb(new_terminal->term->screen, scrollback_size);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700210
David Sodmanbbcb0522014-09-19 10:34:07 -0700211 status = tsm_vte_new(&new_terminal->term->vte, new_terminal->term->screen,
212 term_write_cb, new_terminal->term, log_tsm, new_terminal->term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700213
David Sodmanbbcb0522014-09-19 10:34:07 -0700214 if (status < 0) {
215 term_close(new_terminal);
216 return NULL;
217 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700218
David Sodmanbbcb0522014-09-19 10:34:07 -0700219 new_terminal->term->pty_bridge = shl_pty_bridge_new();
220 if (new_terminal->term->pty_bridge < 0) {
221 term_close(new_terminal);
222 return NULL;
223 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700224
David Sodmanbbcb0522014-09-19 10:34:07 -0700225 status = shl_pty_open(&new_terminal->term->pty,
226 term_read_cb, new_terminal, new_terminal->term->char_x,
227 new_terminal->term->char_y);
David Sodman8ef20062015-01-06 09:23:40 -0800228
David Sodmanbbcb0522014-09-19 10:34:07 -0700229 if (status < 0) {
230 term_close(new_terminal);
231 return NULL;
232 } else if (status == 0) {
David Sodman8ef20062015-01-06 09:23:40 -0800233 term_run_child(new_terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700234 exit(1);
235 }
236
David Sodmanbbcb0522014-09-19 10:34:07 -0700237 status = shl_pty_bridge_add(new_terminal->term->pty_bridge, new_terminal->term->pty);
238 if (status) {
239 shl_pty_close(new_terminal->term->pty);
240 term_close(new_terminal);
241 return NULL;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700242 }
243
David Sodmanbbcb0522014-09-19 10:34:07 -0700244 new_terminal->term->pid = shl_pty_get_child(new_terminal->term->pty);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700245
David Sodmanbbcb0522014-09-19 10:34:07 -0700246 status = tsm_screen_resize(new_terminal->term->screen,
247 new_terminal->term->char_x, new_terminal->term->char_y);
248 if (status < 0) {
249 shl_pty_close(new_terminal->term->pty);
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 status = shl_pty_resize(new_terminal->term->pty, new_terminal->term->char_x, new_terminal->term->char_y);
255 if (status < 0) {
256 shl_pty_close(new_terminal->term->pty);
257 term_close(new_terminal);
258 return NULL;
259 }
David Sodman8ef20062015-01-06 09:23:40 -0800260
261 if (interactive)
262 new_terminal->active = true;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700263
David Sodmanbbcb0522014-09-19 10:34:07 -0700264 return new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700265}
266
David Sodman8ef20062015-01-06 09:23:40 -0800267void term_activate(terminal_t* terminal)
268{
269 input_grab();
270 input_set_current(terminal);
271 terminal->active = true;
272 video_setmode(terminal->video);
273 term_redraw(terminal);
274}
275
David Sodmanbbcb0522014-09-19 10:34:07 -0700276void term_set_dbus(terminal_t *term, dbus_t* dbus)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700277{
David Sodmanbbcb0522014-09-19 10:34:07 -0700278 term->dbus = dbus;
279}
280
281void term_close(terminal_t *term)
282{
283 if (!term)
284 return;
285
David Sodmanbf3f2842014-11-12 08:26:58 -0800286 if (term->video) {
287 video_close(term->video);
288 term->video = NULL;
289 }
290
David Sodmanbbcb0522014-09-19 10:34:07 -0700291 if (term->term) {
292 free(term->term);
293 term->term = NULL;
294 }
295
296 free(term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700297}
David Sodmanbf3f2842014-11-12 08:26:58 -0800298
299bool term_is_child_done(terminal_t* terminal)
300{
301 int status;
302 int ret;
303 ret = waitpid(terminal->term->pid, &status, WNOHANG);
304
305 return ret != 0;
306}
307
308void term_page_up(terminal_t* terminal)
309{
310 tsm_screen_sb_page_up(terminal->term->screen, 1);
311 term_redraw(terminal);
312}
313
314void term_page_down(terminal_t* terminal)
315{
316 tsm_screen_sb_page_down(terminal->term->screen, 1);
317 term_redraw(terminal);
318}
319
320void term_line_up(terminal_t* terminal)
321{
322 tsm_screen_sb_up(terminal->term->screen, 1);
323 term_redraw(terminal);
324}
325
326void term_line_down(terminal_t* terminal)
327{
328 tsm_screen_sb_down(terminal->term->screen, 1);
329 term_redraw(terminal);
330}
331
332bool term_is_valid(terminal_t* terminal)
333{
334 return ((terminal != NULL) && (terminal->term != NULL));
335}
336
337int term_fd(terminal_t* terminal)
338{
339 if (term_is_valid(terminal))
340 return terminal->term->pty_bridge;
341 else
342 return -1;
343}
344
345void term_dispatch_io(terminal_t* terminal, fd_set* read_set)
346{
347 if (term_is_valid(terminal))
348 if (FD_ISSET(terminal->term->pty_bridge, read_set))
349 shl_pty_bridge_dispatch(terminal->term->pty_bridge, 0);
350}
351
352bool term_exception(terminal_t* terminal, fd_set* exception_set)
353{
354 if (term_is_valid(terminal)) {
355 if (terminal->term->pty_bridge >= 0) {
356 return FD_ISSET(terminal->term->pty_bridge,
357 exception_set);
358 }
359 }
360
361 return false;
362}
363
364bool term_is_active(terminal_t* terminal)
365{
366 if (term_is_valid(terminal))
367 return terminal->active;
368
369 return false;
370}
371
372void term_add_fd(terminal_t* terminal, fd_set* read_set, fd_set* exception_set)
373{
374 if (term_is_valid(terminal)) {
375 if (terminal->term->pty_bridge >= 0) {
376 FD_SET(terminal->term->pty_bridge, read_set);
377 FD_SET(terminal->term->pty_bridge, exception_set);
378 }
379 }
380}
David Sodman8ef20062015-01-06 09:23:40 -0800381
382const char* term_get_ptsname(terminal_t* terminal)
383{
384 return ptsname(shl_pty_get_fd(terminal->term->pty));
385}