blob: d0e73e273d37022f26483b10bdeb700c3d341286 [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;
Dominik Behrda55e6c2017-02-28 18:12:10 -080047 bool input_enable;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080048 uint32_t background;
49 bool background_valid;
Dominik Behr83010f82016-03-18 18:43:08 -070050 fb_t* fb;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080051 struct term* term;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080052 char** exec;
David Sodmanf0a925a2015-05-04 11:19:19 -070053};
54
David Sodman8ef20062015-01-06 09:23:40 -080055
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080056static char* interactive_cmd_line[] = {
David Sodman8ef20062015-01-06 09:23:40 -080057 "/sbin/agetty",
58 "-",
59 "9600",
60 "xterm",
61 NULL
62};
63
Dominik Behr83864df2016-04-21 12:35:08 -070064static bool in_background = false;
65static bool hotplug_occured = false;
66
David Sodman8ef20062015-01-06 09:23:40 -080067
68static void __attribute__ ((noreturn)) term_run_child(terminal_t* terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070069{
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070070 /* XXX figure out how to fix "top" for xterm-256color */
71 setenv("TERM", "xterm", 1);
Dominik Behr32680e32016-08-16 12:18:41 -070072 if (terminal->exec) {
73 execve(terminal->exec[0], terminal->exec, environ);
74 exit(1);
75 } else {
76 while (1)
77 sleep(1000000);
78 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070079}
80
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080081static int term_draw_cell(struct tsm_screen* screen, uint32_t id,
Stéphane Marchesin8fc13522015-12-14 17:02:28 -080082 const uint32_t* ch, size_t len,
83 unsigned int cwidth, unsigned int posx,
84 unsigned int posy,
85 const struct tsm_screen_attr* attr,
86 tsm_age_t age, void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070087{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080088 terminal_t* terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070089 uint32_t front_color, back_color;
David Sodmanf0a925a2015-05-04 11:19:19 -070090 uint8_t br, bb, bg;
Stéphane Marchesinedece332015-12-14 15:10:58 -080091 uint32_t luminance;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070092
David Sodmanbbcb0522014-09-19 10:34:07 -070093 if (age && terminal->term->age && age <= terminal->term->age)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070094 return 0;
95
David Sodmanf0a925a2015-05-04 11:19:19 -070096 if (terminal->background_valid) {
97 br = (terminal->background >> 16) & 0xFF;
98 bg = (terminal->background >> 8) & 0xFF;
99 bb = (terminal->background) & 0xFF;
Stéphane Marchesinedece332015-12-14 15:10:58 -0800100 luminance = (3 * br + bb + 4 * bg) >> 3;
David Sodmanf0a925a2015-05-04 11:19:19 -0700101
Stéphane Marchesinedece332015-12-14 15:10:58 -0800102 /*
103 * FIXME: black is chosen on a dark background, but it uses the
104 * default color for light backgrounds
105 */
106 if (luminance > 128) {
David Sodmanf0a925a2015-05-04 11:19:19 -0700107 front_color = 0;
108 back_color = terminal->background;
109 } else {
110 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
111 back_color = terminal->background;
112 }
113 } else {
114 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
115 back_color = (attr->br << 16) | (attr->bg << 8) | attr->bb;
116 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700117
118 if (attr->inverse) {
119 uint32_t tmp = front_color;
120 front_color = back_color;
121 back_color = tmp;
122 }
123
124 if (len)
David Sodmanbbcb0522014-09-19 10:34:07 -0700125 font_render(terminal->term->dst_image, posx, posy, terminal->term->pitch, *ch,
126 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700127 else
David Sodmanbbcb0522014-09-19 10:34:07 -0700128 font_fillchar(terminal->term->dst_image, posx, posy, terminal->term->pitch,
129 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700130
131 return 0;
132}
133
Stéphane Marchesine2a46cd2016-01-07 16:45:24 -0800134static void term_redraw(terminal_t* terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700135{
Dominik Behr83010f82016-03-18 18:43:08 -0700136 uint32_t* fb_buffer;
137 fb_buffer = fb_lock(terminal->fb);
138 if (fb_buffer != NULL) {
139 terminal->term->dst_image = fb_buffer;
David Sodmanbbcb0522014-09-19 10:34:07 -0700140 terminal->term->age =
141 tsm_screen_draw(terminal->term->screen, term_draw_cell, terminal);
Dominik Behr83010f82016-03-18 18:43:08 -0700142 fb_unlock(terminal->fb);
David Sodmanbbcb0522014-09-19 10:34:07 -0700143 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700144}
145
David Sodmanbbcb0522014-09-19 10:34:07 -0700146void term_key_event(terminal_t* terminal, uint32_t keysym, int32_t unicode)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700147{
Dominik Behrda55e6c2017-02-28 18:12:10 -0800148 if (!terminal->input_enable)
149 return;
150
David Sodmanbbcb0522014-09-19 10:34:07 -0700151 if (tsm_vte_handle_keyboard(terminal->term->vte, keysym, 0, 0, unicode))
152 tsm_screen_sb_reset(terminal->term->screen);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700153
David Sodmanbbcb0522014-09-19 10:34:07 -0700154 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700155}
156
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800157static void term_read_cb(struct shl_pty* pty, char* u8, size_t len, void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700158{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800159 terminal_t* terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700160
David Sodmanbbcb0522014-09-19 10:34:07 -0700161 tsm_vte_input(terminal->term->vte, u8, len);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700162
David Sodmanbbcb0522014-09-19 10:34:07 -0700163 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700164}
165
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800166static void term_write_cb(struct tsm_vte* vte, const char* u8, size_t len,
167 void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700168{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800169 struct term* term = data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700170 int r;
171
172 r = shl_pty_write(term->pty, u8, len);
173 if (r < 0)
David Sodmanbbcb0522014-09-19 10:34:07 -0700174 LOG(ERROR, "OOM in pty-write (%d)", r);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700175
176 shl_pty_dispatch(term->pty);
177}
178
Dominik Behrd2530902016-05-05 14:01:06 -0700179static void term_esc_show_image(terminal_t* terminal, char* params)
180{
181 char* tok;
182 image_t* image;
183 int status;
184
185 image = image_create();
186 if (!image) {
187 LOG(ERROR, "Out of memory when creating an image.\n");
188 return;
189 }
190 for (tok = strtok(params, ";"); tok; tok = strtok(NULL, ";")) {
191 if (strncmp("file=", tok, 5) == 0) {
192 image_set_filename(image, tok + 5);
193 } else if (strncmp("location=", tok, 9) == 0) {
194 uint32_t x, y;
195 if (sscanf(tok + 9, "%u,%u", &x, &y) != 2) {
196 LOG(ERROR, "Error parsing image location.\n");
197 goto done;
198 }
199 image_set_location(image, x, y);
200 } else if (strncmp("offset=", tok, 7) == 0) {
201 int32_t x, y;
202 if (sscanf(tok + 7, "%d,%d", &x, &y) != 2) {
203 LOG(ERROR, "Error parsing image offset.\n");
204 goto done;
205 }
206 image_set_offset(image, x, y);
207 } else if (strncmp("scale=", tok, 6) == 0) {
208 uint32_t s;
209 if (sscanf(tok + 6, "%u", &s) != 1) {
210 LOG(ERROR, "Error parsing image scale.\n");
211 goto done;
212 }
213 if (s == 0)
214 s = image_get_auto_scale(term_getfb(terminal));
215 image_set_scale(image, s);
216 }
217 }
218
219 status = image_load_image_from_file(image);
220 if (status != 0) {
221 LOG(WARNING, "Term ESC image_load_image_from_file %s failed: %d:%s.",
222 image_get_filename(image), status, strerror(status));
223 } else {
224 term_show_image(terminal, image);
225 }
226done:
227 image_destroy(image);
228}
229
230static void term_esc_draw_box(terminal_t* terminal, char* params)
231{
232 char* tok;
233 uint32_t color = 0;
234 uint32_t w = 1;
235 uint32_t h = 1;
236 uint32_t locx, locy;
237 bool use_location = false;
238 int32_t offx, offy;
239 bool use_offset = false;
240 uint32_t scale = 1;
241 uint32_t* buffer;
242 int32_t startx, starty;
243 uint32_t pitch4;
244
245 for (tok = strtok(params, ";"); tok; tok = strtok(NULL, ";")) {
246 if (strncmp("color=", tok, 6) == 0) {
247 color = strtoul(tok + 6, NULL, 0);
248 } else if (strncmp("size=", tok, 5) == 0) {
249 if (sscanf(tok + 5, "%u,%u", &w, &h) != 2) {
250 LOG(ERROR, "Error parsing box size.\n");
251 goto done;
252 }
253 } else if (strncmp("location=", tok, 9) == 0) {
254 if (sscanf(tok + 9, "%u,%u", &locx, &locy) != 2) {
255 LOG(ERROR, "Error parsing box location.\n");
256 goto done;
257 }
258 use_location = true;
259 } else if (strncmp("offset=", tok, 7) == 0) {
260 if (sscanf(tok + 7, "%d,%d", &offx, &offy) != 2) {
261 LOG(ERROR, "Error parsing box offset.\n");
262 goto done;
263 }
264 use_offset = true;
265 } else if (strncmp("scale=", tok, 6) == 0) {
266 if (sscanf(tok + 6, "%u", &scale) != 1) {
267 LOG(ERROR, "Error parsing box scale.\n");
268 goto done;
269 }
270 if (scale == 0)
271 scale = image_get_auto_scale(term_getfb(terminal));
272 }
273 }
274
275 w *= scale;
276 h *= scale;
277 offx *= scale;
278 offy *= scale;
279
280 buffer = fb_lock(terminal->fb);
281 if (buffer == NULL)
282 goto done;
283
284 if (use_offset && use_location) {
285 LOG(WARNING, "Box offset and location set, using location.");
286 use_offset = false;
287 }
288
289 if (use_location) {
290 startx = locx;
291 starty = locy;
292 } else {
293 startx = (fb_getwidth(terminal->fb) - (int32_t)w)/2;
294 starty = (fb_getheight(terminal->fb) - (int32_t)h)/2;
295 }
296
297 if (use_offset) {
298 startx += offx;
299 starty += offy;
300 }
301
302 pitch4 = fb_getpitch(terminal->fb) / 4;
303
304 /* Completely outside buffer, do nothing */
305 if (startx + w <= 0 || startx >= fb_getwidth(terminal->fb))
306 goto done_fb;
307 if (starty + h <= 0 || starty >= fb_getheight(terminal->fb))
308 goto done_fb;
309 /* Make sure we are inside buffer. */
310 if (startx < 0)
311 startx = 0;
312 if (startx + (int32_t)w > fb_getwidth(terminal->fb))
313 w = fb_getwidth(terminal->fb) - startx;
314 if (starty < 0)
315 starty = 0;
316 if (starty + (int32_t)h > fb_getheight(terminal->fb))
317 h = fb_getheight(terminal->fb) - starty;
318
319 for (uint32_t y = 0; y < h; y++) {
320 uint32_t *o = buffer + (starty + y) * pitch4
321 + startx;
322 for (uint32_t x = 0; x < w; x++)
323 o[x] = color;
324 }
325done_fb:
326 fb_unlock(terminal->fb);
327done:
328 ;
329}
330
Dominik Behrda55e6c2017-02-28 18:12:10 -0800331static void term_esc_input(terminal_t* terminal, char* params)
332{
333 if (strcmp(params, "1") == 0 ||
334 strcasecmp(params, "on") == 0 ||
335 strcasecmp(params, "true") == 0)
336 term_input_enable(terminal, true);
337 else if (strcmp(params, "0") == 0 ||
338 strcasecmp(params, "off") == 0 ||
339 strcasecmp(params, "false") == 0)
340 term_input_enable(terminal, false);
341 else
342 LOG(ERROR, "Invalid parameter for input escape.\n");
343}
344
Dominik Behrd2530902016-05-05 14:01:06 -0700345static void term_osc_cb(struct tsm_vte *vte, const uint32_t *osc_string,
346 size_t osc_len, void *data)
347{
348 terminal_t* terminal = (terminal_t*)data;
349 size_t i;
350 char *osc;
351
352 for (i = 0; i < osc_len; i++)
353 if (osc_string[i] >= 128)
354 return; /* we only want to deal with ASCII */
355
356 osc = malloc(osc_len + 1);
357 if (!osc) {
358 LOG(WARNING, "Out of memory when processing OSC\n");
359 return;
360 }
361
362 for (i = 0; i < osc_len; i++)
363 osc[i] = (char)osc_string[i];
364 osc[i] = '\0';
365
366 if (strncmp(osc, "image:", 6) == 0)
367 term_esc_show_image(terminal, osc + 6);
368 else if (strncmp(osc, "box:", 4) == 0)
369 term_esc_draw_box(terminal, osc + 4);
Dominik Behrda55e6c2017-02-28 18:12:10 -0800370 else if (strncmp(osc, "input:", 6) == 0)
371 term_esc_input(terminal, osc + 6);
Dominik Behrd2530902016-05-05 14:01:06 -0700372 else
373 LOG(WARNING, "Unknown OSC escape sequence \"%s\", ignoring.", osc);
374
375 free(osc);
376}
377
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800378static const char* sev2str_table[] = {
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700379 "FATAL",
380 "ALERT",
381 "CRITICAL",
382 "ERROR",
383 "WARNING",
384 "NOTICE",
385 "INFO",
386 "DEBUG"
387};
388
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800389static const char* sev2str(unsigned int sev)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700390{
391 if (sev > 7)
392 return "DEBUG";
393
394 return sev2str_table[sev];
395}
396
Dominik Behr00003502014-08-15 16:42:37 -0700397#ifdef __clang__
398__attribute__((__format__ (__printf__, 7, 0)))
399#endif
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800400static void log_tsm(void* data, const char* file, int line, const char* fn,
Stéphane Marchesin8fc13522015-12-14 17:02:28 -0800401 const char* subs, unsigned int sev, const char* format,
402 va_list args)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700403{
404 fprintf(stderr, "%s: %s: ", sev2str(sev), subs);
405 vfprintf(stderr, format, args);
406 fprintf(stderr, "\n");
407}
408
Haixia Shi95285872016-11-08 15:26:35 -0800409static int term_resize(terminal_t* term, int scaling)
Stéphane Marchesin78310662016-01-06 16:09:39 -0800410{
411 uint32_t char_width, char_height;
412 int status;
413
Haixia Shi95285872016-11-08 15:26:35 -0800414 if (!scaling)
415 scaling = fb_getscaling(term->fb);
416
417 font_init(scaling);
Stéphane Marchesin78310662016-01-06 16:09:39 -0800418 font_get_size(&char_width, &char_height);
419
Dominik Behr83010f82016-03-18 18:43:08 -0700420 term->term->char_x = fb_getwidth(term->fb) / char_width;
421 term->term->char_y = fb_getheight(term->fb) / char_height;
422 term->term->pitch = fb_getpitch(term->fb);
Stéphane Marchesin78310662016-01-06 16:09:39 -0800423
424 status = tsm_screen_resize(term->term->screen,
425 term->term->char_x, term->term->char_y);
426 if (status < 0) {
427 font_free();
428 return -1;
429 }
430
431 status = shl_pty_resize(term->term->pty, term->term->char_x,
432 term->term->char_y);
433 if (status < 0) {
434 font_free();
435 return -1;
436 }
437
438 return 0;
439}
440
Dominik Behrda6df412016-08-02 12:56:42 -0700441void term_set_num_terminals(unsigned new_num)
442{
443 if (new_num < 1)
444 term_num_terminals = 1;
445 else if (new_num > TERM_MAX_TERMINALS)
446 term_num_terminals = TERM_MAX_TERMINALS;
447 else
448 term_num_terminals = new_num;
449}
450
451static bool term_is_interactive(unsigned int vt)
452{
453 if (command_flags.no_login)
454 return false;
455
456 if (vt == TERM_SPLASH_TERMINAL)
457 return command_flags.enable_vt1;
458
459 return true;
460}
461
462terminal_t* term_init(unsigned vt, int pts_fd)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700463{
464 const int scrollback_size = 200;
David Sodmanbbcb0522014-09-19 10:34:07 -0700465 int status;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800466 terminal_t* new_terminal;
Dominik Behrda6df412016-08-02 12:56:42 -0700467 bool interactive = term_is_interactive(vt);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700468
David Sodmanbbcb0522014-09-19 10:34:07 -0700469 new_terminal = (terminal_t*)calloc(1, sizeof(*new_terminal));
David Sodmanbf3f2842014-11-12 08:26:58 -0800470 if (!new_terminal)
471 return NULL;
David Sodmanbbcb0522014-09-19 10:34:07 -0700472
Dominik Behrda6df412016-08-02 12:56:42 -0700473 new_terminal->vt = vt;
David Sodmanf0a925a2015-05-04 11:19:19 -0700474 new_terminal->background_valid = false;
Dominik Behrda55e6c2017-02-28 18:12:10 -0800475 new_terminal->input_enable = true;
David Sodmanf0a925a2015-05-04 11:19:19 -0700476
Dominik Behr83010f82016-03-18 18:43:08 -0700477 new_terminal->fb = fb_init();
David Sodmanf0a925a2015-05-04 11:19:19 -0700478
Dominik Behr83010f82016-03-18 18:43:08 -0700479 if (!new_terminal->fb) {
Dominik Behrf9329912016-08-25 17:31:52 -0700480 LOG(ERROR, "Failed to create fb on VT%u.", vt);
David Sodmanbf3f2842014-11-12 08:26:58 -0800481 term_close(new_terminal);
482 return NULL;
483 }
484
485 new_terminal->term = (struct term*)calloc(1, sizeof(*new_terminal->term));
486 if (!new_terminal->term) {
487 term_close(new_terminal);
488 return NULL;
489 }
490
Dominik Behrda6df412016-08-02 12:56:42 -0700491 if (interactive)
David Sodman8ef20062015-01-06 09:23:40 -0800492 new_terminal->exec = interactive_cmd_line;
493 else
Dominik Behr32680e32016-08-16 12:18:41 -0700494 new_terminal->exec = NULL;
David Sodman8ef20062015-01-06 09:23:40 -0800495
David Sodmanbbcb0522014-09-19 10:34:07 -0700496 status = tsm_screen_new(&new_terminal->term->screen,
497 log_tsm, new_terminal->term);
zhuo-hao471f1e22015-08-12 14:55:56 +0800498 if (status < 0) {
Dominik Behrf9329912016-08-25 17:31:52 -0700499 LOG(ERROR, "Failed to create new screen on VT%u.", vt);
David Sodmanbbcb0522014-09-19 10:34:07 -0700500 term_close(new_terminal);
501 return NULL;
502 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700503
David Sodmanbbcb0522014-09-19 10:34:07 -0700504 tsm_screen_set_max_sb(new_terminal->term->screen, scrollback_size);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700505
David Sodmanbbcb0522014-09-19 10:34:07 -0700506 status = tsm_vte_new(&new_terminal->term->vte, new_terminal->term->screen,
507 term_write_cb, new_terminal->term, log_tsm, new_terminal->term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700508
David Sodmanbbcb0522014-09-19 10:34:07 -0700509 if (status < 0) {
Dominik Behrf9329912016-08-25 17:31:52 -0700510 LOG(ERROR, "Failed to create new VT%u.", vt);
David Sodmanbbcb0522014-09-19 10:34:07 -0700511 term_close(new_terminal);
512 return NULL;
513 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700514
Dominik Behrda55e6c2017-02-28 18:12:10 -0800515 if (command_flags.enable_osc)
Dominik Behrd2530902016-05-05 14:01:06 -0700516 tsm_vte_set_osc_cb(new_terminal->term->vte, term_osc_cb, (void *)new_terminal);
517
David Sodmanbbcb0522014-09-19 10:34:07 -0700518 new_terminal->term->pty_bridge = shl_pty_bridge_new();
519 if (new_terminal->term->pty_bridge < 0) {
Dominik Behrf9329912016-08-25 17:31:52 -0700520 LOG(ERROR, "Failed to create pty bridge on VT%u.", vt);
David Sodmanbbcb0522014-09-19 10:34:07 -0700521 term_close(new_terminal);
522 return NULL;
523 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700524
David Sodmanbbcb0522014-09-19 10:34:07 -0700525 status = shl_pty_open(&new_terminal->term->pty,
Dominik Behrda6df412016-08-02 12:56:42 -0700526 term_read_cb, new_terminal, 1, 1, pts_fd);
David Sodman8ef20062015-01-06 09:23:40 -0800527
David Sodmanbbcb0522014-09-19 10:34:07 -0700528 if (status < 0) {
Dominik Behrf9329912016-08-25 17:31:52 -0700529 LOG(ERROR, "Failed to open pty on VT%u.", vt);
David Sodmanbbcb0522014-09-19 10:34:07 -0700530 term_close(new_terminal);
531 return NULL;
532 } else if (status == 0) {
David Sodman8ef20062015-01-06 09:23:40 -0800533 term_run_child(new_terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700534 exit(1);
535 }
536
Dominik Behrda6df412016-08-02 12:56:42 -0700537 status = mkdir(FRECON_RUN_DIR, S_IRWXU);
538 if (status == 0 || (status < 0 && errno == EEXIST)) {
539 char path[32];
540 snprintf(path, sizeof(path), FRECON_VT_PATH, vt);
Dominik Behrb23b05b2016-08-15 16:29:02 -0700541 unlink(path); /* In case it already exists. Ignore return codes. */
Dominik Behrda6df412016-08-02 12:56:42 -0700542 if (symlink(ptsname(shl_pty_get_fd(new_terminal->term->pty)), path) < 0)
543 LOG(ERROR, "Failed to symlink pts name %s to %s, %d:%s",
544 path,
545 ptsname(shl_pty_get_fd(new_terminal->term->pty)),
546 errno, strerror(errno));
547 }
548
David Sodmanbbcb0522014-09-19 10:34:07 -0700549 status = shl_pty_bridge_add(new_terminal->term->pty_bridge, new_terminal->term->pty);
550 if (status) {
Dominik Behrf9329912016-08-25 17:31:52 -0700551 LOG(ERROR, "Failed to add pty bridge on VT%u.", vt);
David Sodmanbbcb0522014-09-19 10:34:07 -0700552 term_close(new_terminal);
553 return NULL;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700554 }
555
David Sodmanbbcb0522014-09-19 10:34:07 -0700556 new_terminal->term->pid = shl_pty_get_child(new_terminal->term->pty);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700557
Haixia Shi95285872016-11-08 15:26:35 -0800558 status = term_resize(new_terminal, 0);
Dominik Behr83010f82016-03-18 18:43:08 -0700559
David Sodmanbbcb0522014-09-19 10:34:07 -0700560 if (status < 0) {
Dominik Behrf9329912016-08-25 17:31:52 -0700561 LOG(ERROR, "Failed to resize VT%u.", vt);
David Sodmanbbcb0522014-09-19 10:34:07 -0700562 term_close(new_terminal);
563 return NULL;
564 }
David Sodman8ef20062015-01-06 09:23:40 -0800565
David Sodmanbbcb0522014-09-19 10:34:07 -0700566 return new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700567}
568
David Sodman8ef20062015-01-06 09:23:40 -0800569void term_activate(terminal_t* terminal)
570{
Dominik Behr4defb362016-01-13 12:36:14 -0800571 term_set_current_to(terminal);
David Sodman8ef20062015-01-06 09:23:40 -0800572 terminal->active = true;
Dominik Behr83010f82016-03-18 18:43:08 -0700573 fb_setmode(terminal->fb);
David Sodman8ef20062015-01-06 09:23:40 -0800574 term_redraw(terminal);
575}
576
David Sodmanf0a925a2015-05-04 11:19:19 -0700577void term_deactivate(terminal_t* terminal)
578{
579 if (!terminal->active)
580 return;
581
David Sodmanf0a925a2015-05-04 11:19:19 -0700582 terminal->active = false;
David Sodmanf0a925a2015-05-04 11:19:19 -0700583}
584
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800585void term_close(terminal_t* term)
David Sodmanbbcb0522014-09-19 10:34:07 -0700586{
Dominik Behrda6df412016-08-02 12:56:42 -0700587 char path[32];
David Sodmanbbcb0522014-09-19 10:34:07 -0700588 if (!term)
589 return;
590
Dominik Behrda6df412016-08-02 12:56:42 -0700591 snprintf(path, sizeof(path), FRECON_VT_PATH, term->vt);
592 unlink(path);
593
Dominik Behr83010f82016-03-18 18:43:08 -0700594 if (term->fb) {
595 fb_close(term->fb);
596 term->fb = NULL;
David Sodmanbf3f2842014-11-12 08:26:58 -0800597 }
598
David Sodmanbbcb0522014-09-19 10:34:07 -0700599 if (term->term) {
Dominik Behrda6df412016-08-02 12:56:42 -0700600 if (term->term->pty) {
601 if (term->term->pty_bridge >= 0) {
602 shl_pty_bridge_remove(term->term->pty_bridge, term->term->pty);
603 shl_pty_bridge_free(term->term->pty_bridge);
604 term->term->pty_bridge = -1;
605 }
606 shl_pty_close(term->term->pty);
607 term->term->pty = NULL;
608 }
David Sodmanbbcb0522014-09-19 10:34:07 -0700609 free(term->term);
610 term->term = NULL;
611 }
612
Haixia Shi95d680e2015-04-27 20:29:17 -0700613 font_free();
David Sodmanbbcb0522014-09-19 10:34:07 -0700614 free(term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700615}
David Sodmanbf3f2842014-11-12 08:26:58 -0800616
617bool term_is_child_done(terminal_t* terminal)
618{
619 int status;
620 int ret;
621 ret = waitpid(terminal->term->pid, &status, WNOHANG);
622
David Sodmanf0a925a2015-05-04 11:19:19 -0700623 if ((ret == -1) && (errno == ECHILD)) {
624 return false;
625 }
David Sodmanbf3f2842014-11-12 08:26:58 -0800626 return ret != 0;
627}
628
629void term_page_up(terminal_t* terminal)
630{
631 tsm_screen_sb_page_up(terminal->term->screen, 1);
632 term_redraw(terminal);
633}
634
635void term_page_down(terminal_t* terminal)
636{
637 tsm_screen_sb_page_down(terminal->term->screen, 1);
638 term_redraw(terminal);
639}
640
641void term_line_up(terminal_t* terminal)
642{
643 tsm_screen_sb_up(terminal->term->screen, 1);
644 term_redraw(terminal);
645}
646
647void term_line_down(terminal_t* terminal)
648{
649 tsm_screen_sb_down(terminal->term->screen, 1);
650 term_redraw(terminal);
651}
652
653bool term_is_valid(terminal_t* terminal)
654{
655 return ((terminal != NULL) && (terminal->term != NULL));
656}
657
658int term_fd(terminal_t* terminal)
659{
660 if (term_is_valid(terminal))
661 return terminal->term->pty_bridge;
662 else
663 return -1;
664}
665
666void term_dispatch_io(terminal_t* terminal, fd_set* read_set)
667{
668 if (term_is_valid(terminal))
669 if (FD_ISSET(terminal->term->pty_bridge, read_set))
670 shl_pty_bridge_dispatch(terminal->term->pty_bridge, 0);
671}
672
673bool term_exception(terminal_t* terminal, fd_set* exception_set)
674{
675 if (term_is_valid(terminal)) {
676 if (terminal->term->pty_bridge >= 0) {
677 return FD_ISSET(terminal->term->pty_bridge,
678 exception_set);
679 }
680 }
681
682 return false;
683}
684
685bool term_is_active(terminal_t* terminal)
686{
687 if (term_is_valid(terminal))
688 return terminal->active;
689
690 return false;
691}
692
Dominik Behrb1abcba2016-04-14 14:57:21 -0700693void term_add_fds(terminal_t* terminal, fd_set* read_set, fd_set* exception_set, int* maxfd)
David Sodmanbf3f2842014-11-12 08:26:58 -0800694{
695 if (term_is_valid(terminal)) {
696 if (terminal->term->pty_bridge >= 0) {
Dominik Behrd7112672016-01-20 16:59:34 -0800697 *maxfd = MAX(*maxfd, terminal->term->pty_bridge);
David Sodmanbf3f2842014-11-12 08:26:58 -0800698 FD_SET(terminal->term->pty_bridge, read_set);
699 FD_SET(terminal->term->pty_bridge, exception_set);
700 }
701 }
702}
David Sodman8ef20062015-01-06 09:23:40 -0800703
704const char* term_get_ptsname(terminal_t* terminal)
705{
706 return ptsname(shl_pty_get_fd(terminal->term->pty));
707}
David Sodmanf0a925a2015-05-04 11:19:19 -0700708
709void term_set_background(terminal_t* terminal, uint32_t bg)
710{
711 terminal->background = bg;
712 terminal->background_valid = true;
713}
714
715int term_show_image(terminal_t* terminal, image_t* image)
716{
Dominik Behr83010f82016-03-18 18:43:08 -0700717 return image_show(image, terminal->fb);
David Sodmanf0a925a2015-05-04 11:19:19 -0700718}
719
720void term_write_message(terminal_t* terminal, char* message)
721{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800722 FILE* fp;
David Sodmanf0a925a2015-05-04 11:19:19 -0700723
724 fp = fopen(term_get_ptsname(terminal), "w");
725 if (fp) {
726 fputs(message, fp);
727 fclose(fp);
728 }
729}
730
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800731static void term_hide_cursor(terminal_t* terminal)
David Sodmanf0a925a2015-05-04 11:19:19 -0700732{
733 term_write_message(terminal, "\033[?25l");
734}
735
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800736__attribute__ ((unused))
737static void term_show_cursor(terminal_t* terminal)
David Sodmanf0a925a2015-05-04 11:19:19 -0700738{
739 term_write_message(terminal, "\033[?25h");
740}
David Sodman30a94ef2015-07-26 17:37:12 -0700741
Dominik Behr83010f82016-03-18 18:43:08 -0700742fb_t* term_getfb(terminal_t* terminal)
David Sodman30a94ef2015-07-26 17:37:12 -0700743{
Dominik Behr83010f82016-03-18 18:43:08 -0700744 return terminal->fb;
David Sodman30a94ef2015-07-26 17:37:12 -0700745}
Stéphane Marchesin08c08e72016-01-07 16:44:08 -0800746
747terminal_t* term_get_terminal(int num)
748{
749 return terminals[num];
750}
751
752void term_set_terminal(int num, terminal_t* terminal)
753{
754 terminals[num] = terminal;
755}
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800756
Dominik Behrda6df412016-08-02 12:56:42 -0700757int term_create_splash_term(int pts_fd)
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800758{
Dominik Behrda6df412016-08-02 12:56:42 -0700759 terminal_t* terminal = term_init(TERM_SPLASH_TERMINAL, pts_fd);
760
761 if (!terminal) {
762 LOG(ERROR, "Could not create splash term.");
763 return -1;
764 }
765 term_set_terminal(TERM_SPLASH_TERMINAL, terminal);
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800766
767 // Hide the cursor on the splash screen
Dominik Behrda6df412016-08-02 12:56:42 -0700768 term_hide_cursor(terminal);
769 return 0;
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800770}
771
Dominik Behrda6df412016-08-02 12:56:42 -0700772void term_destroy_splash_term(void)
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800773{
Dominik Behrda6df412016-08-02 12:56:42 -0700774 terminal_t *terminal;
775 if (command_flags.enable_vt1) {
776 return;
777 }
778 terminal = term_get_terminal(TERM_SPLASH_TERMINAL);
779 term_set_terminal(TERM_SPLASH_TERMINAL, NULL);
780 term_close(terminal);
Stéphane Marchesin92a297d2016-01-07 20:24:55 -0800781}
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800782
Dominik Behr4defb362016-01-13 12:36:14 -0800783void term_set_current(uint32_t t)
784{
Dominik Behrda6df412016-08-02 12:56:42 -0700785 if (t >= TERM_MAX_TERMINALS)
786 LOG(ERROR, "set_current: larger than array size");
787 else
788 if (t >= term_num_terminals)
789 LOG(ERROR, "set_current: larger than num terminals");
Dominik Behr4defb362016-01-13 12:36:14 -0800790 else
791 current_terminal = t;
792}
793
794uint32_t term_get_current(void)
795{
796 return current_terminal;
797}
798
799terminal_t *term_get_current_terminal(void)
800{
801 return terminals[current_terminal];
802}
803
Dominik Behrb1abcba2016-04-14 14:57:21 -0700804void term_set_current_terminal(terminal_t* terminal)
Dominik Behr4defb362016-01-13 12:36:14 -0800805{
806 terminals[current_terminal] = terminal;
807}
808
809void term_set_current_to(terminal_t* terminal)
810{
811 if (!terminal) {
Haixia Shi95285872016-11-08 15:26:35 -0800812 if (terminals[current_terminal])
813 term_close(terminals[current_terminal]);
Dominik Behr4defb362016-01-13 12:36:14 -0800814 terminals[current_terminal] = NULL;
815 current_terminal = 0;
816 return;
817 }
818
Dominik Behrda6df412016-08-02 12:56:42 -0700819 for (unsigned i = 0; i < term_num_terminals; i++) {
Dominik Behr4defb362016-01-13 12:36:14 -0800820 if (terminal == terminals[i]) {
821 current_terminal = i;
822 return;
823 }
824 }
825 LOG(ERROR, "set_current_to: terminal not in array");
826}
Dominik Behr01a7a582016-01-28 17:02:21 -0800827
Dominik Behrda6df412016-08-02 12:56:42 -0700828int term_switch_to(unsigned int vt)
829{
830 terminal_t *terminal;
831 if (vt == term_get_current()) {
832 terminal = term_get_current_terminal();
Dominik Behrf9329912016-08-25 17:31:52 -0700833 if (term_is_valid(terminal)) {
834 if (!term_is_active(terminal))
835 term_activate(terminal);
836 return vt;
837 }
Dominik Behrda6df412016-08-02 12:56:42 -0700838 }
839
840 if (vt >= term_num_terminals)
841 return -EINVAL;
842
843 terminal = term_get_current_terminal();
844 if (term_is_active(terminal))
845 term_deactivate(terminal);
846
847 if (vt == TERM_SPLASH_TERMINAL
848 && !term_get_terminal(TERM_SPLASH_TERMINAL)
849 && !command_flags.enable_vt1) {
850 term_set_current(vt);
851 /* Splash term is already gone, returning to Chrome. */
Dominik Behr33847332016-12-12 17:27:56 -0800852 term_background(false);
Dominik Behrda6df412016-08-02 12:56:42 -0700853 return vt;
854 }
855
856 term_foreground();
857
858 term_set_current(vt);
859 terminal = term_get_current_terminal();
860 if (!terminal) {
861 /* No terminal where we are switching to, create new one. */
862 term_set_current_terminal(term_init(vt, -1));
863 terminal = term_get_current_terminal();
Dominik Behrda6df412016-08-02 12:56:42 -0700864 if (!term_is_valid(terminal)) {
Dominik Behrf9329912016-08-25 17:31:52 -0700865 LOG(ERROR, "Term init failed VT%u.", vt);
Dominik Behrda6df412016-08-02 12:56:42 -0700866 return -1;
867 }
Dominik Behrf9329912016-08-25 17:31:52 -0700868 term_activate(terminal);
Dominik Behrda6df412016-08-02 12:56:42 -0700869 } else {
870 term_activate(terminal);
Dominik Behrda6df412016-08-02 12:56:42 -0700871 }
872
873 return vt;
874}
875
Dominik Behr01a7a582016-01-28 17:02:21 -0800876void term_monitor_hotplug(void)
877{
878 unsigned int t;
879
Dominik Behr83864df2016-04-21 12:35:08 -0700880 if (in_background) {
881 hotplug_occured = true;
882 return;
883 }
884
Dominik Behr83010f82016-03-18 18:43:08 -0700885 if (!drm_rescan())
886 return;
887
Dominik Behrda6df412016-08-02 12:56:42 -0700888 for (t = 0; t < term_num_terminals; t++) {
Dominik Behr01a7a582016-01-28 17:02:21 -0800889 if (!terminals[t])
890 continue;
Dominik Behr83010f82016-03-18 18:43:08 -0700891 if (!terminals[t]->fb)
Dominik Behr01a7a582016-01-28 17:02:21 -0800892 continue;
Dominik Behr83010f82016-03-18 18:43:08 -0700893 fb_buffer_destroy(terminals[t]->fb);
Dominik Behra3f65712016-04-25 17:42:14 -0700894 font_free();
Dominik Behr83010f82016-03-18 18:43:08 -0700895 }
896
Dominik Behrda6df412016-08-02 12:56:42 -0700897 for (t = 0; t < term_num_terminals; t++) {
Dominik Behr83010f82016-03-18 18:43:08 -0700898 if (!terminals[t])
899 continue;
900 if (!terminals[t]->fb)
901 continue;
902 fb_buffer_init(terminals[t]->fb);
Haixia Shi95285872016-11-08 15:26:35 -0800903 term_resize(terminals[t], 0);
Dominik Behr83010f82016-03-18 18:43:08 -0700904 if (current_terminal == t && terminals[t]->active)
905 fb_setmode(terminals[t]->fb);
906 terminals[t]->term->age = 0;
907 term_redraw(terminals[t]);
Dominik Behr01a7a582016-01-28 17:02:21 -0800908 }
909}
Dominik Behrb1abcba2016-04-14 14:57:21 -0700910
911void term_redrm(terminal_t* terminal)
912{
913 fb_buffer_destroy(terminal->fb);
Dominik Behra3f65712016-04-25 17:42:14 -0700914 font_free();
Dominik Behrb1abcba2016-04-14 14:57:21 -0700915 fb_buffer_init(terminal->fb);
Haixia Shi95285872016-11-08 15:26:35 -0800916 term_resize(terminal, 0);
Dominik Behrb1abcba2016-04-14 14:57:21 -0700917 terminal->term->age = 0;
918 term_redraw(terminal);
919}
920
921void term_clear(terminal_t* terminal)
922{
923 tsm_screen_erase_screen(terminal->term->screen, false);
924 term_redraw(terminal);
925}
Dominik Behr83864df2016-04-21 12:35:08 -0700926
Haixia Shi95285872016-11-08 15:26:35 -0800927void term_zoom(bool zoom_in)
928{
929 int scaling = font_get_scaling();
930 if (zoom_in && scaling < 4)
931 scaling++;
932 else if (!zoom_in && scaling > 1)
933 scaling--;
934 else
935 return;
936
937 unsigned int t;
938 for (t = 0; t < term_num_terminals; t++) {
939 if (terminals[t])
940 font_free();
941 }
942 for (t = 0; t < term_num_terminals; t++) {
943 terminal_t* term = terminals[t];
944 if (term) {
945 term_resize(term, scaling);
946 term->term->age = 0;
947 term_redraw(term);
948 }
949 }
950}
951
Dominik Behr33847332016-12-12 17:27:56 -0800952/*
953 * Put frecon in background. Give up DRM master.
954 * onetry - if true, do not retry to notify Chrome multiple times. For use at
955 * time when Chrome may be not around yet to receive the message.
956 */
957void term_background(bool onetry)
Dominik Behr83864df2016-04-21 12:35:08 -0700958{
Dominik Behr33847332016-12-12 17:27:56 -0800959 int retry = onetry ? 1 : 5;
Dominik Behr83864df2016-04-21 12:35:08 -0700960 if (in_background)
961 return;
962 in_background = true;
Dominik Behrda3c0102016-06-08 15:05:38 -0700963 drm_dropmaster(NULL);
Dominik Behra818a1e2016-10-26 19:46:22 -0700964 while (!dbus_take_display_ownership() && retry--) {
Dominik Behr33847332016-12-12 17:27:56 -0800965 if (onetry)
966 break;
Dominik Behra818a1e2016-10-26 19:46:22 -0700967 LOG(ERROR, "Chrome failed to take display ownership. %s",
968 retry ? "Trying again." : "Giving up, Chrome is probably dead.");
Dominik Behr33847332016-12-12 17:27:56 -0800969 if (retry > 0)
970 usleep(500 * 1000);
Dominik Behra818a1e2016-10-26 19:46:22 -0700971 }
Dominik Behr83864df2016-04-21 12:35:08 -0700972}
973
974void term_foreground(void)
975{
Dominik Behrda3c0102016-06-08 15:05:38 -0700976 int ret;
Dominik Behr33847332016-12-12 17:27:56 -0800977 int retry = 5;
Dominik Behra818a1e2016-10-26 19:46:22 -0700978
Dominik Behr83864df2016-04-21 12:35:08 -0700979 if (!in_background)
980 return;
981 in_background = false;
Dominik Behra818a1e2016-10-26 19:46:22 -0700982
983 while (!dbus_release_display_ownership() && retry--) {
984 LOG(ERROR, "Chrome did not release master. %s",
985 retry ? "Trying again." : "Frecon will steal master.");
Dominik Behr33847332016-12-12 17:27:56 -0800986 if (retry > 0)
987 usleep(500 * 1000);
Dominik Behr83864df2016-04-21 12:35:08 -0700988 }
Dominik Behrda3c0102016-06-08 15:05:38 -0700989
990 ret = drm_setmaster(NULL);
Dominik Behrda3c0102016-06-08 15:05:38 -0700991 if (ret < 0)
992 LOG(ERROR, "Could not set master when switching to foreground %m.");
993
Dominik Behr83864df2016-04-21 12:35:08 -0700994 if (hotplug_occured) {
995 hotplug_occured = false;
996 term_monitor_hotplug();
997 }
998}
Dominik Behr1883c042016-04-27 12:31:02 -0700999
1000void term_suspend_done(void* ignore)
1001{
1002 term_monitor_hotplug();
1003}
Dominik Behrda55e6c2017-02-28 18:12:10 -08001004
1005void term_input_enable(terminal_t* terminal, bool input_enable)
1006{
1007 terminal->input_enable = input_enable;
1008}