blob: 48ea6d5c832631f5c6687b8eefeb9cea40461320 [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>
Dominik Behrda6df412016-08-02 12:56:42 -07008#include <fcntl.h>
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -07009#include <libtsm.h>
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070010#include <stdio.h>
Dominik Behrda6df412016-08-02 12:56:42 -070011#include <stdlib.h>
Dominik Behr93899452014-08-18 22:16:21 -070012#include <sys/select.h>
Dominik Behrda6df412016-08-02 12:56:42 -070013#include <sys/stat.h>
David Sodmanbf3f2842014-11-12 08:26:58 -080014#include <sys/types.h>
15#include <sys/wait.h>
Dominik Behr32680e32016-08-16 12:18:41 -070016#include <unistd.h>
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070017
Dominik Behr83864df2016-04-21 12:35:08 -070018#include "dbus.h"
Dominik Behr83010f82016-03-18 18:43:08 -070019#include "fb.h"
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070020#include "font.h"
Dominik Behrd2530902016-05-05 14:01:06 -070021#include "image.h"
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070022#include "input.h"
Dominik Behr83864df2016-04-21 12:35:08 -070023#include "main.h"
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070024#include "shl_pty.h"
25#include "term.h"
26#include "util.h"
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070027
Dominik Behrda6df412016-08-02 12:56:42 -070028unsigned int term_num_terminals = 4;
29static terminal_t* terminals[TERM_MAX_TERMINALS];
Dominik Behr4defb362016-01-13 12:36:14 -080030static uint32_t current_terminal = 0;
Stéphane Marchesin08c08e72016-01-07 16:44:08 -080031
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070032struct term {
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080033 struct tsm_screen* screen;
34 struct tsm_vte* vte;
35 struct shl_pty* pty;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070036 int pty_bridge;
37 int pid;
38 tsm_age_t age;
39 int char_x, char_y;
40 int pitch;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080041 uint32_t* dst_image;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070042};
43
David Sodmanf0a925a2015-05-04 11:19:19 -070044struct _terminal_t {
Dominik Behrda6df412016-08-02 12:56:42 -070045 unsigned vt;
46 bool active;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080047 uint32_t background;
48 bool background_valid;
Dominik Behr83010f82016-03-18 18:43:08 -070049 fb_t* fb;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080050 struct term* term;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080051 char** exec;
David Sodmanf0a925a2015-05-04 11:19:19 -070052};
53
David Sodman8ef20062015-01-06 09:23:40 -080054
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080055static char* interactive_cmd_line[] = {
David Sodman8ef20062015-01-06 09:23:40 -080056 "/sbin/agetty",
57 "-",
58 "9600",
59 "xterm",
60 NULL
61};
62
Dominik Behr83864df2016-04-21 12:35:08 -070063static bool in_background = false;
64static bool hotplug_occured = false;
65
David Sodman8ef20062015-01-06 09:23:40 -080066
67static void __attribute__ ((noreturn)) term_run_child(terminal_t* terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070068{
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070069 /* XXX figure out how to fix "top" for xterm-256color */
70 setenv("TERM", "xterm", 1);
Dominik Behr32680e32016-08-16 12:18:41 -070071 if (terminal->exec) {
72 execve(terminal->exec[0], terminal->exec, environ);
73 exit(1);
74 } else {
75 while (1)
76 sleep(1000000);
77 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070078}
79
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080080static int term_draw_cell(struct tsm_screen* screen, uint32_t id,
Stéphane Marchesin8fc13522015-12-14 17:02:28 -080081 const uint32_t* ch, size_t len,
82 unsigned int cwidth, unsigned int posx,
83 unsigned int posy,
84 const struct tsm_screen_attr* attr,
85 tsm_age_t age, void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070086{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080087 terminal_t* terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070088 uint32_t front_color, back_color;
David Sodmanf0a925a2015-05-04 11:19:19 -070089 uint8_t br, bb, bg;
Stéphane Marchesinedece332015-12-14 15:10:58 -080090 uint32_t luminance;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070091
David Sodmanbbcb0522014-09-19 10:34:07 -070092 if (age && terminal->term->age && age <= terminal->term->age)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070093 return 0;
94
David Sodmanf0a925a2015-05-04 11:19:19 -070095 if (terminal->background_valid) {
96 br = (terminal->background >> 16) & 0xFF;
97 bg = (terminal->background >> 8) & 0xFF;
98 bb = (terminal->background) & 0xFF;
Stéphane Marchesinedece332015-12-14 15:10:58 -080099 luminance = (3 * br + bb + 4 * bg) >> 3;
David Sodmanf0a925a2015-05-04 11:19:19 -0700100
Stéphane Marchesinedece332015-12-14 15:10:58 -0800101 /*
102 * FIXME: black is chosen on a dark background, but it uses the
103 * default color for light backgrounds
104 */
105 if (luminance > 128) {
David Sodmanf0a925a2015-05-04 11:19:19 -0700106 front_color = 0;
107 back_color = terminal->background;
108 } else {
109 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
110 back_color = terminal->background;
111 }
112 } else {
113 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
114 back_color = (attr->br << 16) | (attr->bg << 8) | attr->bb;
115 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700116
117 if (attr->inverse) {
118 uint32_t tmp = front_color;
119 front_color = back_color;
120 back_color = tmp;
121 }
122
123 if (len)
David Sodmanbbcb0522014-09-19 10:34:07 -0700124 font_render(terminal->term->dst_image, posx, posy, terminal->term->pitch, *ch,
125 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700126 else
David Sodmanbbcb0522014-09-19 10:34:07 -0700127 font_fillchar(terminal->term->dst_image, posx, posy, terminal->term->pitch,
128 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700129
130 return 0;
131}
132
Stéphane Marchesine2a46cd2016-01-07 16:45:24 -0800133static void term_redraw(terminal_t* terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700134{
Dominik Behr83010f82016-03-18 18:43:08 -0700135 uint32_t* fb_buffer;
136 fb_buffer = fb_lock(terminal->fb);
137 if (fb_buffer != NULL) {
138 terminal->term->dst_image = fb_buffer;
David Sodmanbbcb0522014-09-19 10:34:07 -0700139 terminal->term->age =
140 tsm_screen_draw(terminal->term->screen, term_draw_cell, terminal);
Dominik Behr83010f82016-03-18 18:43:08 -0700141 fb_unlock(terminal->fb);
David Sodmanbbcb0522014-09-19 10:34:07 -0700142 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700143}
144
David Sodmanbbcb0522014-09-19 10:34:07 -0700145void term_key_event(terminal_t* terminal, uint32_t keysym, int32_t unicode)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700146{
David Sodmanbbcb0522014-09-19 10:34:07 -0700147 if (tsm_vte_handle_keyboard(terminal->term->vte, keysym, 0, 0, unicode))
148 tsm_screen_sb_reset(terminal->term->screen);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700149
David Sodmanbbcb0522014-09-19 10:34:07 -0700150 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700151}
152
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800153static void term_read_cb(struct shl_pty* pty, char* u8, size_t len, void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700154{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800155 terminal_t* terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700156
David Sodmanbbcb0522014-09-19 10:34:07 -0700157 tsm_vte_input(terminal->term->vte, u8, len);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700158
David Sodmanbbcb0522014-09-19 10:34:07 -0700159 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700160}
161
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800162static void term_write_cb(struct tsm_vte* vte, const char* u8, size_t len,
163 void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700164{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800165 struct term* term = data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700166 int r;
167
168 r = shl_pty_write(term->pty, u8, len);
169 if (r < 0)
David Sodmanbbcb0522014-09-19 10:34:07 -0700170 LOG(ERROR, "OOM in pty-write (%d)", r);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700171
172 shl_pty_dispatch(term->pty);
173}
174
Dominik Behrd2530902016-05-05 14:01:06 -0700175static void term_esc_show_image(terminal_t* terminal, char* params)
176{
177 char* tok;
178 image_t* image;
179 int status;
180
181 image = image_create();
182 if (!image) {
183 LOG(ERROR, "Out of memory when creating an image.\n");
184 return;
185 }
186 for (tok = strtok(params, ";"); tok; tok = strtok(NULL, ";")) {
187 if (strncmp("file=", tok, 5) == 0) {
188 image_set_filename(image, tok + 5);
189 } else if (strncmp("location=", tok, 9) == 0) {
190 uint32_t x, y;
191 if (sscanf(tok + 9, "%u,%u", &x, &y) != 2) {
192 LOG(ERROR, "Error parsing image location.\n");
193 goto done;
194 }
195 image_set_location(image, x, y);
196 } else if (strncmp("offset=", tok, 7) == 0) {
197 int32_t x, y;
198 if (sscanf(tok + 7, "%d,%d", &x, &y) != 2) {
199 LOG(ERROR, "Error parsing image offset.\n");
200 goto done;
201 }
202 image_set_offset(image, x, y);
203 } else if (strncmp("scale=", tok, 6) == 0) {
204 uint32_t s;
205 if (sscanf(tok + 6, "%u", &s) != 1) {
206 LOG(ERROR, "Error parsing image scale.\n");
207 goto done;
208 }
209 if (s == 0)
210 s = image_get_auto_scale(term_getfb(terminal));
211 image_set_scale(image, s);
212 }
213 }
214
215 status = image_load_image_from_file(image);
216 if (status != 0) {
217 LOG(WARNING, "Term ESC image_load_image_from_file %s failed: %d:%s.",
218 image_get_filename(image), status, strerror(status));
219 } else {
220 term_show_image(terminal, image);
221 }
222done:
223 image_destroy(image);
224}
225
226static void term_esc_draw_box(terminal_t* terminal, char* params)
227{
228 char* tok;
229 uint32_t color = 0;
230 uint32_t w = 1;
231 uint32_t h = 1;
232 uint32_t locx, locy;
233 bool use_location = false;
234 int32_t offx, offy;
235 bool use_offset = false;
236 uint32_t scale = 1;
237 uint32_t* buffer;
238 int32_t startx, starty;
239 uint32_t pitch4;
240
241 for (tok = strtok(params, ";"); tok; tok = strtok(NULL, ";")) {
242 if (strncmp("color=", tok, 6) == 0) {
243 color = strtoul(tok + 6, NULL, 0);
244 } else if (strncmp("size=", tok, 5) == 0) {
245 if (sscanf(tok + 5, "%u,%u", &w, &h) != 2) {
246 LOG(ERROR, "Error parsing box size.\n");
247 goto done;
248 }
249 } else if (strncmp("location=", tok, 9) == 0) {
250 if (sscanf(tok + 9, "%u,%u", &locx, &locy) != 2) {
251 LOG(ERROR, "Error parsing box location.\n");
252 goto done;
253 }
254 use_location = true;
255 } else if (strncmp("offset=", tok, 7) == 0) {
256 if (sscanf(tok + 7, "%d,%d", &offx, &offy) != 2) {
257 LOG(ERROR, "Error parsing box offset.\n");
258 goto done;
259 }
260 use_offset = true;
261 } else if (strncmp("scale=", tok, 6) == 0) {
262 if (sscanf(tok + 6, "%u", &scale) != 1) {
263 LOG(ERROR, "Error parsing box scale.\n");
264 goto done;
265 }
266 if (scale == 0)
267 scale = image_get_auto_scale(term_getfb(terminal));
268 }
269 }
270
271 w *= scale;
272 h *= scale;
273 offx *= scale;
274 offy *= scale;
275
276 buffer = fb_lock(terminal->fb);
277 if (buffer == NULL)
278 goto done;
279
280 if (use_offset && use_location) {
281 LOG(WARNING, "Box offset and location set, using location.");
282 use_offset = false;
283 }
284
285 if (use_location) {
286 startx = locx;
287 starty = locy;
288 } else {
289 startx = (fb_getwidth(terminal->fb) - (int32_t)w)/2;
290 starty = (fb_getheight(terminal->fb) - (int32_t)h)/2;
291 }
292
293 if (use_offset) {
294 startx += offx;
295 starty += offy;
296 }
297
298 pitch4 = fb_getpitch(terminal->fb) / 4;
299
300 /* Completely outside buffer, do nothing */
301 if (startx + w <= 0 || startx >= fb_getwidth(terminal->fb))
302 goto done_fb;
303 if (starty + h <= 0 || starty >= fb_getheight(terminal->fb))
304 goto done_fb;
305 /* Make sure we are inside buffer. */
306 if (startx < 0)
307 startx = 0;
308 if (startx + (int32_t)w > fb_getwidth(terminal->fb))
309 w = fb_getwidth(terminal->fb) - startx;
310 if (starty < 0)
311 starty = 0;
312 if (starty + (int32_t)h > fb_getheight(terminal->fb))
313 h = fb_getheight(terminal->fb) - starty;
314
315 for (uint32_t y = 0; y < h; y++) {
316 uint32_t *o = buffer + (starty + y) * pitch4
317 + startx;
318 for (uint32_t x = 0; x < w; x++)
319 o[x] = color;
320 }
321done_fb:
322 fb_unlock(terminal->fb);
323done:
324 ;
325}
326
327static void term_osc_cb(struct tsm_vte *vte, const uint32_t *osc_string,
328 size_t osc_len, void *data)
329{
330 terminal_t* terminal = (terminal_t*)data;
331 size_t i;
332 char *osc;
333
334 for (i = 0; i < osc_len; i++)
335 if (osc_string[i] >= 128)
336 return; /* we only want to deal with ASCII */
337
338 osc = malloc(osc_len + 1);
339 if (!osc) {
340 LOG(WARNING, "Out of memory when processing OSC\n");
341 return;
342 }
343
344 for (i = 0; i < osc_len; i++)
345 osc[i] = (char)osc_string[i];
346 osc[i] = '\0';
347
348 if (strncmp(osc, "image:", 6) == 0)
349 term_esc_show_image(terminal, osc + 6);
350 else if (strncmp(osc, "box:", 4) == 0)
351 term_esc_draw_box(terminal, osc + 4);
352 else
353 LOG(WARNING, "Unknown OSC escape sequence \"%s\", ignoring.", osc);
354
355 free(osc);
356}
357
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800358static const char* sev2str_table[] = {
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700359 "FATAL",
360 "ALERT",
361 "CRITICAL",
362 "ERROR",
363 "WARNING",
364 "NOTICE",
365 "INFO",
366 "DEBUG"
367};
368
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800369static const char* sev2str(unsigned int sev)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700370{
371 if (sev > 7)
372 return "DEBUG";
373
374 return sev2str_table[sev];
375}
376
Dominik Behr00003502014-08-15 16:42:37 -0700377#ifdef __clang__
378__attribute__((__format__ (__printf__, 7, 0)))
379#endif
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800380static void log_tsm(void* data, const char* file, int line, const char* fn,
Stéphane Marchesin8fc13522015-12-14 17:02:28 -0800381 const char* subs, unsigned int sev, const char* format,
382 va_list args)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700383{
384 fprintf(stderr, "%s: %s: ", sev2str(sev), subs);
385 vfprintf(stderr, format, args);
386 fprintf(stderr, "\n");
387}
388
Stéphane Marchesin78310662016-01-06 16:09:39 -0800389static int term_resize(terminal_t* term)
390{
391 uint32_t char_width, char_height;
392 int status;
393
Dominik Behr83010f82016-03-18 18:43:08 -0700394 font_init(fb_getscaling(term->fb));
Stéphane Marchesin78310662016-01-06 16:09:39 -0800395 font_get_size(&char_width, &char_height);
396
Dominik Behr83010f82016-03-18 18:43:08 -0700397 term->term->char_x = fb_getwidth(term->fb) / char_width;
398 term->term->char_y = fb_getheight(term->fb) / char_height;
399 term->term->pitch = fb_getpitch(term->fb);
Stéphane Marchesin78310662016-01-06 16:09:39 -0800400
401 status = tsm_screen_resize(term->term->screen,
402 term->term->char_x, term->term->char_y);
403 if (status < 0) {
404 font_free();
405 return -1;
406 }
407
408 status = shl_pty_resize(term->term->pty, term->term->char_x,
409 term->term->char_y);
410 if (status < 0) {
411 font_free();
412 return -1;
413 }
414
415 return 0;
416}
417
Dominik Behrda6df412016-08-02 12:56:42 -0700418void term_set_num_terminals(unsigned new_num)
419{
420 if (new_num < 1)
421 term_num_terminals = 1;
422 else if (new_num > TERM_MAX_TERMINALS)
423 term_num_terminals = TERM_MAX_TERMINALS;
424 else
425 term_num_terminals = new_num;
426}
427
428static bool term_is_interactive(unsigned int vt)
429{
430 if (command_flags.no_login)
431 return false;
432
433 if (vt == TERM_SPLASH_TERMINAL)
434 return command_flags.enable_vt1;
435
436 return true;
437}
438
439terminal_t* term_init(unsigned vt, int pts_fd)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700440{
441 const int scrollback_size = 200;
David Sodmanbbcb0522014-09-19 10:34:07 -0700442 int status;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800443 terminal_t* new_terminal;
Dominik Behrda6df412016-08-02 12:56:42 -0700444 bool interactive = term_is_interactive(vt);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700445
David Sodmanbbcb0522014-09-19 10:34:07 -0700446 new_terminal = (terminal_t*)calloc(1, sizeof(*new_terminal));
David Sodmanbf3f2842014-11-12 08:26:58 -0800447 if (!new_terminal)
448 return NULL;
David Sodmanbbcb0522014-09-19 10:34:07 -0700449
Dominik Behrda6df412016-08-02 12:56:42 -0700450 new_terminal->vt = vt;
David Sodmanf0a925a2015-05-04 11:19:19 -0700451 new_terminal->background_valid = false;
452
Dominik Behr83010f82016-03-18 18:43:08 -0700453 new_terminal->fb = fb_init();
David Sodmanf0a925a2015-05-04 11:19:19 -0700454
Dominik Behr83010f82016-03-18 18:43:08 -0700455 if (!new_terminal->fb) {
Dominik Behrf9329912016-08-25 17:31:52 -0700456 LOG(ERROR, "Failed to create fb on VT%u.", vt);
David Sodmanbf3f2842014-11-12 08:26:58 -0800457 term_close(new_terminal);
458 return NULL;
459 }
460
461 new_terminal->term = (struct term*)calloc(1, sizeof(*new_terminal->term));
462 if (!new_terminal->term) {
463 term_close(new_terminal);
464 return NULL;
465 }
466
Dominik Behrda6df412016-08-02 12:56:42 -0700467 if (interactive)
David Sodman8ef20062015-01-06 09:23:40 -0800468 new_terminal->exec = interactive_cmd_line;
469 else
Dominik Behr32680e32016-08-16 12:18:41 -0700470 new_terminal->exec = NULL;
David Sodman8ef20062015-01-06 09:23:40 -0800471
David Sodmanbbcb0522014-09-19 10:34:07 -0700472 status = tsm_screen_new(&new_terminal->term->screen,
473 log_tsm, new_terminal->term);
zhuo-hao471f1e22015-08-12 14:55:56 +0800474 if (status < 0) {
Dominik Behrf9329912016-08-25 17:31:52 -0700475 LOG(ERROR, "Failed to create new screen on VT%u.", vt);
David Sodmanbbcb0522014-09-19 10:34:07 -0700476 term_close(new_terminal);
477 return NULL;
478 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700479
David Sodmanbbcb0522014-09-19 10:34:07 -0700480 tsm_screen_set_max_sb(new_terminal->term->screen, scrollback_size);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700481
David Sodmanbbcb0522014-09-19 10:34:07 -0700482 status = tsm_vte_new(&new_terminal->term->vte, new_terminal->term->screen,
483 term_write_cb, new_terminal->term, log_tsm, new_terminal->term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700484
David Sodmanbbcb0522014-09-19 10:34:07 -0700485 if (status < 0) {
Dominik Behrf9329912016-08-25 17:31:52 -0700486 LOG(ERROR, "Failed to create new VT%u.", vt);
David Sodmanbbcb0522014-09-19 10:34:07 -0700487 term_close(new_terminal);
488 return NULL;
489 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700490
Dominik Behrd2530902016-05-05 14:01:06 -0700491 if (command_flags.enable_gfx)
492 tsm_vte_set_osc_cb(new_terminal->term->vte, term_osc_cb, (void *)new_terminal);
493
David Sodmanbbcb0522014-09-19 10:34:07 -0700494 new_terminal->term->pty_bridge = shl_pty_bridge_new();
495 if (new_terminal->term->pty_bridge < 0) {
Dominik Behrf9329912016-08-25 17:31:52 -0700496 LOG(ERROR, "Failed to create pty bridge on VT%u.", vt);
David Sodmanbbcb0522014-09-19 10:34:07 -0700497 term_close(new_terminal);
498 return NULL;
499 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700500
David Sodmanbbcb0522014-09-19 10:34:07 -0700501 status = shl_pty_open(&new_terminal->term->pty,
Dominik Behrda6df412016-08-02 12:56:42 -0700502 term_read_cb, new_terminal, 1, 1, pts_fd);
David Sodman8ef20062015-01-06 09:23:40 -0800503
David Sodmanbbcb0522014-09-19 10:34:07 -0700504 if (status < 0) {
Dominik Behrf9329912016-08-25 17:31:52 -0700505 LOG(ERROR, "Failed to open pty on VT%u.", vt);
David Sodmanbbcb0522014-09-19 10:34:07 -0700506 term_close(new_terminal);
507 return NULL;
508 } else if (status == 0) {
David Sodman8ef20062015-01-06 09:23:40 -0800509 term_run_child(new_terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700510 exit(1);
511 }
512
Dominik Behrda6df412016-08-02 12:56:42 -0700513 status = mkdir(FRECON_RUN_DIR, S_IRWXU);
514 if (status == 0 || (status < 0 && errno == EEXIST)) {
515 char path[32];
516 snprintf(path, sizeof(path), FRECON_VT_PATH, vt);
Dominik Behrb23b05b2016-08-15 16:29:02 -0700517 unlink(path); /* In case it already exists. Ignore return codes. */
Dominik Behrda6df412016-08-02 12:56:42 -0700518 if (symlink(ptsname(shl_pty_get_fd(new_terminal->term->pty)), path) < 0)
519 LOG(ERROR, "Failed to symlink pts name %s to %s, %d:%s",
520 path,
521 ptsname(shl_pty_get_fd(new_terminal->term->pty)),
522 errno, strerror(errno));
523 }
524
David Sodmanbbcb0522014-09-19 10:34:07 -0700525 status = shl_pty_bridge_add(new_terminal->term->pty_bridge, new_terminal->term->pty);
526 if (status) {
Dominik Behrf9329912016-08-25 17:31:52 -0700527 LOG(ERROR, "Failed to add pty bridge on VT%u.", vt);
David Sodmanbbcb0522014-09-19 10:34:07 -0700528 term_close(new_terminal);
529 return NULL;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700530 }
531
David Sodmanbbcb0522014-09-19 10:34:07 -0700532 new_terminal->term->pid = shl_pty_get_child(new_terminal->term->pty);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700533
Stéphane Marchesin78310662016-01-06 16:09:39 -0800534 status = term_resize(new_terminal);
Dominik Behr83010f82016-03-18 18:43:08 -0700535
David Sodmanbbcb0522014-09-19 10:34:07 -0700536 if (status < 0) {
Dominik Behrf9329912016-08-25 17:31:52 -0700537 LOG(ERROR, "Failed to resize VT%u.", vt);
David Sodmanbbcb0522014-09-19 10:34:07 -0700538 term_close(new_terminal);
539 return NULL;
540 }
David Sodman8ef20062015-01-06 09:23:40 -0800541
David Sodmanbbcb0522014-09-19 10:34:07 -0700542 return new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700543}
544
David Sodman8ef20062015-01-06 09:23:40 -0800545void term_activate(terminal_t* terminal)
546{
Dominik Behr4defb362016-01-13 12:36:14 -0800547 term_set_current_to(terminal);
David Sodman8ef20062015-01-06 09:23:40 -0800548 terminal->active = true;
Dominik Behr83010f82016-03-18 18:43:08 -0700549 fb_setmode(terminal->fb);
David Sodman8ef20062015-01-06 09:23:40 -0800550 term_redraw(terminal);
551}
552
David Sodmanf0a925a2015-05-04 11:19:19 -0700553void term_deactivate(terminal_t* terminal)
554{
555 if (!terminal->active)
556 return;
557
David Sodmanf0a925a2015-05-04 11:19:19 -0700558 terminal->active = false;
David Sodmanf0a925a2015-05-04 11:19:19 -0700559}
560
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800561void term_close(terminal_t* term)
David Sodmanbbcb0522014-09-19 10:34:07 -0700562{
Dominik Behrda6df412016-08-02 12:56:42 -0700563 char path[32];
David Sodmanbbcb0522014-09-19 10:34:07 -0700564 if (!term)
565 return;
566
Dominik Behrda6df412016-08-02 12:56:42 -0700567 snprintf(path, sizeof(path), FRECON_VT_PATH, term->vt);
568 unlink(path);
569
Dominik Behr83010f82016-03-18 18:43:08 -0700570 if (term->fb) {
571 fb_close(term->fb);
572 term->fb = NULL;
David Sodmanbf3f2842014-11-12 08:26:58 -0800573 }
574
David Sodmanbbcb0522014-09-19 10:34:07 -0700575 if (term->term) {
Dominik Behrda6df412016-08-02 12:56:42 -0700576 if (term->term->pty) {
577 if (term->term->pty_bridge >= 0) {
578 shl_pty_bridge_remove(term->term->pty_bridge, term->term->pty);
579 shl_pty_bridge_free(term->term->pty_bridge);
580 term->term->pty_bridge = -1;
581 }
582 shl_pty_close(term->term->pty);
583 term->term->pty = NULL;
584 }
David Sodmanbbcb0522014-09-19 10:34:07 -0700585 free(term->term);
586 term->term = NULL;
587 }
588
Haixia Shi95d680e2015-04-27 20:29:17 -0700589 font_free();
David Sodmanbbcb0522014-09-19 10:34:07 -0700590 free(term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700591}
David Sodmanbf3f2842014-11-12 08:26:58 -0800592
593bool term_is_child_done(terminal_t* terminal)
594{
595 int status;
596 int ret;
597 ret = waitpid(terminal->term->pid, &status, WNOHANG);
598
David Sodmanf0a925a2015-05-04 11:19:19 -0700599 if ((ret == -1) && (errno == ECHILD)) {
600 return false;
601 }
David Sodmanbf3f2842014-11-12 08:26:58 -0800602 return ret != 0;
603}
604
605void term_page_up(terminal_t* terminal)
606{
607 tsm_screen_sb_page_up(terminal->term->screen, 1);
608 term_redraw(terminal);
609}
610
611void term_page_down(terminal_t* terminal)
612{
613 tsm_screen_sb_page_down(terminal->term->screen, 1);
614 term_redraw(terminal);
615}
616
617void term_line_up(terminal_t* terminal)
618{
619 tsm_screen_sb_up(terminal->term->screen, 1);
620 term_redraw(terminal);
621}
622
623void term_line_down(terminal_t* terminal)
624{
625 tsm_screen_sb_down(terminal->term->screen, 1);
626 term_redraw(terminal);
627}
628
629bool term_is_valid(terminal_t* terminal)
630{
631 return ((terminal != NULL) && (terminal->term != NULL));
632}
633
634int term_fd(terminal_t* terminal)
635{
636 if (term_is_valid(terminal))
637 return terminal->term->pty_bridge;
638 else
639 return -1;
640}
641
642void term_dispatch_io(terminal_t* terminal, fd_set* read_set)
643{
644 if (term_is_valid(terminal))
645 if (FD_ISSET(terminal->term->pty_bridge, read_set))
646 shl_pty_bridge_dispatch(terminal->term->pty_bridge, 0);
647}
648
649bool term_exception(terminal_t* terminal, fd_set* exception_set)
650{
651 if (term_is_valid(terminal)) {
652 if (terminal->term->pty_bridge >= 0) {
653 return FD_ISSET(terminal->term->pty_bridge,
654 exception_set);
655 }
656 }
657
658 return false;
659}
660
661bool term_is_active(terminal_t* terminal)
662{
663 if (term_is_valid(terminal))
664 return terminal->active;
665
666 return false;
667}
668
Dominik Behrb1abcba2016-04-14 14:57:21 -0700669void term_add_fds(terminal_t* terminal, fd_set* read_set, fd_set* exception_set, int* maxfd)
David Sodmanbf3f2842014-11-12 08:26:58 -0800670{
671 if (term_is_valid(terminal)) {
672 if (terminal->term->pty_bridge >= 0) {
Dominik Behrd7112672016-01-20 16:59:34 -0800673 *maxfd = MAX(*maxfd, terminal->term->pty_bridge);
David Sodmanbf3f2842014-11-12 08:26:58 -0800674 FD_SET(terminal->term->pty_bridge, read_set);
675 FD_SET(terminal->term->pty_bridge, exception_set);
676 }
677 }
678}
David Sodman8ef20062015-01-06 09:23:40 -0800679
680const char* term_get_ptsname(terminal_t* terminal)
681{
682 return ptsname(shl_pty_get_fd(terminal->term->pty));
683}
David Sodmanf0a925a2015-05-04 11:19:19 -0700684
685void term_set_background(terminal_t* terminal, uint32_t bg)
686{
687 terminal->background = bg;
688 terminal->background_valid = true;
689}
690
691int term_show_image(terminal_t* terminal, image_t* image)
692{
Dominik Behr83010f82016-03-18 18:43:08 -0700693 return image_show(image, terminal->fb);
David Sodmanf0a925a2015-05-04 11:19:19 -0700694}
695
696void term_write_message(terminal_t* terminal, char* message)
697{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800698 FILE* fp;
David Sodmanf0a925a2015-05-04 11:19:19 -0700699
700 fp = fopen(term_get_ptsname(terminal), "w");
701 if (fp) {
702 fputs(message, fp);
703 fclose(fp);
704 }
705}
706
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800707static void term_hide_cursor(terminal_t* terminal)
David Sodmanf0a925a2015-05-04 11:19:19 -0700708{
709 term_write_message(terminal, "\033[?25l");
710}
711
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800712__attribute__ ((unused))
713static void term_show_cursor(terminal_t* terminal)
David Sodmanf0a925a2015-05-04 11:19:19 -0700714{
715 term_write_message(terminal, "\033[?25h");
716}
David Sodman30a94ef2015-07-26 17:37:12 -0700717
Dominik Behr83010f82016-03-18 18:43:08 -0700718fb_t* term_getfb(terminal_t* terminal)
David Sodman30a94ef2015-07-26 17:37:12 -0700719{
Dominik Behr83010f82016-03-18 18:43:08 -0700720 return terminal->fb;
David Sodman30a94ef2015-07-26 17:37:12 -0700721}
Stéphane Marchesin08c08e72016-01-07 16:44:08 -0800722
723terminal_t* term_get_terminal(int num)
724{
725 return terminals[num];
726}
727
728void term_set_terminal(int num, terminal_t* terminal)
729{
730 terminals[num] = terminal;
731}
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800732
Dominik Behrda6df412016-08-02 12:56:42 -0700733int term_create_splash_term(int pts_fd)
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800734{
Dominik Behrda6df412016-08-02 12:56:42 -0700735 terminal_t* terminal = term_init(TERM_SPLASH_TERMINAL, pts_fd);
736
737 if (!terminal) {
738 LOG(ERROR, "Could not create splash term.");
739 return -1;
740 }
741 term_set_terminal(TERM_SPLASH_TERMINAL, terminal);
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800742
743 // Hide the cursor on the splash screen
Dominik Behrda6df412016-08-02 12:56:42 -0700744 term_hide_cursor(terminal);
745 return 0;
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800746}
747
Dominik Behrda6df412016-08-02 12:56:42 -0700748void term_destroy_splash_term(void)
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800749{
Dominik Behrda6df412016-08-02 12:56:42 -0700750 terminal_t *terminal;
751 if (command_flags.enable_vt1) {
752 return;
753 }
754 terminal = term_get_terminal(TERM_SPLASH_TERMINAL);
755 term_set_terminal(TERM_SPLASH_TERMINAL, NULL);
756 term_close(terminal);
Stéphane Marchesin92a297d2016-01-07 20:24:55 -0800757}
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800758
Dominik Behr4defb362016-01-13 12:36:14 -0800759void term_set_current(uint32_t t)
760{
Dominik Behrda6df412016-08-02 12:56:42 -0700761 if (t >= TERM_MAX_TERMINALS)
762 LOG(ERROR, "set_current: larger than array size");
763 else
764 if (t >= term_num_terminals)
765 LOG(ERROR, "set_current: larger than num terminals");
Dominik Behr4defb362016-01-13 12:36:14 -0800766 else
767 current_terminal = t;
768}
769
770uint32_t term_get_current(void)
771{
772 return current_terminal;
773}
774
775terminal_t *term_get_current_terminal(void)
776{
777 return terminals[current_terminal];
778}
779
Dominik Behrb1abcba2016-04-14 14:57:21 -0700780void term_set_current_terminal(terminal_t* terminal)
Dominik Behr4defb362016-01-13 12:36:14 -0800781{
782 terminals[current_terminal] = terminal;
783}
784
785void term_set_current_to(terminal_t* terminal)
786{
787 if (!terminal) {
788 terminals[current_terminal] = NULL;
789 current_terminal = 0;
790 return;
791 }
792
Dominik Behrda6df412016-08-02 12:56:42 -0700793 for (unsigned i = 0; i < term_num_terminals; i++) {
Dominik Behr4defb362016-01-13 12:36:14 -0800794 if (terminal == terminals[i]) {
795 current_terminal = i;
796 return;
797 }
798 }
799 LOG(ERROR, "set_current_to: terminal not in array");
800}
Dominik Behr01a7a582016-01-28 17:02:21 -0800801
Dominik Behrda6df412016-08-02 12:56:42 -0700802int term_switch_to(unsigned int vt)
803{
804 terminal_t *terminal;
805 if (vt == term_get_current()) {
806 terminal = term_get_current_terminal();
Dominik Behrf9329912016-08-25 17:31:52 -0700807 if (term_is_valid(terminal)) {
808 if (!term_is_active(terminal))
809 term_activate(terminal);
810 return vt;
811 }
Dominik Behrda6df412016-08-02 12:56:42 -0700812 }
813
814 if (vt >= term_num_terminals)
815 return -EINVAL;
816
817 terminal = term_get_current_terminal();
818 if (term_is_active(terminal))
819 term_deactivate(terminal);
820
821 if (vt == TERM_SPLASH_TERMINAL
822 && !term_get_terminal(TERM_SPLASH_TERMINAL)
823 && !command_flags.enable_vt1) {
824 term_set_current(vt);
825 /* Splash term is already gone, returning to Chrome. */
826 term_background();
827 return vt;
828 }
829
830 term_foreground();
831
832 term_set_current(vt);
833 terminal = term_get_current_terminal();
834 if (!terminal) {
835 /* No terminal where we are switching to, create new one. */
836 term_set_current_terminal(term_init(vt, -1));
837 terminal = term_get_current_terminal();
Dominik Behrda6df412016-08-02 12:56:42 -0700838 if (!term_is_valid(terminal)) {
Dominik Behrf9329912016-08-25 17:31:52 -0700839 LOG(ERROR, "Term init failed VT%u.", vt);
Dominik Behrda6df412016-08-02 12:56:42 -0700840 return -1;
841 }
Dominik Behrf9329912016-08-25 17:31:52 -0700842 term_activate(terminal);
Dominik Behrda6df412016-08-02 12:56:42 -0700843 } else {
844 term_activate(terminal);
Dominik Behrda6df412016-08-02 12:56:42 -0700845 }
846
847 return vt;
848}
849
Dominik Behr01a7a582016-01-28 17:02:21 -0800850void term_monitor_hotplug(void)
851{
852 unsigned int t;
853
Dominik Behr83864df2016-04-21 12:35:08 -0700854 if (in_background) {
855 hotplug_occured = true;
856 return;
857 }
858
Dominik Behr83010f82016-03-18 18:43:08 -0700859 if (!drm_rescan())
860 return;
861
Dominik Behrda6df412016-08-02 12:56:42 -0700862 for (t = 0; t < term_num_terminals; t++) {
Dominik Behr01a7a582016-01-28 17:02:21 -0800863 if (!terminals[t])
864 continue;
Dominik Behr83010f82016-03-18 18:43:08 -0700865 if (!terminals[t]->fb)
Dominik Behr01a7a582016-01-28 17:02:21 -0800866 continue;
Dominik Behr83010f82016-03-18 18:43:08 -0700867 fb_buffer_destroy(terminals[t]->fb);
Dominik Behra3f65712016-04-25 17:42:14 -0700868 font_free();
Dominik Behr83010f82016-03-18 18:43:08 -0700869 }
870
Dominik Behrda6df412016-08-02 12:56:42 -0700871 for (t = 0; t < term_num_terminals; t++) {
Dominik Behr83010f82016-03-18 18:43:08 -0700872 if (!terminals[t])
873 continue;
874 if (!terminals[t]->fb)
875 continue;
876 fb_buffer_init(terminals[t]->fb);
877 term_resize(terminals[t]);
878 if (current_terminal == t && terminals[t]->active)
879 fb_setmode(terminals[t]->fb);
880 terminals[t]->term->age = 0;
881 term_redraw(terminals[t]);
Dominik Behr01a7a582016-01-28 17:02:21 -0800882 }
883}
Dominik Behrb1abcba2016-04-14 14:57:21 -0700884
885void term_redrm(terminal_t* terminal)
886{
887 fb_buffer_destroy(terminal->fb);
Dominik Behra3f65712016-04-25 17:42:14 -0700888 font_free();
Dominik Behrb1abcba2016-04-14 14:57:21 -0700889 fb_buffer_init(terminal->fb);
890 term_resize(terminal);
891 terminal->term->age = 0;
892 term_redraw(terminal);
893}
894
895void term_clear(terminal_t* terminal)
896{
897 tsm_screen_erase_screen(terminal->term->screen, false);
898 term_redraw(terminal);
899}
Dominik Behr83864df2016-04-21 12:35:08 -0700900
901void term_background(void)
902{
Dominik Behra818a1e2016-10-26 19:46:22 -0700903 int retry = 5;
Dominik Behr83864df2016-04-21 12:35:08 -0700904 if (in_background)
905 return;
906 in_background = true;
Dominik Behrda3c0102016-06-08 15:05:38 -0700907 drm_dropmaster(NULL);
Dominik Behra818a1e2016-10-26 19:46:22 -0700908 while (!dbus_take_display_ownership() && retry--) {
909 LOG(ERROR, "Chrome failed to take display ownership. %s",
910 retry ? "Trying again." : "Giving up, Chrome is probably dead.");
911 usleep(500 * 1000);
912 }
Dominik Behr83864df2016-04-21 12:35:08 -0700913}
914
915void term_foreground(void)
916{
Dominik Behrda3c0102016-06-08 15:05:38 -0700917 int ret;
Dominik Behra818a1e2016-10-26 19:46:22 -0700918 unsigned retry = 5;
919
Dominik Behr83864df2016-04-21 12:35:08 -0700920 if (!in_background)
921 return;
922 in_background = false;
Dominik Behra818a1e2016-10-26 19:46:22 -0700923
924 while (!dbus_release_display_ownership() && retry--) {
925 LOG(ERROR, "Chrome did not release master. %s",
926 retry ? "Trying again." : "Frecon will steal master.");
927 usleep(500 * 1000);
Dominik Behr83864df2016-04-21 12:35:08 -0700928 }
Dominik Behrda3c0102016-06-08 15:05:38 -0700929
930 ret = drm_setmaster(NULL);
Dominik Behrda3c0102016-06-08 15:05:38 -0700931 if (ret < 0)
932 LOG(ERROR, "Could not set master when switching to foreground %m.");
933
Dominik Behr83864df2016-04-21 12:35:08 -0700934 if (hotplug_occured) {
935 hotplug_occured = false;
936 term_monitor_hotplug();
937 }
938}
Dominik Behr1883c042016-04-27 12:31:02 -0700939
940void term_suspend_done(void* ignore)
941{
942 term_monitor_hotplug();
943}