blob: 7f52f8e2ef3446fc85453f2675e04d205595f9a7 [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
14#include "font.h"
15#include "input.h"
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070016#include "shl_pty.h"
17#include "term.h"
18#include "util.h"
19#include "video.h"
20
Stéphane Marchesin08c08e72016-01-07 16:44:08 -080021static terminal_t* terminals[MAX_TERMINALS];
22
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070023struct term {
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080024 struct tsm_screen* screen;
25 struct tsm_vte* vte;
26 struct shl_pty* pty;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070027 int pty_bridge;
28 int pid;
29 tsm_age_t age;
30 int char_x, char_y;
31 int pitch;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080032 uint32_t* dst_image;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070033};
34
David Sodmanf0a925a2015-05-04 11:19:19 -070035struct _terminal_t {
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080036 uint32_t background;
37 bool background_valid;
38 video_t* video;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080039 struct term* term;
40 bool active;
41 char** exec;
David Sodmanf0a925a2015-05-04 11:19:19 -070042};
43
David Sodman8ef20062015-01-06 09:23:40 -080044
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080045static char* interactive_cmd_line[] = {
David Sodman8ef20062015-01-06 09:23:40 -080046 "/sbin/agetty",
47 "-",
48 "9600",
49 "xterm",
50 NULL
51};
52
53
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080054static char* noninteractive_cmd_line[] = {
David Sodman8ef20062015-01-06 09:23:40 -080055 "/bin/cat",
56 NULL
57};
58
59
60static void __attribute__ ((noreturn)) term_run_child(terminal_t* terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070061{
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070062 /* XXX figure out how to fix "top" for xterm-256color */
63 setenv("TERM", "xterm", 1);
David Sodman8ef20062015-01-06 09:23:40 -080064 execve(terminal->exec[0], terminal->exec, environ);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070065 exit(1);
66}
67
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080068static int term_draw_cell(struct tsm_screen* screen, uint32_t id,
Stéphane Marchesin8fc13522015-12-14 17:02:28 -080069 const uint32_t* ch, size_t len,
70 unsigned int cwidth, unsigned int posx,
71 unsigned int posy,
72 const struct tsm_screen_attr* attr,
73 tsm_age_t age, void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070074{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080075 terminal_t* terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070076 uint32_t front_color, back_color;
David Sodmanf0a925a2015-05-04 11:19:19 -070077 uint8_t br, bb, bg;
Stéphane Marchesinedece332015-12-14 15:10:58 -080078 uint32_t luminance;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070079
David Sodmanbbcb0522014-09-19 10:34:07 -070080 if (age && terminal->term->age && age <= terminal->term->age)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070081 return 0;
82
David Sodmanf0a925a2015-05-04 11:19:19 -070083 if (terminal->background_valid) {
84 br = (terminal->background >> 16) & 0xFF;
85 bg = (terminal->background >> 8) & 0xFF;
86 bb = (terminal->background) & 0xFF;
Stéphane Marchesinedece332015-12-14 15:10:58 -080087 luminance = (3 * br + bb + 4 * bg) >> 3;
David Sodmanf0a925a2015-05-04 11:19:19 -070088
Stéphane Marchesinedece332015-12-14 15:10:58 -080089 /*
90 * FIXME: black is chosen on a dark background, but it uses the
91 * default color for light backgrounds
92 */
93 if (luminance > 128) {
David Sodmanf0a925a2015-05-04 11:19:19 -070094 front_color = 0;
95 back_color = terminal->background;
96 } else {
97 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
98 back_color = terminal->background;
99 }
100 } else {
101 front_color = (attr->fr << 16) | (attr->fg << 8) | attr->fb;
102 back_color = (attr->br << 16) | (attr->bg << 8) | attr->bb;
103 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700104
105 if (attr->inverse) {
106 uint32_t tmp = front_color;
107 front_color = back_color;
108 back_color = tmp;
109 }
110
111 if (len)
David Sodmanbbcb0522014-09-19 10:34:07 -0700112 font_render(terminal->term->dst_image, posx, posy, terminal->term->pitch, *ch,
113 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700114 else
David Sodmanbbcb0522014-09-19 10:34:07 -0700115 font_fillchar(terminal->term->dst_image, posx, posy, terminal->term->pitch,
116 front_color, back_color);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700117
118 return 0;
119}
120
Stéphane Marchesine2a46cd2016-01-07 16:45:24 -0800121static void term_redraw(terminal_t* terminal)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700122{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800123 uint32_t* video_buffer;
David Sodmanbbcb0522014-09-19 10:34:07 -0700124 video_buffer = video_lock(terminal->video);
125 if (video_buffer != NULL) {
126 terminal->term->dst_image = video_buffer;
127 terminal->term->age =
128 tsm_screen_draw(terminal->term->screen, term_draw_cell, terminal);
129 video_unlock(terminal->video);
130 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700131}
132
David Sodmanbbcb0522014-09-19 10:34:07 -0700133void term_key_event(terminal_t* terminal, uint32_t keysym, int32_t unicode)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700134{
David Sodmanbbcb0522014-09-19 10:34:07 -0700135 if (tsm_vte_handle_keyboard(terminal->term->vte, keysym, 0, 0, unicode))
136 tsm_screen_sb_reset(terminal->term->screen);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700137
David Sodmanbbcb0522014-09-19 10:34:07 -0700138 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700139}
140
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800141static void term_read_cb(struct shl_pty* pty, char* u8, size_t len, void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700142{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800143 terminal_t* terminal = (terminal_t*)data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700144
David Sodmanbbcb0522014-09-19 10:34:07 -0700145 tsm_vte_input(terminal->term->vte, u8, len);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700146
David Sodmanbbcb0522014-09-19 10:34:07 -0700147 term_redraw(terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700148}
149
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800150static void term_write_cb(struct tsm_vte* vte, const char* u8, size_t len,
151 void* data)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700152{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800153 struct term* term = data;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700154 int r;
155
156 r = shl_pty_write(term->pty, u8, len);
157 if (r < 0)
David Sodmanbbcb0522014-09-19 10:34:07 -0700158 LOG(ERROR, "OOM in pty-write (%d)", r);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700159
160 shl_pty_dispatch(term->pty);
161}
162
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800163static const char* sev2str_table[] = {
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700164 "FATAL",
165 "ALERT",
166 "CRITICAL",
167 "ERROR",
168 "WARNING",
169 "NOTICE",
170 "INFO",
171 "DEBUG"
172};
173
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800174static const char* sev2str(unsigned int sev)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700175{
176 if (sev > 7)
177 return "DEBUG";
178
179 return sev2str_table[sev];
180}
181
Dominik Behr00003502014-08-15 16:42:37 -0700182#ifdef __clang__
183__attribute__((__format__ (__printf__, 7, 0)))
184#endif
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800185static void log_tsm(void* data, const char* file, int line, const char* fn,
Stéphane Marchesin8fc13522015-12-14 17:02:28 -0800186 const char* subs, unsigned int sev, const char* format,
187 va_list args)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700188{
189 fprintf(stderr, "%s: %s: ", sev2str(sev), subs);
190 vfprintf(stderr, format, args);
191 fprintf(stderr, "\n");
192}
193
Stéphane Marchesin78310662016-01-06 16:09:39 -0800194static int term_resize(terminal_t* term)
195{
196 uint32_t char_width, char_height;
197 int status;
198
199 font_init(video_getscaling(term->video));
200 font_get_size(&char_width, &char_height);
201
202 term->term->char_x = video_getwidth(term->video) / char_width;
203 term->term->char_y = video_getheight(term->video) / char_height;
204 term->term->pitch = video_getpitch(term->video);
205
206 status = tsm_screen_resize(term->term->screen,
207 term->term->char_x, term->term->char_y);
208 if (status < 0) {
209 font_free();
210 return -1;
211 }
212
213 status = shl_pty_resize(term->term->pty, term->term->char_x,
214 term->term->char_y);
215 if (status < 0) {
216 font_free();
217 return -1;
218 }
219
220 return 0;
221}
222
David Sodmanf0a925a2015-05-04 11:19:19 -0700223terminal_t* term_init(bool interactive, video_t* video)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700224{
225 const int scrollback_size = 200;
David Sodmanbbcb0522014-09-19 10:34:07 -0700226 int status;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800227 terminal_t* new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700228
David Sodmanbbcb0522014-09-19 10:34:07 -0700229 new_terminal = (terminal_t*)calloc(1, sizeof(*new_terminal));
David Sodmanbf3f2842014-11-12 08:26:58 -0800230 if (!new_terminal)
231 return NULL;
David Sodmanbbcb0522014-09-19 10:34:07 -0700232
David Sodmanf0a925a2015-05-04 11:19:19 -0700233 new_terminal->background_valid = false;
234
David Sodman30a94ef2015-07-26 17:37:12 -0700235 if (video) {
David Sodmanf0a925a2015-05-04 11:19:19 -0700236 new_terminal->video = video;
David Sodman30a94ef2015-07-26 17:37:12 -0700237 video_addref(video);
238 }
David Sodmanf0a925a2015-05-04 11:19:19 -0700239 else
240 new_terminal->video = video_init();
241
David Sodmanbf3f2842014-11-12 08:26:58 -0800242 if (!new_terminal->video) {
243 term_close(new_terminal);
244 return NULL;
245 }
246
247 new_terminal->term = (struct term*)calloc(1, sizeof(*new_terminal->term));
248 if (!new_terminal->term) {
249 term_close(new_terminal);
250 return NULL;
251 }
252
David Sodman8ef20062015-01-06 09:23:40 -0800253 if (interactive)
254 new_terminal->exec = interactive_cmd_line;
255 else
256 new_terminal->exec = noninteractive_cmd_line;
257
David Sodmanbbcb0522014-09-19 10:34:07 -0700258 status = tsm_screen_new(&new_terminal->term->screen,
259 log_tsm, new_terminal->term);
zhuo-hao471f1e22015-08-12 14:55:56 +0800260 if (status < 0) {
David Sodmanbbcb0522014-09-19 10:34:07 -0700261 term_close(new_terminal);
262 return NULL;
263 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700264
David Sodmanbbcb0522014-09-19 10:34:07 -0700265 tsm_screen_set_max_sb(new_terminal->term->screen, scrollback_size);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700266
David Sodmanbbcb0522014-09-19 10:34:07 -0700267 status = tsm_vte_new(&new_terminal->term->vte, new_terminal->term->screen,
268 term_write_cb, new_terminal->term, log_tsm, new_terminal->term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700269
David Sodmanbbcb0522014-09-19 10:34:07 -0700270 if (status < 0) {
271 term_close(new_terminal);
272 return NULL;
273 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700274
David Sodmanbbcb0522014-09-19 10:34:07 -0700275 new_terminal->term->pty_bridge = shl_pty_bridge_new();
276 if (new_terminal->term->pty_bridge < 0) {
277 term_close(new_terminal);
278 return NULL;
279 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700280
David Sodmanbbcb0522014-09-19 10:34:07 -0700281 status = shl_pty_open(&new_terminal->term->pty,
Stéphane Marchesin78310662016-01-06 16:09:39 -0800282 term_read_cb, new_terminal, 1, 1);
David Sodman8ef20062015-01-06 09:23:40 -0800283
David Sodmanbbcb0522014-09-19 10:34:07 -0700284 if (status < 0) {
285 term_close(new_terminal);
286 return NULL;
287 } else if (status == 0) {
David Sodman8ef20062015-01-06 09:23:40 -0800288 term_run_child(new_terminal);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700289 exit(1);
290 }
291
David Sodmanbbcb0522014-09-19 10:34:07 -0700292 status = shl_pty_bridge_add(new_terminal->term->pty_bridge, new_terminal->term->pty);
293 if (status) {
294 shl_pty_close(new_terminal->term->pty);
295 term_close(new_terminal);
296 return NULL;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700297 }
298
David Sodmanbbcb0522014-09-19 10:34:07 -0700299 new_terminal->term->pid = shl_pty_get_child(new_terminal->term->pty);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700300
Stéphane Marchesin78310662016-01-06 16:09:39 -0800301 status = term_resize(new_terminal);
David Sodmanbbcb0522014-09-19 10:34:07 -0700302 if (status < 0) {
303 shl_pty_close(new_terminal->term->pty);
304 term_close(new_terminal);
305 return NULL;
306 }
David Sodman8ef20062015-01-06 09:23:40 -0800307
308 if (interactive)
309 new_terminal->active = true;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700310
David Sodmanbbcb0522014-09-19 10:34:07 -0700311 return new_terminal;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700312}
313
David Sodman8ef20062015-01-06 09:23:40 -0800314void term_activate(terminal_t* terminal)
315{
David Sodman8ef20062015-01-06 09:23:40 -0800316 input_set_current(terminal);
317 terminal->active = true;
318 video_setmode(terminal->video);
319 term_redraw(terminal);
320}
321
David Sodmanf0a925a2015-05-04 11:19:19 -0700322void term_deactivate(terminal_t* terminal)
323{
324 if (!terminal->active)
325 return;
326
David Sodmanf0a925a2015-05-04 11:19:19 -0700327 terminal->active = false;
328 video_release(terminal->video);
329}
330
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800331void term_close(terminal_t* term)
David Sodmanbbcb0522014-09-19 10:34:07 -0700332{
333 if (!term)
334 return;
335
David Sodmanbf3f2842014-11-12 08:26:58 -0800336 if (term->video) {
337 video_close(term->video);
338 term->video = NULL;
339 }
340
David Sodmanbbcb0522014-09-19 10:34:07 -0700341 if (term->term) {
342 free(term->term);
343 term->term = NULL;
344 }
345
Haixia Shi95d680e2015-04-27 20:29:17 -0700346 font_free();
David Sodmanbbcb0522014-09-19 10:34:07 -0700347 free(term);
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700348}
David Sodmanbf3f2842014-11-12 08:26:58 -0800349
350bool term_is_child_done(terminal_t* terminal)
351{
352 int status;
353 int ret;
354 ret = waitpid(terminal->term->pid, &status, WNOHANG);
355
David Sodmanf0a925a2015-05-04 11:19:19 -0700356 if ((ret == -1) && (errno == ECHILD)) {
357 return false;
358 }
David Sodmanbf3f2842014-11-12 08:26:58 -0800359 return ret != 0;
360}
361
362void term_page_up(terminal_t* terminal)
363{
364 tsm_screen_sb_page_up(terminal->term->screen, 1);
365 term_redraw(terminal);
366}
367
368void term_page_down(terminal_t* terminal)
369{
370 tsm_screen_sb_page_down(terminal->term->screen, 1);
371 term_redraw(terminal);
372}
373
374void term_line_up(terminal_t* terminal)
375{
376 tsm_screen_sb_up(terminal->term->screen, 1);
377 term_redraw(terminal);
378}
379
380void term_line_down(terminal_t* terminal)
381{
382 tsm_screen_sb_down(terminal->term->screen, 1);
383 term_redraw(terminal);
384}
385
386bool term_is_valid(terminal_t* terminal)
387{
388 return ((terminal != NULL) && (terminal->term != NULL));
389}
390
391int term_fd(terminal_t* terminal)
392{
393 if (term_is_valid(terminal))
394 return terminal->term->pty_bridge;
395 else
396 return -1;
397}
398
399void term_dispatch_io(terminal_t* terminal, fd_set* read_set)
400{
401 if (term_is_valid(terminal))
402 if (FD_ISSET(terminal->term->pty_bridge, read_set))
403 shl_pty_bridge_dispatch(terminal->term->pty_bridge, 0);
404}
405
406bool term_exception(terminal_t* terminal, fd_set* exception_set)
407{
408 if (term_is_valid(terminal)) {
409 if (terminal->term->pty_bridge >= 0) {
410 return FD_ISSET(terminal->term->pty_bridge,
411 exception_set);
412 }
413 }
414
415 return false;
416}
417
418bool term_is_active(terminal_t* terminal)
419{
420 if (term_is_valid(terminal))
421 return terminal->active;
422
423 return false;
424}
425
Stéphane Marchesin905a6f22016-01-07 22:02:07 -0800426int term_add_fds(terminal_t* terminal, fd_set* read_set, fd_set* exception_set)
David Sodmanbf3f2842014-11-12 08:26:58 -0800427{
Stéphane Marchesin905a6f22016-01-07 22:02:07 -0800428 int maxfd = 0;
429
David Sodmanbf3f2842014-11-12 08:26:58 -0800430 if (term_is_valid(terminal)) {
431 if (terminal->term->pty_bridge >= 0) {
Stéphane Marchesin905a6f22016-01-07 22:02:07 -0800432 maxfd = MAX(maxfd, terminal->term->pty_bridge);
David Sodmanbf3f2842014-11-12 08:26:58 -0800433 FD_SET(terminal->term->pty_bridge, read_set);
434 FD_SET(terminal->term->pty_bridge, exception_set);
435 }
436 }
Stéphane Marchesin905a6f22016-01-07 22:02:07 -0800437 return maxfd;
David Sodmanbf3f2842014-11-12 08:26:58 -0800438}
David Sodman8ef20062015-01-06 09:23:40 -0800439
440const char* term_get_ptsname(terminal_t* terminal)
441{
442 return ptsname(shl_pty_get_fd(terminal->term->pty));
443}
David Sodmanf0a925a2015-05-04 11:19:19 -0700444
445void term_set_background(terminal_t* terminal, uint32_t bg)
446{
447 terminal->background = bg;
448 terminal->background_valid = true;
449}
450
451int term_show_image(terminal_t* terminal, image_t* image)
452{
453 return image_show(image, terminal->video);
454}
455
456void term_write_message(terminal_t* terminal, char* message)
457{
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800458 FILE* fp;
David Sodmanf0a925a2015-05-04 11:19:19 -0700459
460 fp = fopen(term_get_ptsname(terminal), "w");
461 if (fp) {
462 fputs(message, fp);
463 fclose(fp);
464 }
465}
466
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800467static void term_hide_cursor(terminal_t* terminal)
David Sodmanf0a925a2015-05-04 11:19:19 -0700468{
469 term_write_message(terminal, "\033[?25l");
470}
471
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800472__attribute__ ((unused))
473static void term_show_cursor(terminal_t* terminal)
David Sodmanf0a925a2015-05-04 11:19:19 -0700474{
475 term_write_message(terminal, "\033[?25h");
476}
David Sodman30a94ef2015-07-26 17:37:12 -0700477
478video_t* term_getvideo(terminal_t* terminal)
479{
480 return terminal->video;
481}
Stéphane Marchesin08c08e72016-01-07 16:44:08 -0800482
483terminal_t* term_get_terminal(int num)
484{
485 return terminals[num];
486}
487
488void term_set_terminal(int num, terminal_t* terminal)
489{
490 terminals[num] = terminal;
491}
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800492
Stéphane Marchesin0a7ce422016-01-07 20:45:47 -0800493terminal_t* term_create_term(int vt)
494{
495 terminal_t* terminal;
496
497 terminal = term_get_terminal(vt - 1);
498 if (term_is_active(terminal))
499 return terminal;
500
501 if (terminal == NULL) {
502 term_set_terminal(vt - 1, term_init(false, NULL));
503 terminal = term_get_terminal(vt - 1);
504 if (!term_is_valid(terminal)) {
505 LOG(ERROR, "create_term: Term init failed");
506 }
507 }
508
509 return terminal;
510}
511
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800512terminal_t* term_create_splash_term(video_t* video)
513{
514 terminal_t* splash_terminal = term_init(false, video);
515 term_set_terminal(SPLASH_TERMINAL, splash_terminal);
516
517 // Hide the cursor on the splash screen
518 term_hide_cursor(splash_terminal);
519
520 return splash_terminal;
521}
522
523void term_destroy_splash_term()
524{
525 term_set_terminal(SPLASH_TERMINAL, NULL);
526}
527
Stéphane Marchesin92a297d2016-01-07 20:24:55 -0800528unsigned int term_get_max_terminals()
529{
530 return MAX_STD_TERMINALS;
531}
Stéphane Marchesinf75c8512016-01-07 16:53:21 -0800532