blob: 34e21b211fc3dc71e2bce427b22b1c4761f33555 [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) {
David Sodmanbf3f2842014-11-12 08:26:58 -0800456 term_close(new_terminal);
457 return NULL;
458 }
459
460 new_terminal->term = (struct term*)calloc(1, sizeof(*new_terminal->term));
461 if (!new_terminal->term) {
462 term_close(new_terminal);
463 return NULL;
464 }
465
Dominik Behrda6df412016-08-02 12:56:42 -0700466 if (interactive)
David Sodman8ef20062015-01-06 09:23:40 -0800467 new_terminal->exec = interactive_cmd_line;
468 else
Dominik Behr32680e32016-08-16 12:18:41 -0700469 new_terminal->exec = NULL;
David Sodman8ef20062015-01-06 09:23:40 -0800470
David Sodmanbbcb0522014-09-19 10:34:07 -0700471 status = tsm_screen_new(&new_terminal->term->screen,
472 log_tsm, new_terminal->term);
zhuo-hao471f1e22015-08-12 14:55:56 +0800473 if (status < 0) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700474 term_close(new_terminal);
475 return NULL;
476 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700477
David Sodmanbbcb0522014-09-19 10:34:07 -0700478 tsm_screen_set_max_sb(new_terminal->term->screen, scrollback_size);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700479
David Sodmanbbcb0522014-09-19 10:34:07 -0700480 status = tsm_vte_new(&new_terminal->term->vte, new_terminal->term->screen,
481 term_write_cb, new_terminal->term, log_tsm, new_terminal->term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700482
David Sodmanbbcb0522014-09-19 10:34:07 -0700483 if (status < 0) {
484 term_close(new_terminal);
485 return NULL;
486 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700487
Dominik Behrd2530902016-05-05 14:01:06 -0700488 if (command_flags.enable_gfx)
489 tsm_vte_set_osc_cb(new_terminal->term->vte, term_osc_cb, (void *)new_terminal);
490
David Sodmanbbcb0522014-09-19 10:34:07 -0700491 new_terminal->term->pty_bridge = shl_pty_bridge_new();
492 if (new_terminal->term->pty_bridge < 0) {
493 term_close(new_terminal);
494 return NULL;
495 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700496
David Sodmanbbcb0522014-09-19 10:34:07 -0700497 status = shl_pty_open(&new_terminal->term->pty,
Dominik Behrda6df412016-08-02 12:56:42 -0700498 term_read_cb, new_terminal, 1, 1, pts_fd);
David Sodman8ef20062015-01-06 09:23:40 -0800499
David Sodmanbbcb0522014-09-19 10:34:07 -0700500 if (status < 0) {
501 term_close(new_terminal);
502 return NULL;
503 } else if (status == 0) {
David Sodman8ef20062015-01-06 09:23:40 -0800504 term_run_child(new_terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700505 exit(1);
506 }
507
Dominik Behrda6df412016-08-02 12:56:42 -0700508 status = mkdir(FRECON_RUN_DIR, S_IRWXU);
509 if (status == 0 || (status < 0 && errno == EEXIST)) {
510 char path[32];
511 snprintf(path, sizeof(path), FRECON_VT_PATH, vt);
Dominik Behrb23b05b2016-08-15 16:29:02 -0700512 unlink(path); /* In case it already exists. Ignore return codes. */
Dominik Behrda6df412016-08-02 12:56:42 -0700513 if (symlink(ptsname(shl_pty_get_fd(new_terminal->term->pty)), path) < 0)
514 LOG(ERROR, "Failed to symlink pts name %s to %s, %d:%s",
515 path,
516 ptsname(shl_pty_get_fd(new_terminal->term->pty)),
517 errno, strerror(errno));
518 }
519
David Sodmanbbcb0522014-09-19 10:34:07 -0700520 status = shl_pty_bridge_add(new_terminal->term->pty_bridge, new_terminal->term->pty);
521 if (status) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700522 term_close(new_terminal);
523 return NULL;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700524 }
525
David Sodmanbbcb0522014-09-19 10:34:07 -0700526 new_terminal->term->pid = shl_pty_get_child(new_terminal->term->pty);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700527
Stéphane Marchesin78310662016-01-06 16:09:39 -0800528 status = term_resize(new_terminal);
Dominik Behr83010f82016-03-18 18:43:08 -0700529
David Sodmanbbcb0522014-09-19 10:34:07 -0700530 if (status < 0) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700531 term_close(new_terminal);
532 return NULL;
533 }
David Sodman8ef20062015-01-06 09:23:40 -0800534
David Sodmanbbcb0522014-09-19 10:34:07 -0700535 return new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700536}
537
David Sodman8ef20062015-01-06 09:23:40 -0800538void term_activate(terminal_t* terminal)
539{
Dominik Behr4defb362016-01-13 12:36:14 -0800540 term_set_current_to(terminal);
David Sodman8ef20062015-01-06 09:23:40 -0800541 terminal->active = true;
Dominik Behr83010f82016-03-18 18:43:08 -0700542 fb_setmode(terminal->fb);
David Sodman8ef20062015-01-06 09:23:40 -0800543 term_redraw(terminal);
544}
545
David Sodmanf0a925a2015-05-04 11:19:19 -0700546void term_deactivate(terminal_t* terminal)
547{
548 if (!terminal->active)
549 return;
550
David Sodmanf0a925a2015-05-04 11:19:19 -0700551 terminal->active = false;
David Sodmanf0a925a2015-05-04 11:19:19 -0700552}
553
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800554void term_close(terminal_t* term)
David Sodmanbbcb0522014-09-19 10:34:07 -0700555{
Dominik Behrda6df412016-08-02 12:56:42 -0700556 char path[32];
David Sodmanbbcb0522014-09-19 10:34:07 -0700557 if (!term)
558 return;
559
Dominik Behrda6df412016-08-02 12:56:42 -0700560 snprintf(path, sizeof(path), FRECON_VT_PATH, term->vt);
561 unlink(path);
562
Dominik Behr83010f82016-03-18 18:43:08 -0700563 if (term->fb) {
564 fb_close(term->fb);
565 term->fb = NULL;
David Sodmanbf3f2842014-11-12 08:26:58 -0800566 }
567
David Sodmanbbcb0522014-09-19 10:34:07 -0700568 if (term->term) {
Dominik Behrda6df412016-08-02 12:56:42 -0700569 if (term->term->pty) {
570 if (term->term->pty_bridge >= 0) {
571 shl_pty_bridge_remove(term->term->pty_bridge, term->term->pty);
572 shl_pty_bridge_free(term->term->pty_bridge);
573 term->term->pty_bridge = -1;
574 }
575 shl_pty_close(term->term->pty);
576 term->term->pty = NULL;
577 }
David Sodmanbbcb0522014-09-19 10:34:07 -0700578 free(term->term);
579 term->term = NULL;
580 }
581
Haixia Shi95d680e2015-04-27 20:29:17 -0700582 font_free();
David Sodmanbbcb0522014-09-19 10:34:07 -0700583 free(term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700584}
David Sodmanbf3f2842014-11-12 08:26:58 -0800585
586bool term_is_child_done(terminal_t* terminal)
587{
588 int status;
589 int ret;
590 ret = waitpid(terminal->term->pid, &status, WNOHANG);
591
David Sodmanf0a925a2015-05-04 11:19:19 -0700592 if ((ret == -1) && (errno == ECHILD)) {
593 return false;
594 }
David Sodmanbf3f2842014-11-12 08:26:58 -0800595 return ret != 0;
596}
597
598void term_page_up(terminal_t* terminal)
599{
600 tsm_screen_sb_page_up(terminal->term->screen, 1);
601 term_redraw(terminal);
602}
603
604void term_page_down(terminal_t* terminal)
605{
606 tsm_screen_sb_page_down(terminal->term->screen, 1);
607 term_redraw(terminal);
608}
609
610void term_line_up(terminal_t* terminal)
611{
612 tsm_screen_sb_up(terminal->term->screen, 1);
613 term_redraw(terminal);
614}
615
616void term_line_down(terminal_t* terminal)
617{
618 tsm_screen_sb_down(terminal->term->screen, 1);
619 term_redraw(terminal);
620}
621
622bool term_is_valid(terminal_t* terminal)
623{
624 return ((terminal != NULL) && (terminal->term != NULL));
625}
626
627int term_fd(terminal_t* terminal)
628{
629 if (term_is_valid(terminal))
630 return terminal->term->pty_bridge;
631 else
632 return -1;
633}
634
635void term_dispatch_io(terminal_t* terminal, fd_set* read_set)
636{
637 if (term_is_valid(terminal))
638 if (FD_ISSET(terminal->term->pty_bridge, read_set))
639 shl_pty_bridge_dispatch(terminal->term->pty_bridge, 0);
640}
641
642bool term_exception(terminal_t* terminal, fd_set* exception_set)
643{
644 if (term_is_valid(terminal)) {
645 if (terminal->term->pty_bridge >= 0) {
646 return FD_ISSET(terminal->term->pty_bridge,
647 exception_set);
648 }
649 }
650
651 return false;
652}
653
654bool term_is_active(terminal_t* terminal)
655{
656 if (term_is_valid(terminal))
657 return terminal->active;
658
659 return false;
660}
661
Dominik Behrb1abcba2016-04-14 14:57:21 -0700662void term_add_fds(terminal_t* terminal, fd_set* read_set, fd_set* exception_set, int* maxfd)
David Sodmanbf3f2842014-11-12 08:26:58 -0800663{
664 if (term_is_valid(terminal)) {
665 if (terminal->term->pty_bridge >= 0) {
Dominik Behrd7112672016-01-20 16:59:34 -0800666 *maxfd = MAX(*maxfd, terminal->term->pty_bridge);
David Sodmanbf3f2842014-11-12 08:26:58 -0800667 FD_SET(terminal->term->pty_bridge, read_set);
668 FD_SET(terminal->term->pty_bridge, exception_set);
669 }
670 }
671}
David Sodman8ef20062015-01-06 09:23:40 -0800672
673const char* term_get_ptsname(terminal_t* terminal)
674{
675 return ptsname(shl_pty_get_fd(terminal->term->pty));
676}
David Sodmanf0a925a2015-05-04 11:19:19 -0700677
678void term_set_background(terminal_t* terminal, uint32_t bg)
679{
680 terminal->background = bg;
681 terminal->background_valid = true;
682}
683
684int term_show_image(terminal_t* terminal, image_t* image)
685{
Dominik Behr83010f82016-03-18 18:43:08 -0700686 return image_show(image, terminal->fb);
David Sodmanf0a925a2015-05-04 11:19:19 -0700687}
688
689void term_write_message(terminal_t* terminal, char* message)
690{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800691 FILE* fp;
David Sodmanf0a925a2015-05-04 11:19:19 -0700692
693 fp = fopen(term_get_ptsname(terminal), "w");
694 if (fp) {
695 fputs(message, fp);
696 fclose(fp);
697 }
698}
699
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800700static void term_hide_cursor(terminal_t* terminal)
David Sodmanf0a925a2015-05-04 11:19:19 -0700701{
702 term_write_message(terminal, "\033[?25l");
703}
704
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800705__attribute__ ((unused))
706static void term_show_cursor(terminal_t* terminal)
David Sodmanf0a925a2015-05-04 11:19:19 -0700707{
708 term_write_message(terminal, "\033[?25h");
709}
David Sodman30a94ef2015-07-26 17:37:12 -0700710
Dominik Behr83010f82016-03-18 18:43:08 -0700711fb_t* term_getfb(terminal_t* terminal)
David Sodman30a94ef2015-07-26 17:37:12 -0700712{
Dominik Behr83010f82016-03-18 18:43:08 -0700713 return terminal->fb;
David Sodman30a94ef2015-07-26 17:37:12 -0700714}
Stéphane Marchesin08c08e72016-01-07 16:44:08 -0800715
716terminal_t* term_get_terminal(int num)
717{
718 return terminals[num];
719}
720
721void term_set_terminal(int num, terminal_t* terminal)
722{
723 terminals[num] = terminal;
724}
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800725
Dominik Behrda6df412016-08-02 12:56:42 -0700726int term_create_splash_term(int pts_fd)
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800727{
Dominik Behrda6df412016-08-02 12:56:42 -0700728 terminal_t* terminal = term_init(TERM_SPLASH_TERMINAL, pts_fd);
729
730 if (!terminal) {
731 LOG(ERROR, "Could not create splash term.");
732 return -1;
733 }
734 term_set_terminal(TERM_SPLASH_TERMINAL, terminal);
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800735
736 // Hide the cursor on the splash screen
Dominik Behrda6df412016-08-02 12:56:42 -0700737 term_hide_cursor(terminal);
738 return 0;
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800739}
740
Dominik Behrda6df412016-08-02 12:56:42 -0700741void term_destroy_splash_term(void)
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800742{
Dominik Behrda6df412016-08-02 12:56:42 -0700743 terminal_t *terminal;
744 if (command_flags.enable_vt1) {
745 return;
746 }
747 terminal = term_get_terminal(TERM_SPLASH_TERMINAL);
748 term_set_terminal(TERM_SPLASH_TERMINAL, NULL);
749 term_close(terminal);
Stéphane Marchesin92a297d2016-01-07 20:24:55 -0800750}
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800751
Dominik Behr4defb362016-01-13 12:36:14 -0800752void term_set_current(uint32_t t)
753{
Dominik Behrda6df412016-08-02 12:56:42 -0700754 if (t >= TERM_MAX_TERMINALS)
755 LOG(ERROR, "set_current: larger than array size");
756 else
757 if (t >= term_num_terminals)
758 LOG(ERROR, "set_current: larger than num terminals");
Dominik Behr4defb362016-01-13 12:36:14 -0800759 else
760 current_terminal = t;
761}
762
763uint32_t term_get_current(void)
764{
765 return current_terminal;
766}
767
768terminal_t *term_get_current_terminal(void)
769{
770 return terminals[current_terminal];
771}
772
Dominik Behrb1abcba2016-04-14 14:57:21 -0700773void term_set_current_terminal(terminal_t* terminal)
Dominik Behr4defb362016-01-13 12:36:14 -0800774{
775 terminals[current_terminal] = terminal;
776}
777
778void term_set_current_to(terminal_t* terminal)
779{
780 if (!terminal) {
781 terminals[current_terminal] = NULL;
782 current_terminal = 0;
783 return;
784 }
785
Dominik Behrda6df412016-08-02 12:56:42 -0700786 for (unsigned i = 0; i < term_num_terminals; i++) {
Dominik Behr4defb362016-01-13 12:36:14 -0800787 if (terminal == terminals[i]) {
788 current_terminal = i;
789 return;
790 }
791 }
792 LOG(ERROR, "set_current_to: terminal not in array");
793}
Dominik Behr01a7a582016-01-28 17:02:21 -0800794
Dominik Behrda6df412016-08-02 12:56:42 -0700795int term_switch_to(unsigned int vt)
796{
797 terminal_t *terminal;
798 if (vt == term_get_current()) {
799 terminal = term_get_current_terminal();
800 if (!term_is_active(terminal))
801 term_activate(terminal);
802 return vt;
803 }
804
805 if (vt >= term_num_terminals)
806 return -EINVAL;
807
808 terminal = term_get_current_terminal();
809 if (term_is_active(terminal))
810 term_deactivate(terminal);
811
812 if (vt == TERM_SPLASH_TERMINAL
813 && !term_get_terminal(TERM_SPLASH_TERMINAL)
814 && !command_flags.enable_vt1) {
815 term_set_current(vt);
816 /* Splash term is already gone, returning to Chrome. */
817 term_background();
818 return vt;
819 }
820
821 term_foreground();
822
823 term_set_current(vt);
824 terminal = term_get_current_terminal();
825 if (!terminal) {
826 /* No terminal where we are switching to, create new one. */
827 term_set_current_terminal(term_init(vt, -1));
828 terminal = term_get_current_terminal();
829 term_activate(terminal);
830 if (!term_is_valid(terminal)) {
831 LOG(ERROR, "Term init failed");
832 return -1;
833 }
834 } else {
835 term_activate(terminal);
836 LOG(INFO, "Activated existing terminal %p on VT%u", terminal, vt);
837 }
838
839 return vt;
840}
841
Dominik Behr01a7a582016-01-28 17:02:21 -0800842void term_monitor_hotplug(void)
843{
844 unsigned int t;
845
Dominik Behr83864df2016-04-21 12:35:08 -0700846 if (in_background) {
847 hotplug_occured = true;
848 return;
849 }
850
Dominik Behr83010f82016-03-18 18:43:08 -0700851 if (!drm_rescan())
852 return;
853
Dominik Behrda6df412016-08-02 12:56:42 -0700854 for (t = 0; t < term_num_terminals; t++) {
Dominik Behr01a7a582016-01-28 17:02:21 -0800855 if (!terminals[t])
856 continue;
Dominik Behr83010f82016-03-18 18:43:08 -0700857 if (!terminals[t]->fb)
Dominik Behr01a7a582016-01-28 17:02:21 -0800858 continue;
Dominik Behr83010f82016-03-18 18:43:08 -0700859 fb_buffer_destroy(terminals[t]->fb);
Dominik Behra3f65712016-04-25 17:42:14 -0700860 font_free();
Dominik Behr83010f82016-03-18 18:43:08 -0700861 }
862
Dominik Behrda6df412016-08-02 12:56:42 -0700863 for (t = 0; t < term_num_terminals; t++) {
Dominik Behr83010f82016-03-18 18:43:08 -0700864 if (!terminals[t])
865 continue;
866 if (!terminals[t]->fb)
867 continue;
868 fb_buffer_init(terminals[t]->fb);
869 term_resize(terminals[t]);
870 if (current_terminal == t && terminals[t]->active)
871 fb_setmode(terminals[t]->fb);
872 terminals[t]->term->age = 0;
873 term_redraw(terminals[t]);
Dominik Behr01a7a582016-01-28 17:02:21 -0800874 }
875}
Dominik Behrb1abcba2016-04-14 14:57:21 -0700876
877void term_redrm(terminal_t* terminal)
878{
879 fb_buffer_destroy(terminal->fb);
Dominik Behra3f65712016-04-25 17:42:14 -0700880 font_free();
Dominik Behrb1abcba2016-04-14 14:57:21 -0700881 fb_buffer_init(terminal->fb);
882 term_resize(terminal);
883 terminal->term->age = 0;
884 term_redraw(terminal);
885}
886
887void term_clear(terminal_t* terminal)
888{
889 tsm_screen_erase_screen(terminal->term->screen, false);
890 term_redraw(terminal);
891}
Dominik Behr83864df2016-04-21 12:35:08 -0700892
893void term_background(void)
894{
895 if (in_background)
896 return;
897 in_background = true;
Dominik Behrda3c0102016-06-08 15:05:38 -0700898 drm_dropmaster(NULL);
Dominik Behr83864df2016-04-21 12:35:08 -0700899 dbus_take_display_ownership();
900}
901
902void term_foreground(void)
903{
Dominik Behrda3c0102016-06-08 15:05:38 -0700904 int ret;
Dominik Behr83864df2016-04-21 12:35:08 -0700905 if (!in_background)
906 return;
907 in_background = false;
908 if (!dbus_release_display_ownership()) {
909 LOG(ERROR, "Chrome did not release master. Frecon will try to steal it.");
910 set_drm_master_relax();
911 }
Dominik Behrda3c0102016-06-08 15:05:38 -0700912
913 ret = drm_setmaster(NULL);
914 if (ret < 0) {
915 /*
916 * In case there is high system load give Chrome some time
917 * and try again. */
918 usleep(500 * 1000);
919 ret = drm_setmaster(NULL);
920 }
921 if (ret < 0)
922 LOG(ERROR, "Could not set master when switching to foreground %m.");
923
Dominik Behr83864df2016-04-21 12:35:08 -0700924 if (hotplug_occured) {
925 hotplug_occured = false;
926 term_monitor_hotplug();
927 }
928}
Dominik Behr1883c042016-04-27 12:31:02 -0700929
930void term_suspend_done(void* ignore)
931{
932 term_monitor_hotplug();
933}