blob: 96b82485c27e18ba54b79a5763dac843d14765df [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 -070028#define FRECON_VT_PATH FRECON_RUN_DIR "/vt%u"
29
30unsigned int term_num_terminals = 4;
31static terminal_t* terminals[TERM_MAX_TERMINALS];
Dominik Behr4defb362016-01-13 12:36:14 -080032static uint32_t current_terminal = 0;
Stéphane Marchesin08c08e72016-01-07 16:44:08 -080033
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070034struct term {
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080035 struct tsm_screen* screen;
36 struct tsm_vte* vte;
37 struct shl_pty* pty;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070038 int pty_bridge;
39 int pid;
40 tsm_age_t age;
41 int char_x, char_y;
42 int pitch;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080043 uint32_t* dst_image;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070044};
45
David Sodmanf0a925a2015-05-04 11:19:19 -070046struct _terminal_t {
Dominik Behrda6df412016-08-02 12:56:42 -070047 unsigned vt;
48 bool active;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080049 uint32_t background;
50 bool background_valid;
Dominik Behr83010f82016-03-18 18:43:08 -070051 fb_t* fb;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080052 struct term* term;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080053 char** exec;
David Sodmanf0a925a2015-05-04 11:19:19 -070054};
55
David Sodman8ef20062015-01-06 09:23:40 -080056
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080057static char* interactive_cmd_line[] = {
David Sodman8ef20062015-01-06 09:23:40 -080058 "/sbin/agetty",
59 "-",
60 "9600",
61 "xterm",
62 NULL
63};
64
Dominik Behr83864df2016-04-21 12:35:08 -070065static bool in_background = false;
66static bool hotplug_occured = false;
67
David Sodman8ef20062015-01-06 09:23:40 -080068
69static void __attribute__ ((noreturn)) term_run_child(terminal_t* terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070070{
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070071 /* XXX figure out how to fix "top" for xterm-256color */
72 setenv("TERM", "xterm", 1);
Dominik Behr32680e32016-08-16 12:18:41 -070073 if (terminal->exec) {
74 execve(terminal->exec[0], terminal->exec, environ);
75 exit(1);
76 } else {
77 while (1)
78 sleep(1000000);
79 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070080}
81
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080082static int term_draw_cell(struct tsm_screen* screen, uint32_t id,
Stéphane Marchesin8fc13522015-12-14 17:02:28 -080083 const uint32_t* ch, size_t len,
84 unsigned int cwidth, unsigned int posx,
85 unsigned int posy,
86 const struct tsm_screen_attr* attr,
87 tsm_age_t age, void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070088{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080089 terminal_t* terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070090 uint32_t front_color, back_color;
David Sodmanf0a925a2015-05-04 11:19:19 -070091 uint8_t br, bb, bg;
Stéphane Marchesinedece332015-12-14 15:10:58 -080092 uint32_t luminance;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070093
David Sodmanbbcb0522014-09-19 10:34:07 -070094 if (age && terminal->term->age && age <= terminal->term->age)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070095 return 0;
96
David Sodmanf0a925a2015-05-04 11:19:19 -070097 if (terminal->background_valid) {
98 br = (terminal->background >> 16) & 0xFF;
99 bg = (terminal->background >> 8) & 0xFF;
100 bb = (terminal->background) & 0xFF;
Stéphane Marchesinedece332015-12-14 15:10:58 -0800101 luminance = (3 * br + bb + 4 * bg) >> 3;
David Sodmanf0a925a2015-05-04 11:19:19 -0700102
Stéphane Marchesinedece332015-12-14 15:10:58 -0800103 /*
104 * FIXME: black is chosen on a dark background, but it uses the
105 * default color for light backgrounds
106 */
107 if (luminance > 128) {
David Sodmanf0a925a2015-05-04 11:19:19 -0700108 front_color = 0;
109 back_color = terminal->background;
110 } else {
111 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
112 back_color = terminal->background;
113 }
114 } else {
115 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
116 back_color = (attr->br << 16) | (attr->bg << 8) | attr->bb;
117 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700118
119 if (attr->inverse) {
120 uint32_t tmp = front_color;
121 front_color = back_color;
122 back_color = tmp;
123 }
124
125 if (len)
David Sodmanbbcb0522014-09-19 10:34:07 -0700126 font_render(terminal->term->dst_image, posx, posy, terminal->term->pitch, *ch,
127 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700128 else
David Sodmanbbcb0522014-09-19 10:34:07 -0700129 font_fillchar(terminal->term->dst_image, posx, posy, terminal->term->pitch,
130 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700131
132 return 0;
133}
134
Stéphane Marchesine2a46cd2016-01-07 16:45:24 -0800135static void term_redraw(terminal_t* terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700136{
Dominik Behr83010f82016-03-18 18:43:08 -0700137 uint32_t* fb_buffer;
138 fb_buffer = fb_lock(terminal->fb);
139 if (fb_buffer != NULL) {
140 terminal->term->dst_image = fb_buffer;
David Sodmanbbcb0522014-09-19 10:34:07 -0700141 terminal->term->age =
142 tsm_screen_draw(terminal->term->screen, term_draw_cell, terminal);
Dominik Behr83010f82016-03-18 18:43:08 -0700143 fb_unlock(terminal->fb);
David Sodmanbbcb0522014-09-19 10:34:07 -0700144 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700145}
146
David Sodmanbbcb0522014-09-19 10:34:07 -0700147void term_key_event(terminal_t* terminal, uint32_t keysym, int32_t unicode)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700148{
David Sodmanbbcb0522014-09-19 10:34:07 -0700149 if (tsm_vte_handle_keyboard(terminal->term->vte, keysym, 0, 0, unicode))
150 tsm_screen_sb_reset(terminal->term->screen);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700151
David Sodmanbbcb0522014-09-19 10:34:07 -0700152 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700153}
154
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800155static void term_read_cb(struct shl_pty* pty, char* u8, size_t len, void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700156{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800157 terminal_t* terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700158
David Sodmanbbcb0522014-09-19 10:34:07 -0700159 tsm_vte_input(terminal->term->vte, u8, len);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700160
David Sodmanbbcb0522014-09-19 10:34:07 -0700161 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700162}
163
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800164static void term_write_cb(struct tsm_vte* vte, const char* u8, size_t len,
165 void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700166{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800167 struct term* term = data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700168 int r;
169
170 r = shl_pty_write(term->pty, u8, len);
171 if (r < 0)
David Sodmanbbcb0522014-09-19 10:34:07 -0700172 LOG(ERROR, "OOM in pty-write (%d)", r);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700173
174 shl_pty_dispatch(term->pty);
175}
176
Dominik Behrd2530902016-05-05 14:01:06 -0700177static void term_esc_show_image(terminal_t* terminal, char* params)
178{
179 char* tok;
180 image_t* image;
181 int status;
182
183 image = image_create();
184 if (!image) {
185 LOG(ERROR, "Out of memory when creating an image.\n");
186 return;
187 }
188 for (tok = strtok(params, ";"); tok; tok = strtok(NULL, ";")) {
189 if (strncmp("file=", tok, 5) == 0) {
190 image_set_filename(image, tok + 5);
191 } else if (strncmp("location=", tok, 9) == 0) {
192 uint32_t x, y;
193 if (sscanf(tok + 9, "%u,%u", &x, &y) != 2) {
194 LOG(ERROR, "Error parsing image location.\n");
195 goto done;
196 }
197 image_set_location(image, x, y);
198 } else if (strncmp("offset=", tok, 7) == 0) {
199 int32_t x, y;
200 if (sscanf(tok + 7, "%d,%d", &x, &y) != 2) {
201 LOG(ERROR, "Error parsing image offset.\n");
202 goto done;
203 }
204 image_set_offset(image, x, y);
205 } else if (strncmp("scale=", tok, 6) == 0) {
206 uint32_t s;
207 if (sscanf(tok + 6, "%u", &s) != 1) {
208 LOG(ERROR, "Error parsing image scale.\n");
209 goto done;
210 }
211 if (s == 0)
212 s = image_get_auto_scale(term_getfb(terminal));
213 image_set_scale(image, s);
214 }
215 }
216
217 status = image_load_image_from_file(image);
218 if (status != 0) {
219 LOG(WARNING, "Term ESC image_load_image_from_file %s failed: %d:%s.",
220 image_get_filename(image), status, strerror(status));
221 } else {
222 term_show_image(terminal, image);
223 }
224done:
225 image_destroy(image);
226}
227
228static void term_esc_draw_box(terminal_t* terminal, char* params)
229{
230 char* tok;
231 uint32_t color = 0;
232 uint32_t w = 1;
233 uint32_t h = 1;
234 uint32_t locx, locy;
235 bool use_location = false;
236 int32_t offx, offy;
237 bool use_offset = false;
238 uint32_t scale = 1;
239 uint32_t* buffer;
240 int32_t startx, starty;
241 uint32_t pitch4;
242
243 for (tok = strtok(params, ";"); tok; tok = strtok(NULL, ";")) {
244 if (strncmp("color=", tok, 6) == 0) {
245 color = strtoul(tok + 6, NULL, 0);
246 } else if (strncmp("size=", tok, 5) == 0) {
247 if (sscanf(tok + 5, "%u,%u", &w, &h) != 2) {
248 LOG(ERROR, "Error parsing box size.\n");
249 goto done;
250 }
251 } else if (strncmp("location=", tok, 9) == 0) {
252 if (sscanf(tok + 9, "%u,%u", &locx, &locy) != 2) {
253 LOG(ERROR, "Error parsing box location.\n");
254 goto done;
255 }
256 use_location = true;
257 } else if (strncmp("offset=", tok, 7) == 0) {
258 if (sscanf(tok + 7, "%d,%d", &offx, &offy) != 2) {
259 LOG(ERROR, "Error parsing box offset.\n");
260 goto done;
261 }
262 use_offset = true;
263 } else if (strncmp("scale=", tok, 6) == 0) {
264 if (sscanf(tok + 6, "%u", &scale) != 1) {
265 LOG(ERROR, "Error parsing box scale.\n");
266 goto done;
267 }
268 if (scale == 0)
269 scale = image_get_auto_scale(term_getfb(terminal));
270 }
271 }
272
273 w *= scale;
274 h *= scale;
275 offx *= scale;
276 offy *= scale;
277
278 buffer = fb_lock(terminal->fb);
279 if (buffer == NULL)
280 goto done;
281
282 if (use_offset && use_location) {
283 LOG(WARNING, "Box offset and location set, using location.");
284 use_offset = false;
285 }
286
287 if (use_location) {
288 startx = locx;
289 starty = locy;
290 } else {
291 startx = (fb_getwidth(terminal->fb) - (int32_t)w)/2;
292 starty = (fb_getheight(terminal->fb) - (int32_t)h)/2;
293 }
294
295 if (use_offset) {
296 startx += offx;
297 starty += offy;
298 }
299
300 pitch4 = fb_getpitch(terminal->fb) / 4;
301
302 /* Completely outside buffer, do nothing */
303 if (startx + w <= 0 || startx >= fb_getwidth(terminal->fb))
304 goto done_fb;
305 if (starty + h <= 0 || starty >= fb_getheight(terminal->fb))
306 goto done_fb;
307 /* Make sure we are inside buffer. */
308 if (startx < 0)
309 startx = 0;
310 if (startx + (int32_t)w > fb_getwidth(terminal->fb))
311 w = fb_getwidth(terminal->fb) - startx;
312 if (starty < 0)
313 starty = 0;
314 if (starty + (int32_t)h > fb_getheight(terminal->fb))
315 h = fb_getheight(terminal->fb) - starty;
316
317 for (uint32_t y = 0; y < h; y++) {
318 uint32_t *o = buffer + (starty + y) * pitch4
319 + startx;
320 for (uint32_t x = 0; x < w; x++)
321 o[x] = color;
322 }
323done_fb:
324 fb_unlock(terminal->fb);
325done:
326 ;
327}
328
329static void term_osc_cb(struct tsm_vte *vte, const uint32_t *osc_string,
330 size_t osc_len, void *data)
331{
332 terminal_t* terminal = (terminal_t*)data;
333 size_t i;
334 char *osc;
335
336 for (i = 0; i < osc_len; i++)
337 if (osc_string[i] >= 128)
338 return; /* we only want to deal with ASCII */
339
340 osc = malloc(osc_len + 1);
341 if (!osc) {
342 LOG(WARNING, "Out of memory when processing OSC\n");
343 return;
344 }
345
346 for (i = 0; i < osc_len; i++)
347 osc[i] = (char)osc_string[i];
348 osc[i] = '\0';
349
350 if (strncmp(osc, "image:", 6) == 0)
351 term_esc_show_image(terminal, osc + 6);
352 else if (strncmp(osc, "box:", 4) == 0)
353 term_esc_draw_box(terminal, osc + 4);
354 else
355 LOG(WARNING, "Unknown OSC escape sequence \"%s\", ignoring.", osc);
356
357 free(osc);
358}
359
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800360static const char* sev2str_table[] = {
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700361 "FATAL",
362 "ALERT",
363 "CRITICAL",
364 "ERROR",
365 "WARNING",
366 "NOTICE",
367 "INFO",
368 "DEBUG"
369};
370
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800371static const char* sev2str(unsigned int sev)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700372{
373 if (sev > 7)
374 return "DEBUG";
375
376 return sev2str_table[sev];
377}
378
Dominik Behr00003502014-08-15 16:42:37 -0700379#ifdef __clang__
380__attribute__((__format__ (__printf__, 7, 0)))
381#endif
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800382static void log_tsm(void* data, const char* file, int line, const char* fn,
Stéphane Marchesin8fc13522015-12-14 17:02:28 -0800383 const char* subs, unsigned int sev, const char* format,
384 va_list args)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700385{
386 fprintf(stderr, "%s: %s: ", sev2str(sev), subs);
387 vfprintf(stderr, format, args);
388 fprintf(stderr, "\n");
389}
390
Stéphane Marchesin78310662016-01-06 16:09:39 -0800391static int term_resize(terminal_t* term)
392{
393 uint32_t char_width, char_height;
394 int status;
395
Dominik Behr83010f82016-03-18 18:43:08 -0700396 font_init(fb_getscaling(term->fb));
Stéphane Marchesin78310662016-01-06 16:09:39 -0800397 font_get_size(&char_width, &char_height);
398
Dominik Behr83010f82016-03-18 18:43:08 -0700399 term->term->char_x = fb_getwidth(term->fb) / char_width;
400 term->term->char_y = fb_getheight(term->fb) / char_height;
401 term->term->pitch = fb_getpitch(term->fb);
Stéphane Marchesin78310662016-01-06 16:09:39 -0800402
403 status = tsm_screen_resize(term->term->screen,
404 term->term->char_x, term->term->char_y);
405 if (status < 0) {
406 font_free();
407 return -1;
408 }
409
410 status = shl_pty_resize(term->term->pty, term->term->char_x,
411 term->term->char_y);
412 if (status < 0) {
413 font_free();
414 return -1;
415 }
416
417 return 0;
418}
419
Dominik Behrda6df412016-08-02 12:56:42 -0700420void term_set_num_terminals(unsigned new_num)
421{
422 if (new_num < 1)
423 term_num_terminals = 1;
424 else if (new_num > TERM_MAX_TERMINALS)
425 term_num_terminals = TERM_MAX_TERMINALS;
426 else
427 term_num_terminals = new_num;
428}
429
430static bool term_is_interactive(unsigned int vt)
431{
432 if (command_flags.no_login)
433 return false;
434
435 if (vt == TERM_SPLASH_TERMINAL)
436 return command_flags.enable_vt1;
437
438 return true;
439}
440
441terminal_t* term_init(unsigned vt, int pts_fd)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700442{
443 const int scrollback_size = 200;
David Sodmanbbcb0522014-09-19 10:34:07 -0700444 int status;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800445 terminal_t* new_terminal;
Dominik Behrda6df412016-08-02 12:56:42 -0700446 bool interactive = term_is_interactive(vt);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700447
David Sodmanbbcb0522014-09-19 10:34:07 -0700448 new_terminal = (terminal_t*)calloc(1, sizeof(*new_terminal));
David Sodmanbf3f2842014-11-12 08:26:58 -0800449 if (!new_terminal)
450 return NULL;
David Sodmanbbcb0522014-09-19 10:34:07 -0700451
Dominik Behrda6df412016-08-02 12:56:42 -0700452 new_terminal->vt = vt;
David Sodmanf0a925a2015-05-04 11:19:19 -0700453 new_terminal->background_valid = false;
454
Dominik Behr83010f82016-03-18 18:43:08 -0700455 new_terminal->fb = fb_init();
David Sodmanf0a925a2015-05-04 11:19:19 -0700456
Dominik Behr83010f82016-03-18 18:43:08 -0700457 if (!new_terminal->fb) {
David Sodmanbf3f2842014-11-12 08:26:58 -0800458 term_close(new_terminal);
459 return NULL;
460 }
461
462 new_terminal->term = (struct term*)calloc(1, sizeof(*new_terminal->term));
463 if (!new_terminal->term) {
464 term_close(new_terminal);
465 return NULL;
466 }
467
Dominik Behrda6df412016-08-02 12:56:42 -0700468 if (interactive)
David Sodman8ef20062015-01-06 09:23:40 -0800469 new_terminal->exec = interactive_cmd_line;
470 else
Dominik Behr32680e32016-08-16 12:18:41 -0700471 new_terminal->exec = NULL;
David Sodman8ef20062015-01-06 09:23:40 -0800472
David Sodmanbbcb0522014-09-19 10:34:07 -0700473 status = tsm_screen_new(&new_terminal->term->screen,
474 log_tsm, new_terminal->term);
zhuo-hao471f1e22015-08-12 14:55:56 +0800475 if (status < 0) {
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) {
486 term_close(new_terminal);
487 return NULL;
488 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700489
Dominik Behrd2530902016-05-05 14:01:06 -0700490 if (command_flags.enable_gfx)
491 tsm_vte_set_osc_cb(new_terminal->term->vte, term_osc_cb, (void *)new_terminal);
492
David Sodmanbbcb0522014-09-19 10:34:07 -0700493 new_terminal->term->pty_bridge = shl_pty_bridge_new();
494 if (new_terminal->term->pty_bridge < 0) {
495 term_close(new_terminal);
496 return NULL;
497 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700498
David Sodmanbbcb0522014-09-19 10:34:07 -0700499 status = shl_pty_open(&new_terminal->term->pty,
Dominik Behrda6df412016-08-02 12:56:42 -0700500 term_read_cb, new_terminal, 1, 1, pts_fd);
David Sodman8ef20062015-01-06 09:23:40 -0800501
David Sodmanbbcb0522014-09-19 10:34:07 -0700502 if (status < 0) {
503 term_close(new_terminal);
504 return NULL;
505 } else if (status == 0) {
David Sodman8ef20062015-01-06 09:23:40 -0800506 term_run_child(new_terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700507 exit(1);
508 }
509
Dominik Behrda6df412016-08-02 12:56:42 -0700510 status = mkdir(FRECON_RUN_DIR, S_IRWXU);
511 if (status == 0 || (status < 0 && errno == EEXIST)) {
512 char path[32];
513 snprintf(path, sizeof(path), FRECON_VT_PATH, vt);
514 if (symlink(ptsname(shl_pty_get_fd(new_terminal->term->pty)), path) < 0)
515 LOG(ERROR, "Failed to symlink pts name %s to %s, %d:%s",
516 path,
517 ptsname(shl_pty_get_fd(new_terminal->term->pty)),
518 errno, strerror(errno));
519 }
520
David Sodmanbbcb0522014-09-19 10:34:07 -0700521 status = shl_pty_bridge_add(new_terminal->term->pty_bridge, new_terminal->term->pty);
522 if (status) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700523 term_close(new_terminal);
524 return NULL;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700525 }
526
David Sodmanbbcb0522014-09-19 10:34:07 -0700527 new_terminal->term->pid = shl_pty_get_child(new_terminal->term->pty);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700528
Stéphane Marchesin78310662016-01-06 16:09:39 -0800529 status = term_resize(new_terminal);
Dominik Behr83010f82016-03-18 18:43:08 -0700530
David Sodmanbbcb0522014-09-19 10:34:07 -0700531 if (status < 0) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700532 term_close(new_terminal);
533 return NULL;
534 }
David Sodman8ef20062015-01-06 09:23:40 -0800535
David Sodmanbbcb0522014-09-19 10:34:07 -0700536 return new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700537}
538
David Sodman8ef20062015-01-06 09:23:40 -0800539void term_activate(terminal_t* terminal)
540{
Dominik Behr4defb362016-01-13 12:36:14 -0800541 term_set_current_to(terminal);
David Sodman8ef20062015-01-06 09:23:40 -0800542 terminal->active = true;
Dominik Behr83010f82016-03-18 18:43:08 -0700543 fb_setmode(terminal->fb);
David Sodman8ef20062015-01-06 09:23:40 -0800544 term_redraw(terminal);
545}
546
David Sodmanf0a925a2015-05-04 11:19:19 -0700547void term_deactivate(terminal_t* terminal)
548{
549 if (!terminal->active)
550 return;
551
David Sodmanf0a925a2015-05-04 11:19:19 -0700552 terminal->active = false;
David Sodmanf0a925a2015-05-04 11:19:19 -0700553}
554
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800555void term_close(terminal_t* term)
David Sodmanbbcb0522014-09-19 10:34:07 -0700556{
Dominik Behrda6df412016-08-02 12:56:42 -0700557 char path[32];
David Sodmanbbcb0522014-09-19 10:34:07 -0700558 if (!term)
559 return;
560
Dominik Behrda6df412016-08-02 12:56:42 -0700561 snprintf(path, sizeof(path), FRECON_VT_PATH, term->vt);
562 unlink(path);
563
Dominik Behr83010f82016-03-18 18:43:08 -0700564 if (term->fb) {
565 fb_close(term->fb);
566 term->fb = NULL;
David Sodmanbf3f2842014-11-12 08:26:58 -0800567 }
568
David Sodmanbbcb0522014-09-19 10:34:07 -0700569 if (term->term) {
Dominik Behrda6df412016-08-02 12:56:42 -0700570 if (term->term->pty) {
571 if (term->term->pty_bridge >= 0) {
572 shl_pty_bridge_remove(term->term->pty_bridge, term->term->pty);
573 shl_pty_bridge_free(term->term->pty_bridge);
574 term->term->pty_bridge = -1;
575 }
576 shl_pty_close(term->term->pty);
577 term->term->pty = NULL;
578 }
David Sodmanbbcb0522014-09-19 10:34:07 -0700579 free(term->term);
580 term->term = NULL;
581 }
582
Haixia Shi95d680e2015-04-27 20:29:17 -0700583 font_free();
David Sodmanbbcb0522014-09-19 10:34:07 -0700584 free(term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700585}
David Sodmanbf3f2842014-11-12 08:26:58 -0800586
587bool term_is_child_done(terminal_t* terminal)
588{
589 int status;
590 int ret;
591 ret = waitpid(terminal->term->pid, &status, WNOHANG);
592
David Sodmanf0a925a2015-05-04 11:19:19 -0700593 if ((ret == -1) && (errno == ECHILD)) {
594 return false;
595 }
David Sodmanbf3f2842014-11-12 08:26:58 -0800596 return ret != 0;
597}
598
599void term_page_up(terminal_t* terminal)
600{
601 tsm_screen_sb_page_up(terminal->term->screen, 1);
602 term_redraw(terminal);
603}
604
605void term_page_down(terminal_t* terminal)
606{
607 tsm_screen_sb_page_down(terminal->term->screen, 1);
608 term_redraw(terminal);
609}
610
611void term_line_up(terminal_t* terminal)
612{
613 tsm_screen_sb_up(terminal->term->screen, 1);
614 term_redraw(terminal);
615}
616
617void term_line_down(terminal_t* terminal)
618{
619 tsm_screen_sb_down(terminal->term->screen, 1);
620 term_redraw(terminal);
621}
622
623bool term_is_valid(terminal_t* terminal)
624{
625 return ((terminal != NULL) && (terminal->term != NULL));
626}
627
628int term_fd(terminal_t* terminal)
629{
630 if (term_is_valid(terminal))
631 return terminal->term->pty_bridge;
632 else
633 return -1;
634}
635
636void term_dispatch_io(terminal_t* terminal, fd_set* read_set)
637{
638 if (term_is_valid(terminal))
639 if (FD_ISSET(terminal->term->pty_bridge, read_set))
640 shl_pty_bridge_dispatch(terminal->term->pty_bridge, 0);
641}
642
643bool term_exception(terminal_t* terminal, fd_set* exception_set)
644{
645 if (term_is_valid(terminal)) {
646 if (terminal->term->pty_bridge >= 0) {
647 return FD_ISSET(terminal->term->pty_bridge,
648 exception_set);
649 }
650 }
651
652 return false;
653}
654
655bool term_is_active(terminal_t* terminal)
656{
657 if (term_is_valid(terminal))
658 return terminal->active;
659
660 return false;
661}
662
Dominik Behrb1abcba2016-04-14 14:57:21 -0700663void term_add_fds(terminal_t* terminal, fd_set* read_set, fd_set* exception_set, int* maxfd)
David Sodmanbf3f2842014-11-12 08:26:58 -0800664{
665 if (term_is_valid(terminal)) {
666 if (terminal->term->pty_bridge >= 0) {
Dominik Behrd7112672016-01-20 16:59:34 -0800667 *maxfd = MAX(*maxfd, terminal->term->pty_bridge);
David Sodmanbf3f2842014-11-12 08:26:58 -0800668 FD_SET(terminal->term->pty_bridge, read_set);
669 FD_SET(terminal->term->pty_bridge, exception_set);
670 }
671 }
672}
David Sodman8ef20062015-01-06 09:23:40 -0800673
674const char* term_get_ptsname(terminal_t* terminal)
675{
676 return ptsname(shl_pty_get_fd(terminal->term->pty));
677}
David Sodmanf0a925a2015-05-04 11:19:19 -0700678
679void term_set_background(terminal_t* terminal, uint32_t bg)
680{
681 terminal->background = bg;
682 terminal->background_valid = true;
683}
684
685int term_show_image(terminal_t* terminal, image_t* image)
686{
Dominik Behr83010f82016-03-18 18:43:08 -0700687 return image_show(image, terminal->fb);
David Sodmanf0a925a2015-05-04 11:19:19 -0700688}
689
690void term_write_message(terminal_t* terminal, char* message)
691{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800692 FILE* fp;
David Sodmanf0a925a2015-05-04 11:19:19 -0700693
694 fp = fopen(term_get_ptsname(terminal), "w");
695 if (fp) {
696 fputs(message, fp);
697 fclose(fp);
698 }
699}
700
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800701static void term_hide_cursor(terminal_t* terminal)
David Sodmanf0a925a2015-05-04 11:19:19 -0700702{
703 term_write_message(terminal, "\033[?25l");
704}
705
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800706__attribute__ ((unused))
707static void term_show_cursor(terminal_t* terminal)
David Sodmanf0a925a2015-05-04 11:19:19 -0700708{
709 term_write_message(terminal, "\033[?25h");
710}
David Sodman30a94ef2015-07-26 17:37:12 -0700711
Dominik Behr83010f82016-03-18 18:43:08 -0700712fb_t* term_getfb(terminal_t* terminal)
David Sodman30a94ef2015-07-26 17:37:12 -0700713{
Dominik Behr83010f82016-03-18 18:43:08 -0700714 return terminal->fb;
David Sodman30a94ef2015-07-26 17:37:12 -0700715}
Stéphane Marchesin08c08e72016-01-07 16:44:08 -0800716
717terminal_t* term_get_terminal(int num)
718{
719 return terminals[num];
720}
721
722void term_set_terminal(int num, terminal_t* terminal)
723{
724 terminals[num] = terminal;
725}
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800726
Dominik Behrda6df412016-08-02 12:56:42 -0700727int term_create_splash_term(int pts_fd)
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800728{
Dominik Behrda6df412016-08-02 12:56:42 -0700729 terminal_t* terminal = term_init(TERM_SPLASH_TERMINAL, pts_fd);
730
731 if (!terminal) {
732 LOG(ERROR, "Could not create splash term.");
733 return -1;
734 }
735 term_set_terminal(TERM_SPLASH_TERMINAL, terminal);
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800736
737 // Hide the cursor on the splash screen
Dominik Behrda6df412016-08-02 12:56:42 -0700738 term_hide_cursor(terminal);
739 return 0;
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800740}
741
Dominik Behrda6df412016-08-02 12:56:42 -0700742void term_destroy_splash_term(void)
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800743{
Dominik Behrda6df412016-08-02 12:56:42 -0700744 terminal_t *terminal;
745 if (command_flags.enable_vt1) {
746 return;
747 }
748 terminal = term_get_terminal(TERM_SPLASH_TERMINAL);
749 term_set_terminal(TERM_SPLASH_TERMINAL, NULL);
750 term_close(terminal);
Stéphane Marchesin92a297d2016-01-07 20:24:55 -0800751}
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800752
Dominik Behr4defb362016-01-13 12:36:14 -0800753void term_set_current(uint32_t t)
754{
Dominik Behrda6df412016-08-02 12:56:42 -0700755 if (t >= TERM_MAX_TERMINALS)
756 LOG(ERROR, "set_current: larger than array size");
757 else
758 if (t >= term_num_terminals)
759 LOG(ERROR, "set_current: larger than num terminals");
Dominik Behr4defb362016-01-13 12:36:14 -0800760 else
761 current_terminal = t;
762}
763
764uint32_t term_get_current(void)
765{
766 return current_terminal;
767}
768
769terminal_t *term_get_current_terminal(void)
770{
771 return terminals[current_terminal];
772}
773
Dominik Behrb1abcba2016-04-14 14:57:21 -0700774void term_set_current_terminal(terminal_t* terminal)
Dominik Behr4defb362016-01-13 12:36:14 -0800775{
776 terminals[current_terminal] = terminal;
777}
778
779void term_set_current_to(terminal_t* terminal)
780{
781 if (!terminal) {
782 terminals[current_terminal] = NULL;
783 current_terminal = 0;
784 return;
785 }
786
Dominik Behrda6df412016-08-02 12:56:42 -0700787 for (unsigned i = 0; i < term_num_terminals; i++) {
Dominik Behr4defb362016-01-13 12:36:14 -0800788 if (terminal == terminals[i]) {
789 current_terminal = i;
790 return;
791 }
792 }
793 LOG(ERROR, "set_current_to: terminal not in array");
794}
Dominik Behr01a7a582016-01-28 17:02:21 -0800795
Dominik Behrda6df412016-08-02 12:56:42 -0700796int term_switch_to(unsigned int vt)
797{
798 terminal_t *terminal;
799 if (vt == term_get_current()) {
800 terminal = term_get_current_terminal();
801 if (!term_is_active(terminal))
802 term_activate(terminal);
803 return vt;
804 }
805
806 if (vt >= term_num_terminals)
807 return -EINVAL;
808
809 terminal = term_get_current_terminal();
810 if (term_is_active(terminal))
811 term_deactivate(terminal);
812
813 if (vt == TERM_SPLASH_TERMINAL
814 && !term_get_terminal(TERM_SPLASH_TERMINAL)
815 && !command_flags.enable_vt1) {
816 term_set_current(vt);
817 /* Splash term is already gone, returning to Chrome. */
818 term_background();
819 return vt;
820 }
821
822 term_foreground();
823
824 term_set_current(vt);
825 terminal = term_get_current_terminal();
826 if (!terminal) {
827 /* No terminal where we are switching to, create new one. */
828 term_set_current_terminal(term_init(vt, -1));
829 terminal = term_get_current_terminal();
830 term_activate(terminal);
831 if (!term_is_valid(terminal)) {
832 LOG(ERROR, "Term init failed");
833 return -1;
834 }
835 } else {
836 term_activate(terminal);
837 LOG(INFO, "Activated existing terminal %p on VT%u", terminal, vt);
838 }
839
840 return vt;
841}
842
Dominik Behr01a7a582016-01-28 17:02:21 -0800843void term_monitor_hotplug(void)
844{
845 unsigned int t;
846
Dominik Behr83864df2016-04-21 12:35:08 -0700847 if (in_background) {
848 hotplug_occured = true;
849 return;
850 }
851
Dominik Behr83010f82016-03-18 18:43:08 -0700852 if (!drm_rescan())
853 return;
854
Dominik Behrda6df412016-08-02 12:56:42 -0700855 for (t = 0; t < term_num_terminals; t++) {
Dominik Behr01a7a582016-01-28 17:02:21 -0800856 if (!terminals[t])
857 continue;
Dominik Behr83010f82016-03-18 18:43:08 -0700858 if (!terminals[t]->fb)
Dominik Behr01a7a582016-01-28 17:02:21 -0800859 continue;
Dominik Behr83010f82016-03-18 18:43:08 -0700860 fb_buffer_destroy(terminals[t]->fb);
Dominik Behra3f65712016-04-25 17:42:14 -0700861 font_free();
Dominik Behr83010f82016-03-18 18:43:08 -0700862 }
863
Dominik Behrda6df412016-08-02 12:56:42 -0700864 for (t = 0; t < term_num_terminals; t++) {
Dominik Behr83010f82016-03-18 18:43:08 -0700865 if (!terminals[t])
866 continue;
867 if (!terminals[t]->fb)
868 continue;
869 fb_buffer_init(terminals[t]->fb);
870 term_resize(terminals[t]);
871 if (current_terminal == t && terminals[t]->active)
872 fb_setmode(terminals[t]->fb);
873 terminals[t]->term->age = 0;
874 term_redraw(terminals[t]);
Dominik Behr01a7a582016-01-28 17:02:21 -0800875 }
876}
Dominik Behrb1abcba2016-04-14 14:57:21 -0700877
878void term_redrm(terminal_t* terminal)
879{
880 fb_buffer_destroy(terminal->fb);
Dominik Behra3f65712016-04-25 17:42:14 -0700881 font_free();
Dominik Behrb1abcba2016-04-14 14:57:21 -0700882 fb_buffer_init(terminal->fb);
883 term_resize(terminal);
884 terminal->term->age = 0;
885 term_redraw(terminal);
886}
887
888void term_clear(terminal_t* terminal)
889{
890 tsm_screen_erase_screen(terminal->term->screen, false);
891 term_redraw(terminal);
892}
Dominik Behr83864df2016-04-21 12:35:08 -0700893
894void term_background(void)
895{
896 if (in_background)
897 return;
898 in_background = true;
Dominik Behrda3c0102016-06-08 15:05:38 -0700899 drm_dropmaster(NULL);
Dominik Behr83864df2016-04-21 12:35:08 -0700900 dbus_take_display_ownership();
901}
902
903void term_foreground(void)
904{
Dominik Behrda3c0102016-06-08 15:05:38 -0700905 int ret;
Dominik Behr83864df2016-04-21 12:35:08 -0700906 if (!in_background)
907 return;
908 in_background = false;
909 if (!dbus_release_display_ownership()) {
910 LOG(ERROR, "Chrome did not release master. Frecon will try to steal it.");
911 set_drm_master_relax();
912 }
Dominik Behrda3c0102016-06-08 15:05:38 -0700913
914 ret = drm_setmaster(NULL);
915 if (ret < 0) {
916 /*
917 * In case there is high system load give Chrome some time
918 * and try again. */
919 usleep(500 * 1000);
920 ret = drm_setmaster(NULL);
921 }
922 if (ret < 0)
923 LOG(ERROR, "Could not set master when switching to foreground %m.");
924
Dominik Behr83864df2016-04-21 12:35:08 -0700925 if (hotplug_occured) {
926 hotplug_occured = false;
927 term_monitor_hotplug();
928 }
929}
Dominik Behr1883c042016-04-27 12:31:02 -0700930
931void term_suspend_done(void* ignore)
932{
933 term_monitor_hotplug();
934}