blob: feef50a91f41bd0f0690fe46837fcd0179f7730f [file] [log] [blame]
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -07001/*
2 * Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7#include <ctype.h>
8#include <libtsm.h>
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -07009#include <stdio.h>
Dominik Behr93899452014-08-18 22:16:21 -070010#include <sys/select.h>
David Sodmanbf3f2842014-11-12 08:26:58 -080011#include <sys/types.h>
12#include <sys/wait.h>
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070013
Dominik Behr83864df2016-04-21 12:35:08 -070014#include "dbus.h"
Dominik Behr83010f82016-03-18 18:43:08 -070015#include "fb.h"
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070016#include "font.h"
Dominik Behrd2530902016-05-05 14:01:06 -070017#include "image.h"
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070018#include "input.h"
Dominik Behr83864df2016-04-21 12:35:08 -070019#include "main.h"
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070020#include "shl_pty.h"
21#include "term.h"
22#include "util.h"
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070023
Stéphane Marchesin08c08e72016-01-07 16:44:08 -080024static terminal_t* terminals[MAX_TERMINALS];
Dominik Behr4defb362016-01-13 12:36:14 -080025static uint32_t current_terminal = 0;
Stéphane Marchesin08c08e72016-01-07 16:44:08 -080026
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070027struct term {
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080028 struct tsm_screen* screen;
29 struct tsm_vte* vte;
30 struct shl_pty* pty;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070031 int pty_bridge;
32 int pid;
33 tsm_age_t age;
34 int char_x, char_y;
35 int pitch;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080036 uint32_t* dst_image;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070037};
38
David Sodmanf0a925a2015-05-04 11:19:19 -070039struct _terminal_t {
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080040 uint32_t background;
41 bool background_valid;
Dominik Behr83010f82016-03-18 18:43:08 -070042 fb_t* fb;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080043 struct term* term;
44 bool active;
45 char** exec;
David Sodmanf0a925a2015-05-04 11:19:19 -070046};
47
David Sodman8ef20062015-01-06 09:23:40 -080048
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080049static char* interactive_cmd_line[] = {
David Sodman8ef20062015-01-06 09:23:40 -080050 "/sbin/agetty",
51 "-",
52 "9600",
53 "xterm",
54 NULL
55};
56
57
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080058static char* noninteractive_cmd_line[] = {
David Sodman8ef20062015-01-06 09:23:40 -080059 "/bin/cat",
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);
David Sodman8ef20062015-01-06 09:23:40 -080071 execve(terminal->exec[0], terminal->exec, environ);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070072 exit(1);
73}
74
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080075static int term_draw_cell(struct tsm_screen* screen, uint32_t id,
Stéphane Marchesin8fc13522015-12-14 17:02:28 -080076 const uint32_t* ch, size_t len,
77 unsigned int cwidth, unsigned int posx,
78 unsigned int posy,
79 const struct tsm_screen_attr* attr,
80 tsm_age_t age, void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070081{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080082 terminal_t* terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070083 uint32_t front_color, back_color;
David Sodmanf0a925a2015-05-04 11:19:19 -070084 uint8_t br, bb, bg;
Stéphane Marchesinedece332015-12-14 15:10:58 -080085 uint32_t luminance;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070086
David Sodmanbbcb0522014-09-19 10:34:07 -070087 if (age && terminal->term->age && age <= terminal->term->age)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070088 return 0;
89
David Sodmanf0a925a2015-05-04 11:19:19 -070090 if (terminal->background_valid) {
91 br = (terminal->background >> 16) & 0xFF;
92 bg = (terminal->background >> 8) & 0xFF;
93 bb = (terminal->background) & 0xFF;
Stéphane Marchesinedece332015-12-14 15:10:58 -080094 luminance = (3 * br + bb + 4 * bg) >> 3;
David Sodmanf0a925a2015-05-04 11:19:19 -070095
Stéphane Marchesinedece332015-12-14 15:10:58 -080096 /*
97 * FIXME: black is chosen on a dark background, but it uses the
98 * default color for light backgrounds
99 */
100 if (luminance > 128) {
David Sodmanf0a925a2015-05-04 11:19:19 -0700101 front_color = 0;
102 back_color = terminal->background;
103 } else {
104 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
105 back_color = terminal->background;
106 }
107 } else {
108 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
109 back_color = (attr->br << 16) | (attr->bg << 8) | attr->bb;
110 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700111
112 if (attr->inverse) {
113 uint32_t tmp = front_color;
114 front_color = back_color;
115 back_color = tmp;
116 }
117
118 if (len)
David Sodmanbbcb0522014-09-19 10:34:07 -0700119 font_render(terminal->term->dst_image, posx, posy, terminal->term->pitch, *ch,
120 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700121 else
David Sodmanbbcb0522014-09-19 10:34:07 -0700122 font_fillchar(terminal->term->dst_image, posx, posy, terminal->term->pitch,
123 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700124
125 return 0;
126}
127
Stéphane Marchesine2a46cd2016-01-07 16:45:24 -0800128static void term_redraw(terminal_t* terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700129{
Dominik Behr83010f82016-03-18 18:43:08 -0700130 uint32_t* fb_buffer;
131 fb_buffer = fb_lock(terminal->fb);
132 if (fb_buffer != NULL) {
133 terminal->term->dst_image = fb_buffer;
David Sodmanbbcb0522014-09-19 10:34:07 -0700134 terminal->term->age =
135 tsm_screen_draw(terminal->term->screen, term_draw_cell, terminal);
Dominik Behr83010f82016-03-18 18:43:08 -0700136 fb_unlock(terminal->fb);
David Sodmanbbcb0522014-09-19 10:34:07 -0700137 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700138}
139
David Sodmanbbcb0522014-09-19 10:34:07 -0700140void term_key_event(terminal_t* terminal, uint32_t keysym, int32_t unicode)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700141{
David Sodmanbbcb0522014-09-19 10:34:07 -0700142 if (tsm_vte_handle_keyboard(terminal->term->vte, keysym, 0, 0, unicode))
143 tsm_screen_sb_reset(terminal->term->screen);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700144
David Sodmanbbcb0522014-09-19 10:34:07 -0700145 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700146}
147
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800148static void term_read_cb(struct shl_pty* pty, char* u8, size_t len, void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700149{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800150 terminal_t* terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700151
David Sodmanbbcb0522014-09-19 10:34:07 -0700152 tsm_vte_input(terminal->term->vte, u8, len);
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_write_cb(struct tsm_vte* vte, const char* u8, size_t len,
158 void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700159{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800160 struct term* term = data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700161 int r;
162
163 r = shl_pty_write(term->pty, u8, len);
164 if (r < 0)
David Sodmanbbcb0522014-09-19 10:34:07 -0700165 LOG(ERROR, "OOM in pty-write (%d)", r);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700166
167 shl_pty_dispatch(term->pty);
168}
169
Dominik Behrd2530902016-05-05 14:01:06 -0700170static void term_esc_show_image(terminal_t* terminal, char* params)
171{
172 char* tok;
173 image_t* image;
174 int status;
175
176 image = image_create();
177 if (!image) {
178 LOG(ERROR, "Out of memory when creating an image.\n");
179 return;
180 }
181 for (tok = strtok(params, ";"); tok; tok = strtok(NULL, ";")) {
182 if (strncmp("file=", tok, 5) == 0) {
183 image_set_filename(image, tok + 5);
184 } else if (strncmp("location=", tok, 9) == 0) {
185 uint32_t x, y;
186 if (sscanf(tok + 9, "%u,%u", &x, &y) != 2) {
187 LOG(ERROR, "Error parsing image location.\n");
188 goto done;
189 }
190 image_set_location(image, x, y);
191 } else if (strncmp("offset=", tok, 7) == 0) {
192 int32_t x, y;
193 if (sscanf(tok + 7, "%d,%d", &x, &y) != 2) {
194 LOG(ERROR, "Error parsing image offset.\n");
195 goto done;
196 }
197 image_set_offset(image, x, y);
198 } else if (strncmp("scale=", tok, 6) == 0) {
199 uint32_t s;
200 if (sscanf(tok + 6, "%u", &s) != 1) {
201 LOG(ERROR, "Error parsing image scale.\n");
202 goto done;
203 }
204 if (s == 0)
205 s = image_get_auto_scale(term_getfb(terminal));
206 image_set_scale(image, s);
207 }
208 }
209
210 status = image_load_image_from_file(image);
211 if (status != 0) {
212 LOG(WARNING, "Term ESC image_load_image_from_file %s failed: %d:%s.",
213 image_get_filename(image), status, strerror(status));
214 } else {
215 term_show_image(terminal, image);
216 }
217done:
218 image_destroy(image);
219}
220
221static void term_esc_draw_box(terminal_t* terminal, char* params)
222{
223 char* tok;
224 uint32_t color = 0;
225 uint32_t w = 1;
226 uint32_t h = 1;
227 uint32_t locx, locy;
228 bool use_location = false;
229 int32_t offx, offy;
230 bool use_offset = false;
231 uint32_t scale = 1;
232 uint32_t* buffer;
233 int32_t startx, starty;
234 uint32_t pitch4;
235
236 for (tok = strtok(params, ";"); tok; tok = strtok(NULL, ";")) {
237 if (strncmp("color=", tok, 6) == 0) {
238 color = strtoul(tok + 6, NULL, 0);
239 } else if (strncmp("size=", tok, 5) == 0) {
240 if (sscanf(tok + 5, "%u,%u", &w, &h) != 2) {
241 LOG(ERROR, "Error parsing box size.\n");
242 goto done;
243 }
244 } else if (strncmp("location=", tok, 9) == 0) {
245 if (sscanf(tok + 9, "%u,%u", &locx, &locy) != 2) {
246 LOG(ERROR, "Error parsing box location.\n");
247 goto done;
248 }
249 use_location = true;
250 } else if (strncmp("offset=", tok, 7) == 0) {
251 if (sscanf(tok + 7, "%d,%d", &offx, &offy) != 2) {
252 LOG(ERROR, "Error parsing box offset.\n");
253 goto done;
254 }
255 use_offset = true;
256 } else if (strncmp("scale=", tok, 6) == 0) {
257 if (sscanf(tok + 6, "%u", &scale) != 1) {
258 LOG(ERROR, "Error parsing box scale.\n");
259 goto done;
260 }
261 if (scale == 0)
262 scale = image_get_auto_scale(term_getfb(terminal));
263 }
264 }
265
266 w *= scale;
267 h *= scale;
268 offx *= scale;
269 offy *= scale;
270
271 buffer = fb_lock(terminal->fb);
272 if (buffer == NULL)
273 goto done;
274
275 if (use_offset && use_location) {
276 LOG(WARNING, "Box offset and location set, using location.");
277 use_offset = false;
278 }
279
280 if (use_location) {
281 startx = locx;
282 starty = locy;
283 } else {
284 startx = (fb_getwidth(terminal->fb) - (int32_t)w)/2;
285 starty = (fb_getheight(terminal->fb) - (int32_t)h)/2;
286 }
287
288 if (use_offset) {
289 startx += offx;
290 starty += offy;
291 }
292
293 pitch4 = fb_getpitch(terminal->fb) / 4;
294
295 /* Completely outside buffer, do nothing */
296 if (startx + w <= 0 || startx >= fb_getwidth(terminal->fb))
297 goto done_fb;
298 if (starty + h <= 0 || starty >= fb_getheight(terminal->fb))
299 goto done_fb;
300 /* Make sure we are inside buffer. */
301 if (startx < 0)
302 startx = 0;
303 if (startx + (int32_t)w > fb_getwidth(terminal->fb))
304 w = fb_getwidth(terminal->fb) - startx;
305 if (starty < 0)
306 starty = 0;
307 if (starty + (int32_t)h > fb_getheight(terminal->fb))
308 h = fb_getheight(terminal->fb) - starty;
309
310 for (uint32_t y = 0; y < h; y++) {
311 uint32_t *o = buffer + (starty + y) * pitch4
312 + startx;
313 for (uint32_t x = 0; x < w; x++)
314 o[x] = color;
315 }
316done_fb:
317 fb_unlock(terminal->fb);
318done:
319 ;
320}
321
322static void term_osc_cb(struct tsm_vte *vte, const uint32_t *osc_string,
323 size_t osc_len, void *data)
324{
325 terminal_t* terminal = (terminal_t*)data;
326 size_t i;
327 char *osc;
328
329 for (i = 0; i < osc_len; i++)
330 if (osc_string[i] >= 128)
331 return; /* we only want to deal with ASCII */
332
333 osc = malloc(osc_len + 1);
334 if (!osc) {
335 LOG(WARNING, "Out of memory when processing OSC\n");
336 return;
337 }
338
339 for (i = 0; i < osc_len; i++)
340 osc[i] = (char)osc_string[i];
341 osc[i] = '\0';
342
343 if (strncmp(osc, "image:", 6) == 0)
344 term_esc_show_image(terminal, osc + 6);
345 else if (strncmp(osc, "box:", 4) == 0)
346 term_esc_draw_box(terminal, osc + 4);
347 else
348 LOG(WARNING, "Unknown OSC escape sequence \"%s\", ignoring.", osc);
349
350 free(osc);
351}
352
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800353static const char* sev2str_table[] = {
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700354 "FATAL",
355 "ALERT",
356 "CRITICAL",
357 "ERROR",
358 "WARNING",
359 "NOTICE",
360 "INFO",
361 "DEBUG"
362};
363
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800364static const char* sev2str(unsigned int sev)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700365{
366 if (sev > 7)
367 return "DEBUG";
368
369 return sev2str_table[sev];
370}
371
Dominik Behr00003502014-08-15 16:42:37 -0700372#ifdef __clang__
373__attribute__((__format__ (__printf__, 7, 0)))
374#endif
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800375static void log_tsm(void* data, const char* file, int line, const char* fn,
Stéphane Marchesin8fc13522015-12-14 17:02:28 -0800376 const char* subs, unsigned int sev, const char* format,
377 va_list args)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700378{
379 fprintf(stderr, "%s: %s: ", sev2str(sev), subs);
380 vfprintf(stderr, format, args);
381 fprintf(stderr, "\n");
382}
383
Stéphane Marchesin78310662016-01-06 16:09:39 -0800384static int term_resize(terminal_t* term)
385{
386 uint32_t char_width, char_height;
387 int status;
388
Dominik Behr83010f82016-03-18 18:43:08 -0700389 font_init(fb_getscaling(term->fb));
Stéphane Marchesin78310662016-01-06 16:09:39 -0800390 font_get_size(&char_width, &char_height);
391
Dominik Behr83010f82016-03-18 18:43:08 -0700392 term->term->char_x = fb_getwidth(term->fb) / char_width;
393 term->term->char_y = fb_getheight(term->fb) / char_height;
394 term->term->pitch = fb_getpitch(term->fb);
Stéphane Marchesin78310662016-01-06 16:09:39 -0800395
396 status = tsm_screen_resize(term->term->screen,
397 term->term->char_x, term->term->char_y);
398 if (status < 0) {
399 font_free();
400 return -1;
401 }
402
403 status = shl_pty_resize(term->term->pty, term->term->char_x,
404 term->term->char_y);
405 if (status < 0) {
406 font_free();
407 return -1;
408 }
409
410 return 0;
411}
412
Dominik Behr83010f82016-03-18 18:43:08 -0700413terminal_t* term_init(bool interactive)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700414{
415 const int scrollback_size = 200;
David Sodmanbbcb0522014-09-19 10:34:07 -0700416 int status;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800417 terminal_t* new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700418
David Sodmanbbcb0522014-09-19 10:34:07 -0700419 new_terminal = (terminal_t*)calloc(1, sizeof(*new_terminal));
David Sodmanbf3f2842014-11-12 08:26:58 -0800420 if (!new_terminal)
421 return NULL;
David Sodmanbbcb0522014-09-19 10:34:07 -0700422
David Sodmanf0a925a2015-05-04 11:19:19 -0700423 new_terminal->background_valid = false;
424
Dominik Behr83010f82016-03-18 18:43:08 -0700425 new_terminal->fb = fb_init();
David Sodmanf0a925a2015-05-04 11:19:19 -0700426
Dominik Behr83010f82016-03-18 18:43:08 -0700427 if (!new_terminal->fb) {
David Sodmanbf3f2842014-11-12 08:26:58 -0800428 term_close(new_terminal);
429 return NULL;
430 }
431
432 new_terminal->term = (struct term*)calloc(1, sizeof(*new_terminal->term));
433 if (!new_terminal->term) {
434 term_close(new_terminal);
435 return NULL;
436 }
437
Dominik Behr222936d2016-05-05 13:50:50 -0700438 if (interactive && !command_flags.no_login)
David Sodman8ef20062015-01-06 09:23:40 -0800439 new_terminal->exec = interactive_cmd_line;
440 else
441 new_terminal->exec = noninteractive_cmd_line;
442
David Sodmanbbcb0522014-09-19 10:34:07 -0700443 status = tsm_screen_new(&new_terminal->term->screen,
444 log_tsm, new_terminal->term);
zhuo-hao471f1e22015-08-12 14:55:56 +0800445 if (status < 0) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700446 term_close(new_terminal);
447 return NULL;
448 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700449
David Sodmanbbcb0522014-09-19 10:34:07 -0700450 tsm_screen_set_max_sb(new_terminal->term->screen, scrollback_size);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700451
David Sodmanbbcb0522014-09-19 10:34:07 -0700452 status = tsm_vte_new(&new_terminal->term->vte, new_terminal->term->screen,
453 term_write_cb, new_terminal->term, log_tsm, new_terminal->term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700454
David Sodmanbbcb0522014-09-19 10:34:07 -0700455 if (status < 0) {
456 term_close(new_terminal);
457 return NULL;
458 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700459
Dominik Behrd2530902016-05-05 14:01:06 -0700460 if (command_flags.enable_gfx)
461 tsm_vte_set_osc_cb(new_terminal->term->vte, term_osc_cb, (void *)new_terminal);
462
David Sodmanbbcb0522014-09-19 10:34:07 -0700463 new_terminal->term->pty_bridge = shl_pty_bridge_new();
464 if (new_terminal->term->pty_bridge < 0) {
465 term_close(new_terminal);
466 return NULL;
467 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700468
David Sodmanbbcb0522014-09-19 10:34:07 -0700469 status = shl_pty_open(&new_terminal->term->pty,
Stéphane Marchesin78310662016-01-06 16:09:39 -0800470 term_read_cb, new_terminal, 1, 1);
David Sodman8ef20062015-01-06 09:23:40 -0800471
David Sodmanbbcb0522014-09-19 10:34:07 -0700472 if (status < 0) {
473 term_close(new_terminal);
474 return NULL;
475 } else if (status == 0) {
David Sodman8ef20062015-01-06 09:23:40 -0800476 term_run_child(new_terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700477 exit(1);
478 }
479
David Sodmanbbcb0522014-09-19 10:34:07 -0700480 status = shl_pty_bridge_add(new_terminal->term->pty_bridge, new_terminal->term->pty);
481 if (status) {
482 shl_pty_close(new_terminal->term->pty);
483 term_close(new_terminal);
484 return NULL;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700485 }
486
David Sodmanbbcb0522014-09-19 10:34:07 -0700487 new_terminal->term->pid = shl_pty_get_child(new_terminal->term->pty);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700488
Stéphane Marchesin78310662016-01-06 16:09:39 -0800489 status = term_resize(new_terminal);
Dominik Behr83010f82016-03-18 18:43:08 -0700490
David Sodmanbbcb0522014-09-19 10:34:07 -0700491 if (status < 0) {
492 shl_pty_close(new_terminal->term->pty);
493 term_close(new_terminal);
494 return NULL;
495 }
David Sodman8ef20062015-01-06 09:23:40 -0800496
497 if (interactive)
498 new_terminal->active = true;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700499
David Sodmanbbcb0522014-09-19 10:34:07 -0700500 return new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700501}
502
David Sodman8ef20062015-01-06 09:23:40 -0800503void term_activate(terminal_t* terminal)
504{
Dominik Behr4defb362016-01-13 12:36:14 -0800505 term_set_current_to(terminal);
David Sodman8ef20062015-01-06 09:23:40 -0800506 terminal->active = true;
Dominik Behr83010f82016-03-18 18:43:08 -0700507 fb_setmode(terminal->fb);
David Sodman8ef20062015-01-06 09:23:40 -0800508 term_redraw(terminal);
509}
510
David Sodmanf0a925a2015-05-04 11:19:19 -0700511void term_deactivate(terminal_t* terminal)
512{
513 if (!terminal->active)
514 return;
515
David Sodmanf0a925a2015-05-04 11:19:19 -0700516 terminal->active = false;
David Sodmanf0a925a2015-05-04 11:19:19 -0700517}
518
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800519void term_close(terminal_t* term)
David Sodmanbbcb0522014-09-19 10:34:07 -0700520{
521 if (!term)
522 return;
523
Dominik Behr83010f82016-03-18 18:43:08 -0700524 if (term->fb) {
525 fb_close(term->fb);
526 term->fb = NULL;
David Sodmanbf3f2842014-11-12 08:26:58 -0800527 }
528
David Sodmanbbcb0522014-09-19 10:34:07 -0700529 if (term->term) {
530 free(term->term);
531 term->term = NULL;
532 }
533
Haixia Shi95d680e2015-04-27 20:29:17 -0700534 font_free();
David Sodmanbbcb0522014-09-19 10:34:07 -0700535 free(term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700536}
David Sodmanbf3f2842014-11-12 08:26:58 -0800537
538bool term_is_child_done(terminal_t* terminal)
539{
540 int status;
541 int ret;
542 ret = waitpid(terminal->term->pid, &status, WNOHANG);
543
David Sodmanf0a925a2015-05-04 11:19:19 -0700544 if ((ret == -1) && (errno == ECHILD)) {
545 return false;
546 }
David Sodmanbf3f2842014-11-12 08:26:58 -0800547 return ret != 0;
548}
549
550void term_page_up(terminal_t* terminal)
551{
552 tsm_screen_sb_page_up(terminal->term->screen, 1);
553 term_redraw(terminal);
554}
555
556void term_page_down(terminal_t* terminal)
557{
558 tsm_screen_sb_page_down(terminal->term->screen, 1);
559 term_redraw(terminal);
560}
561
562void term_line_up(terminal_t* terminal)
563{
564 tsm_screen_sb_up(terminal->term->screen, 1);
565 term_redraw(terminal);
566}
567
568void term_line_down(terminal_t* terminal)
569{
570 tsm_screen_sb_down(terminal->term->screen, 1);
571 term_redraw(terminal);
572}
573
574bool term_is_valid(terminal_t* terminal)
575{
576 return ((terminal != NULL) && (terminal->term != NULL));
577}
578
579int term_fd(terminal_t* terminal)
580{
581 if (term_is_valid(terminal))
582 return terminal->term->pty_bridge;
583 else
584 return -1;
585}
586
587void term_dispatch_io(terminal_t* terminal, fd_set* read_set)
588{
589 if (term_is_valid(terminal))
590 if (FD_ISSET(terminal->term->pty_bridge, read_set))
591 shl_pty_bridge_dispatch(terminal->term->pty_bridge, 0);
592}
593
594bool term_exception(terminal_t* terminal, fd_set* exception_set)
595{
596 if (term_is_valid(terminal)) {
597 if (terminal->term->pty_bridge >= 0) {
598 return FD_ISSET(terminal->term->pty_bridge,
599 exception_set);
600 }
601 }
602
603 return false;
604}
605
606bool term_is_active(terminal_t* terminal)
607{
608 if (term_is_valid(terminal))
609 return terminal->active;
610
611 return false;
612}
613
Dominik Behrb1abcba2016-04-14 14:57:21 -0700614void term_add_fds(terminal_t* terminal, fd_set* read_set, fd_set* exception_set, int* maxfd)
David Sodmanbf3f2842014-11-12 08:26:58 -0800615{
616 if (term_is_valid(terminal)) {
617 if (terminal->term->pty_bridge >= 0) {
Dominik Behrd7112672016-01-20 16:59:34 -0800618 *maxfd = MAX(*maxfd, terminal->term->pty_bridge);
David Sodmanbf3f2842014-11-12 08:26:58 -0800619 FD_SET(terminal->term->pty_bridge, read_set);
620 FD_SET(terminal->term->pty_bridge, exception_set);
621 }
622 }
623}
David Sodman8ef20062015-01-06 09:23:40 -0800624
625const char* term_get_ptsname(terminal_t* terminal)
626{
627 return ptsname(shl_pty_get_fd(terminal->term->pty));
628}
David Sodmanf0a925a2015-05-04 11:19:19 -0700629
630void term_set_background(terminal_t* terminal, uint32_t bg)
631{
632 terminal->background = bg;
633 terminal->background_valid = true;
634}
635
636int term_show_image(terminal_t* terminal, image_t* image)
637{
Dominik Behr83010f82016-03-18 18:43:08 -0700638 return image_show(image, terminal->fb);
David Sodmanf0a925a2015-05-04 11:19:19 -0700639}
640
641void term_write_message(terminal_t* terminal, char* message)
642{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800643 FILE* fp;
David Sodmanf0a925a2015-05-04 11:19:19 -0700644
645 fp = fopen(term_get_ptsname(terminal), "w");
646 if (fp) {
647 fputs(message, fp);
648 fclose(fp);
649 }
650}
651
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800652static void term_hide_cursor(terminal_t* terminal)
David Sodmanf0a925a2015-05-04 11:19:19 -0700653{
654 term_write_message(terminal, "\033[?25l");
655}
656
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800657__attribute__ ((unused))
658static void term_show_cursor(terminal_t* terminal)
David Sodmanf0a925a2015-05-04 11:19:19 -0700659{
660 term_write_message(terminal, "\033[?25h");
661}
David Sodman30a94ef2015-07-26 17:37:12 -0700662
Dominik Behr83010f82016-03-18 18:43:08 -0700663fb_t* term_getfb(terminal_t* terminal)
David Sodman30a94ef2015-07-26 17:37:12 -0700664{
Dominik Behr83010f82016-03-18 18:43:08 -0700665 return terminal->fb;
David Sodman30a94ef2015-07-26 17:37:12 -0700666}
Stéphane Marchesin08c08e72016-01-07 16:44:08 -0800667
668terminal_t* term_get_terminal(int num)
669{
670 return terminals[num];
671}
672
673void term_set_terminal(int num, terminal_t* terminal)
674{
675 terminals[num] = terminal;
676}
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800677
Stéphane Marchesin0a7ce422016-01-07 20:45:47 -0800678terminal_t* term_create_term(int vt)
679{
680 terminal_t* terminal;
681
682 terminal = term_get_terminal(vt - 1);
683 if (term_is_active(terminal))
684 return terminal;
685
686 if (terminal == NULL) {
Dominik Behr83010f82016-03-18 18:43:08 -0700687 term_set_terminal(vt - 1, term_init(false));
Stéphane Marchesin0a7ce422016-01-07 20:45:47 -0800688 terminal = term_get_terminal(vt - 1);
689 if (!term_is_valid(terminal)) {
690 LOG(ERROR, "create_term: Term init failed");
691 }
692 }
693
694 return terminal;
695}
696
Dominik Behr83010f82016-03-18 18:43:08 -0700697terminal_t* term_create_splash_term()
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800698{
Dominik Behr83010f82016-03-18 18:43:08 -0700699 terminal_t* splash_terminal = term_init(false);
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800700 term_set_terminal(SPLASH_TERMINAL, splash_terminal);
701
702 // Hide the cursor on the splash screen
703 term_hide_cursor(splash_terminal);
704
705 return splash_terminal;
706}
707
708void term_destroy_splash_term()
709{
710 term_set_terminal(SPLASH_TERMINAL, NULL);
711}
712
Stéphane Marchesin92a297d2016-01-07 20:24:55 -0800713unsigned int term_get_max_terminals()
714{
715 return MAX_STD_TERMINALS;
716}
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800717
Dominik Behr4defb362016-01-13 12:36:14 -0800718void term_set_current(uint32_t t)
719{
720 if (t >= MAX_TERMINALS)
721 LOG(ERROR, "set_current: larger than max");
722 else
723 current_terminal = t;
724}
725
726uint32_t term_get_current(void)
727{
728 return current_terminal;
729}
730
731terminal_t *term_get_current_terminal(void)
732{
733 return terminals[current_terminal];
734}
735
Dominik Behrb1abcba2016-04-14 14:57:21 -0700736void term_set_current_terminal(terminal_t* terminal)
Dominik Behr4defb362016-01-13 12:36:14 -0800737{
738 terminals[current_terminal] = terminal;
739}
740
741void term_set_current_to(terminal_t* terminal)
742{
743 if (!terminal) {
744 terminals[current_terminal] = NULL;
745 current_terminal = 0;
746 return;
747 }
748
749 for (int i = 0; i < MAX_TERMINALS; i++) {
750 if (terminal == terminals[i]) {
751 current_terminal = i;
752 return;
753 }
754 }
755 LOG(ERROR, "set_current_to: terminal not in array");
756}
Dominik Behr01a7a582016-01-28 17:02:21 -0800757
758void term_monitor_hotplug(void)
759{
760 unsigned int t;
761
Dominik Behr83864df2016-04-21 12:35:08 -0700762 if (in_background) {
763 hotplug_occured = true;
764 return;
765 }
766
Dominik Behr83010f82016-03-18 18:43:08 -0700767 if (!drm_rescan())
768 return;
769
Dominik Behr01a7a582016-01-28 17:02:21 -0800770 for (t = 0; t < MAX_TERMINALS; t++) {
Dominik Behr01a7a582016-01-28 17:02:21 -0800771 if (!terminals[t])
772 continue;
Dominik Behr83010f82016-03-18 18:43:08 -0700773 if (!terminals[t]->fb)
Dominik Behr01a7a582016-01-28 17:02:21 -0800774 continue;
Dominik Behr83010f82016-03-18 18:43:08 -0700775 fb_buffer_destroy(terminals[t]->fb);
Dominik Behra3f65712016-04-25 17:42:14 -0700776 font_free();
Dominik Behr83010f82016-03-18 18:43:08 -0700777 }
778
779 for (t = 0; t < MAX_TERMINALS; t++) {
780 if (!terminals[t])
781 continue;
782 if (!terminals[t]->fb)
783 continue;
784 fb_buffer_init(terminals[t]->fb);
785 term_resize(terminals[t]);
786 if (current_terminal == t && terminals[t]->active)
787 fb_setmode(terminals[t]->fb);
788 terminals[t]->term->age = 0;
789 term_redraw(terminals[t]);
Dominik Behr01a7a582016-01-28 17:02:21 -0800790 }
791}
Dominik Behrb1abcba2016-04-14 14:57:21 -0700792
793void term_redrm(terminal_t* terminal)
794{
795 fb_buffer_destroy(terminal->fb);
Dominik Behra3f65712016-04-25 17:42:14 -0700796 font_free();
Dominik Behrb1abcba2016-04-14 14:57:21 -0700797 fb_buffer_init(terminal->fb);
798 term_resize(terminal);
799 terminal->term->age = 0;
800 term_redraw(terminal);
801}
802
803void term_clear(terminal_t* terminal)
804{
805 tsm_screen_erase_screen(terminal->term->screen, false);
806 term_redraw(terminal);
807}
Dominik Behr83864df2016-04-21 12:35:08 -0700808
809void term_background(void)
810{
811 if (in_background)
812 return;
813 in_background = true;
Dominik Behrda3c0102016-06-08 15:05:38 -0700814 drm_dropmaster(NULL);
Dominik Behr83864df2016-04-21 12:35:08 -0700815 dbus_take_display_ownership();
816}
817
818void term_foreground(void)
819{
Dominik Behrda3c0102016-06-08 15:05:38 -0700820 int ret;
Dominik Behr83864df2016-04-21 12:35:08 -0700821 if (!in_background)
822 return;
823 in_background = false;
824 if (!dbus_release_display_ownership()) {
825 LOG(ERROR, "Chrome did not release master. Frecon will try to steal it.");
826 set_drm_master_relax();
827 }
Dominik Behrda3c0102016-06-08 15:05:38 -0700828
829 ret = drm_setmaster(NULL);
830 if (ret < 0) {
831 /*
832 * In case there is high system load give Chrome some time
833 * and try again. */
834 usleep(500 * 1000);
835 ret = drm_setmaster(NULL);
836 }
837 if (ret < 0)
838 LOG(ERROR, "Could not set master when switching to foreground %m.");
839
Dominik Behr83864df2016-04-21 12:35:08 -0700840 if (hotplug_occured) {
841 hotplug_occured = false;
842 term_monitor_hotplug();
843 }
844}
Dominik Behr1883c042016-04-27 12:31:02 -0700845
846void term_suspend_done(void* ignore)
847{
848 term_monitor_hotplug();
849}