2 * Copyright (c) 2012 Petr Koupy
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 #include <io/chargrid.h>
40 #include <gfx/font-8x16.h>
41 #include <io/con_srv.h>
42 #include <io/concaps.h>
43 #include <io/console.h>
46 #include <adt/prodcons.h>
53 #define NAMESPACE "vterm"
55 #define LOCFS_MOUNT_POINT "/loc"
57 #define APP_GETTERM "/app/getterm"
60 (CONSOLE_CAP_STYLE | CONSOLE_CAP_INDEXED | CONSOLE_CAP_RGB)
62 static LIST_INITIALIZE(terms
);
64 static errno_t
term_open(con_srvs_t
*, con_srv_t
*);
65 static errno_t
term_close(con_srv_t
*);
66 static errno_t
term_read(con_srv_t
*, void *, size_t, size_t *);
67 static errno_t
term_write(con_srv_t
*, void *, size_t, size_t *);
68 static void term_sync(con_srv_t
*);
69 static void term_clear(con_srv_t
*);
70 static void term_set_pos(con_srv_t
*, sysarg_t col
, sysarg_t row
);
71 static errno_t
term_get_pos(con_srv_t
*, sysarg_t
*, sysarg_t
*);
72 static errno_t
term_get_size(con_srv_t
*, sysarg_t
*, sysarg_t
*);
73 static errno_t
term_get_color_cap(con_srv_t
*, console_caps_t
*);
74 static void term_set_style(con_srv_t
*, console_style_t
);
75 static void term_set_color(con_srv_t
*, console_color_t
, console_color_t
,
76 console_color_attr_t
);
77 static void term_set_rgb_color(con_srv_t
*, pixel_t
, pixel_t
);
78 static void term_set_cursor_visibility(con_srv_t
*, bool);
79 static errno_t
term_get_event(con_srv_t
*, cons_event_t
*);
81 static con_ops_t con_ops
= {
88 .set_pos
= term_set_pos
,
89 .get_pos
= term_get_pos
,
90 .get_size
= term_get_size
,
91 .get_color_cap
= term_get_color_cap
,
92 .set_style
= term_set_style
,
93 .set_color
= term_set_color
,
94 .set_rgb_color
= term_set_rgb_color
,
95 .set_cursor_visibility
= term_set_cursor_visibility
,
96 .get_event
= term_get_event
99 static terminal_t
*srv_to_terminal(con_srv_t
*srv
)
101 return srv
->srvs
->sarg
;
104 static void getterm(const char *svc
, const char *app
)
106 task_spawnl(NULL
, NULL
, APP_GETTERM
, APP_GETTERM
, svc
,
107 LOCFS_MOUNT_POINT
, "--msg", "--wait", "--", app
, NULL
);
110 static pixel_t color_table
[16] = {
111 [COLOR_BLACK
] = PIXEL(255, 0, 0, 0),
112 [COLOR_BLUE
] = PIXEL(255, 0, 0, 240),
113 [COLOR_GREEN
] = PIXEL(255, 0, 240, 0),
114 [COLOR_CYAN
] = PIXEL(255, 0, 240, 240),
115 [COLOR_RED
] = PIXEL(255, 240, 0, 0),
116 [COLOR_MAGENTA
] = PIXEL(255, 240, 0, 240),
117 [COLOR_YELLOW
] = PIXEL(255, 240, 240, 0),
118 [COLOR_WHITE
] = PIXEL(255, 240, 240, 240),
120 [COLOR_BLACK
+ 8] = PIXEL(255, 0, 0, 0),
121 [COLOR_BLUE
+ 8] = PIXEL(255, 0, 0, 255),
122 [COLOR_GREEN
+ 8] = PIXEL(255, 0, 255, 0),
123 [COLOR_CYAN
+ 8] = PIXEL(255, 0, 255, 255),
124 [COLOR_RED
+ 8] = PIXEL(255, 255, 0, 0),
125 [COLOR_MAGENTA
+ 8] = PIXEL(255, 255, 0, 255),
126 [COLOR_YELLOW
+ 8] = PIXEL(255, 255, 255, 0),
127 [COLOR_WHITE
+ 8] = PIXEL(255, 255, 255, 255),
130 static inline void attrs_rgb(char_attrs_t attrs
, pixel_t
*bgcolor
, pixel_t
*fgcolor
)
132 switch (attrs
.type
) {
133 case CHAR_ATTR_STYLE
:
134 switch (attrs
.val
.style
) {
136 *bgcolor
= color_table
[COLOR_WHITE
];
137 *fgcolor
= color_table
[COLOR_BLACK
];
140 *bgcolor
= color_table
[COLOR_WHITE
];
141 *fgcolor
= color_table
[COLOR_RED
];
144 *bgcolor
= color_table
[COLOR_BLACK
];
145 *fgcolor
= color_table
[COLOR_WHITE
];
148 *bgcolor
= color_table
[COLOR_RED
];
149 *fgcolor
= color_table
[COLOR_WHITE
];
153 case CHAR_ATTR_INDEX
:
154 *bgcolor
= color_table
[(attrs
.val
.index
.bgcolor
& 7) |
155 ((attrs
.val
.index
.attr
& CATTR_BRIGHT
) ? 8 : 0)];
156 *fgcolor
= color_table
[(attrs
.val
.index
.fgcolor
& 7) |
157 ((attrs
.val
.index
.attr
& CATTR_BRIGHT
) ? 8 : 0)];
160 *bgcolor
= 0xff000000 | attrs
.val
.rgb
.bgcolor
;
161 *fgcolor
= 0xff000000 | attrs
.val
.rgb
.fgcolor
;
166 static void term_update_char(terminal_t
*term
, surface_t
*surface
,
167 sysarg_t sx
, sysarg_t sy
, sysarg_t col
, sysarg_t row
)
170 chargrid_charfield_at(term
->backbuf
, col
, row
);
172 bool inverted
= chargrid_cursor_at(term
->backbuf
, col
, row
);
174 sysarg_t bx
= sx
+ (col
* FONT_WIDTH
);
175 sysarg_t by
= sy
+ (row
* FONT_SCANLINES
);
181 attrs_rgb(field
->attrs
, &fgcolor
, &bgcolor
);
183 attrs_rgb(field
->attrs
, &bgcolor
, &fgcolor
);
185 // FIXME: Glyph type should be actually uint32_t
186 // for full UTF-32 coverage.
188 uint16_t glyph
= fb_font_glyph(field
->ch
, NULL
);
190 for (unsigned int y
= 0; y
< FONT_SCANLINES
; y
++) {
191 pixel_t
*dst
= pixelmap_pixel_at(
192 surface_pixmap_access(surface
), bx
, by
+ y
);
193 pixel_t
*dst_max
= pixelmap_pixel_at(
194 surface_pixmap_access(surface
), bx
+ FONT_WIDTH
- 1, by
+ y
);
195 if (!dst
|| !dst_max
) continue;
196 int count
= FONT_WIDTH
;
197 while (count
-- != 0) {
198 *dst
++ = (fb_font
[glyph
][y
] & (1 << count
)) ? fgcolor
: bgcolor
;
201 surface_add_damaged_region(surface
, bx
, by
, FONT_WIDTH
, FONT_SCANLINES
);
204 static bool term_update_scroll(terminal_t
*term
, surface_t
*surface
,
205 sysarg_t sx
, sysarg_t sy
)
207 sysarg_t top_row
= chargrid_get_top_row(term
->frontbuf
);
209 if (term
->top_row
== top_row
)
212 term
->top_row
= top_row
;
214 for (sysarg_t row
= 0; row
< term
->rows
; row
++) {
215 for (sysarg_t col
= 0; col
< term
->cols
; col
++) {
216 charfield_t
*front_field
=
217 chargrid_charfield_at(term
->frontbuf
, col
, row
);
218 charfield_t
*back_field
=
219 chargrid_charfield_at(term
->backbuf
, col
, row
);
222 if (front_field
->ch
!= back_field
->ch
) {
223 back_field
->ch
= front_field
->ch
;
227 if (!attrs_same(front_field
->attrs
, back_field
->attrs
)) {
228 back_field
->attrs
= front_field
->attrs
;
232 front_field
->flags
&= ~CHAR_FLAG_DIRTY
;
235 term_update_char(term
, surface
, sx
, sy
, col
, row
);
242 static bool term_update_cursor(terminal_t
*term
, surface_t
*surface
,
243 sysarg_t sx
, sysarg_t sy
)
249 chargrid_get_cursor(term
->frontbuf
, &front_col
, &front_row
);
253 chargrid_get_cursor(term
->backbuf
, &back_col
, &back_row
);
255 bool front_visibility
=
256 chargrid_get_cursor_visibility(term
->frontbuf
) &&
257 term
->widget
.window
->is_focused
;
258 bool back_visibility
=
259 chargrid_get_cursor_visibility(term
->backbuf
);
261 if (front_visibility
!= back_visibility
) {
262 chargrid_set_cursor_visibility(term
->backbuf
,
264 term_update_char(term
, surface
, sx
, sy
, back_col
, back_row
);
268 if ((front_col
!= back_col
) || (front_row
!= back_row
)) {
269 chargrid_set_cursor(term
->backbuf
, front_col
, front_row
);
270 term_update_char(term
, surface
, sx
, sy
, back_col
, back_row
);
271 term_update_char(term
, surface
, sx
, sy
, front_col
, front_row
);
278 static void term_update(terminal_t
*term
)
280 fibril_mutex_lock(&term
->mtx
);
282 surface_t
*surface
= window_claim(term
->widget
.window
);
284 window_yield(term
->widget
.window
);
285 fibril_mutex_unlock(&term
->mtx
);
290 sysarg_t sx
= term
->widget
.hpos
;
291 sysarg_t sy
= term
->widget
.vpos
;
293 if (term_update_scroll(term
, surface
, sx
, sy
)) {
296 for (sysarg_t y
= 0; y
< term
->rows
; y
++) {
297 for (sysarg_t x
= 0; x
< term
->cols
; x
++) {
298 charfield_t
*front_field
=
299 chargrid_charfield_at(term
->frontbuf
, x
, y
);
300 charfield_t
*back_field
=
301 chargrid_charfield_at(term
->backbuf
, x
, y
);
304 if ((front_field
->flags
& CHAR_FLAG_DIRTY
) ==
306 if (front_field
->ch
!= back_field
->ch
) {
307 back_field
->ch
= front_field
->ch
;
311 if (!attrs_same(front_field
->attrs
,
312 back_field
->attrs
)) {
313 back_field
->attrs
= front_field
->attrs
;
317 front_field
->flags
&= ~CHAR_FLAG_DIRTY
;
321 term_update_char(term
, surface
, sx
, sy
, x
, y
);
328 if (term_update_cursor(term
, surface
, sx
, sy
))
331 window_yield(term
->widget
.window
);
334 window_damage(term
->widget
.window
);
336 fibril_mutex_unlock(&term
->mtx
);
339 static void term_damage(terminal_t
*term
)
341 fibril_mutex_lock(&term
->mtx
);
343 surface_t
*surface
= window_claim(term
->widget
.window
);
345 window_yield(term
->widget
.window
);
346 fibril_mutex_unlock(&term
->mtx
);
350 sysarg_t sx
= term
->widget
.hpos
;
351 sysarg_t sy
= term
->widget
.vpos
;
353 if (!term_update_scroll(term
, surface
, sx
, sy
)) {
354 for (sysarg_t y
= 0; y
< term
->rows
; y
++) {
355 for (sysarg_t x
= 0; x
< term
->cols
; x
++) {
356 charfield_t
*front_field
=
357 chargrid_charfield_at(term
->frontbuf
, x
, y
);
358 charfield_t
*back_field
=
359 chargrid_charfield_at(term
->backbuf
, x
, y
);
361 back_field
->ch
= front_field
->ch
;
362 back_field
->attrs
= front_field
->attrs
;
363 front_field
->flags
&= ~CHAR_FLAG_DIRTY
;
365 term_update_char(term
, surface
, sx
, sy
, x
, y
);
370 term_update_cursor(term
, surface
, sx
, sy
);
372 window_yield(term
->widget
.window
);
373 window_damage(term
->widget
.window
);
375 fibril_mutex_unlock(&term
->mtx
);
378 static errno_t
term_open(con_srvs_t
*srvs
, con_srv_t
*srv
)
383 static errno_t
term_close(con_srv_t
*srv
)
388 static errno_t
term_read(con_srv_t
*srv
, void *buf
, size_t size
, size_t *nread
)
390 terminal_t
*term
= srv_to_terminal(srv
);
395 * Read input from keyboard and copy it to the buffer.
396 * We need to handle situation when wchar is split by 2 following
400 /* Copy to the buffer remaining characters. */
401 while ((pos
< size
) && (term
->char_remains_len
> 0)) {
402 bbuf
[pos
] = term
->char_remains
[0];
405 /* Unshift the array. */
406 for (size_t i
= 1; i
< term
->char_remains_len
; i
++)
407 term
->char_remains
[i
- 1] = term
->char_remains
[i
];
409 term
->char_remains_len
--;
412 /* Still not enough? Then get another key from the queue. */
414 link_t
*link
= prodcons_consume(&term
->input_pc
);
415 cons_event_t
*event
= list_get_instance(link
, cons_event_t
, link
);
417 /* Accept key presses of printable chars only. */
418 if (event
->type
== CEV_KEY
&& event
->ev
.key
.type
== KEY_PRESS
&&
419 event
->ev
.key
.c
!= 0) {
425 wstr_to_str(term
->char_remains
, UTF8_CHAR_BUFFER_SIZE
, tmp
);
426 term
->char_remains_len
= str_size(term
->char_remains
);
437 static void term_write_char(terminal_t
*term
, wchar_t ch
)
439 sysarg_t updated
= 0;
441 fibril_mutex_lock(&term
->mtx
);
445 updated
= chargrid_newline(term
->frontbuf
);
450 updated
= chargrid_tabstop(term
->frontbuf
, 8);
453 updated
= chargrid_backspace(term
->frontbuf
);
456 updated
= chargrid_putchar(term
->frontbuf
, ch
, true);
459 fibril_mutex_unlock(&term
->mtx
);
465 static errno_t
term_write(con_srv_t
*srv
, void *data
, size_t size
, size_t *nwritten
)
467 terminal_t
*term
= srv_to_terminal(srv
);
471 term_write_char(term
, str_decode(data
, &off
, size
));
477 static void term_sync(con_srv_t
*srv
)
479 terminal_t
*term
= srv_to_terminal(srv
);
484 static void term_clear(con_srv_t
*srv
)
486 terminal_t
*term
= srv_to_terminal(srv
);
488 fibril_mutex_lock(&term
->mtx
);
489 chargrid_clear(term
->frontbuf
);
490 fibril_mutex_unlock(&term
->mtx
);
495 static void term_set_pos(con_srv_t
*srv
, sysarg_t col
, sysarg_t row
)
497 terminal_t
*term
= srv_to_terminal(srv
);
499 fibril_mutex_lock(&term
->mtx
);
500 chargrid_set_cursor(term
->frontbuf
, col
, row
);
501 fibril_mutex_unlock(&term
->mtx
);
506 static errno_t
term_get_pos(con_srv_t
*srv
, sysarg_t
*col
, sysarg_t
*row
)
508 terminal_t
*term
= srv_to_terminal(srv
);
510 fibril_mutex_lock(&term
->mtx
);
511 chargrid_get_cursor(term
->frontbuf
, col
, row
);
512 fibril_mutex_unlock(&term
->mtx
);
517 static errno_t
term_get_size(con_srv_t
*srv
, sysarg_t
*cols
, sysarg_t
*rows
)
519 terminal_t
*term
= srv_to_terminal(srv
);
521 fibril_mutex_lock(&term
->mtx
);
524 fibril_mutex_unlock(&term
->mtx
);
529 static errno_t
term_get_color_cap(con_srv_t
*srv
, console_caps_t
*caps
)
537 static void term_set_style(con_srv_t
*srv
, console_style_t style
)
539 terminal_t
*term
= srv_to_terminal(srv
);
541 fibril_mutex_lock(&term
->mtx
);
542 chargrid_set_style(term
->frontbuf
, style
);
543 fibril_mutex_unlock(&term
->mtx
);
546 static void term_set_color(con_srv_t
*srv
, console_color_t bgcolor
,
547 console_color_t fgcolor
, console_color_attr_t attr
)
549 terminal_t
*term
= srv_to_terminal(srv
);
551 fibril_mutex_lock(&term
->mtx
);
552 chargrid_set_color(term
->frontbuf
, bgcolor
, fgcolor
, attr
);
553 fibril_mutex_unlock(&term
->mtx
);
556 static void term_set_rgb_color(con_srv_t
*srv
, pixel_t bgcolor
,
559 terminal_t
*term
= srv_to_terminal(srv
);
561 fibril_mutex_lock(&term
->mtx
);
562 chargrid_set_rgb_color(term
->frontbuf
, bgcolor
, fgcolor
);
563 fibril_mutex_unlock(&term
->mtx
);
566 static void term_set_cursor_visibility(con_srv_t
*srv
, bool visible
)
568 terminal_t
*term
= srv_to_terminal(srv
);
570 fibril_mutex_lock(&term
->mtx
);
571 chargrid_set_cursor_visibility(term
->frontbuf
, visible
);
572 fibril_mutex_unlock(&term
->mtx
);
577 static errno_t
term_get_event(con_srv_t
*srv
, cons_event_t
*event
)
579 terminal_t
*term
= srv_to_terminal(srv
);
580 link_t
*link
= prodcons_consume(&term
->input_pc
);
581 cons_event_t
*ev
= list_get_instance(link
, cons_event_t
, link
);
588 void deinit_terminal(terminal_t
*term
)
590 list_remove(&term
->link
);
591 widget_deinit(&term
->widget
);
594 chargrid_destroy(term
->frontbuf
);
597 chargrid_destroy(term
->backbuf
);
600 static void terminal_destroy(widget_t
*widget
)
602 terminal_t
*term
= (terminal_t
*) widget
;
604 deinit_terminal(term
);
608 static void terminal_reconfigure(widget_t
*widget
)
613 static void terminal_rearrange(widget_t
*widget
, sysarg_t hpos
, sysarg_t vpos
,
614 sysarg_t width
, sysarg_t height
)
616 terminal_t
*term
= (terminal_t
*) widget
;
618 widget_modify(widget
, hpos
, vpos
, width
, height
);
619 widget
->width_ideal
= width
;
620 widget
->height_ideal
= height
;
625 static void terminal_repaint(widget_t
*widget
)
627 terminal_t
*term
= (terminal_t
*) widget
;
632 static void terminal_queue_cons_event(terminal_t
*term
, cons_event_t
*ev
)
634 /* Got key press/release event */
635 cons_event_t
*event
=
636 (cons_event_t
*) malloc(sizeof(cons_event_t
));
641 link_initialize(&event
->link
);
643 prodcons_produce(&term
->input_pc
, &event
->link
);
646 /* Got key press/release event */
647 static void terminal_handle_keyboard_event(widget_t
*widget
,
648 kbd_event_t kbd_event
)
650 terminal_t
*term
= (terminal_t
*) widget
;
653 event
.type
= CEV_KEY
;
654 event
.ev
.key
= kbd_event
;
656 terminal_queue_cons_event(term
, &event
);
659 static void terminal_handle_position_event(widget_t
*widget
, pos_event_t pos_event
)
662 terminal_t
*term
= (terminal_t
*) widget
;
663 sysarg_t sx
= term
->widget
.hpos
;
664 sysarg_t sy
= term
->widget
.vpos
;
666 if (pos_event
.type
== POS_PRESS
) {
667 event
.type
= CEV_POS
;
668 event
.ev
.pos
.type
= pos_event
.type
;
669 event
.ev
.pos
.pos_id
= pos_event
.pos_id
;
670 event
.ev
.pos
.btn_num
= pos_event
.btn_num
;
672 event
.ev
.pos
.hpos
= (pos_event
.hpos
- sx
) / FONT_WIDTH
;
673 event
.ev
.pos
.vpos
= (pos_event
.vpos
- sy
) / FONT_SCANLINES
;
674 terminal_queue_cons_event(term
, &event
);
678 static void term_connection(ipc_callid_t iid
, ipc_call_t
*icall
, void *arg
)
680 terminal_t
*term
= NULL
;
682 list_foreach(terms
, link
, terminal_t
, cur
) {
683 if (cur
->dsid
== (service_id_t
) IPC_GET_ARG2(*icall
)) {
690 async_answer_0(iid
, ENOENT
);
694 if (atomic_postinc(&term
->refcnt
) == 0)
695 chargrid_set_cursor_visibility(term
->frontbuf
, true);
697 con_conn(iid
, icall
, &term
->srvs
);
700 bool init_terminal(terminal_t
*term
, widget_t
*parent
, const void *data
,
701 sysarg_t width
, sysarg_t height
)
703 widget_init(&term
->widget
, parent
, data
);
705 link_initialize(&term
->link
);
706 fibril_mutex_initialize(&term
->mtx
);
707 atomic_set(&term
->refcnt
, 0);
709 prodcons_initialize(&term
->input_pc
);
710 term
->char_remains_len
= 0;
712 term
->widget
.width
= width
;
713 term
->widget
.height
= height
;
714 term
->widget
.width_ideal
= width
;
715 term
->widget
.height_ideal
= height
;
717 term
->widget
.destroy
= terminal_destroy
;
718 term
->widget
.reconfigure
= terminal_reconfigure
;
719 term
->widget
.rearrange
= terminal_rearrange
;
720 term
->widget
.repaint
= terminal_repaint
;
721 term
->widget
.handle_keyboard_event
= terminal_handle_keyboard_event
;
722 term
->widget
.handle_position_event
= terminal_handle_position_event
;
724 term
->cols
= width
/ FONT_WIDTH
;
725 term
->rows
= height
/ FONT_SCANLINES
;
727 term
->frontbuf
= NULL
;
728 term
->backbuf
= NULL
;
730 term
->frontbuf
= chargrid_create(term
->cols
, term
->rows
,
732 if (!term
->frontbuf
) {
733 widget_deinit(&term
->widget
);
737 term
->backbuf
= chargrid_create(term
->cols
, term
->rows
,
739 if (!term
->backbuf
) {
740 widget_deinit(&term
->widget
);
744 chargrid_clear(term
->frontbuf
);
745 chargrid_clear(term
->backbuf
);
748 async_set_fallback_port_handler(term_connection
, NULL
);
749 con_srvs_init(&term
->srvs
);
750 term
->srvs
.ops
= &con_ops
;
751 term
->srvs
.sarg
= term
;
753 errno_t rc
= loc_server_register(NAME
);
755 widget_deinit(&term
->widget
);
759 char vc
[LOC_NAME_MAXLEN
+ 1];
760 snprintf(vc
, LOC_NAME_MAXLEN
, "%s/%" PRIu64
, NAMESPACE
,
763 rc
= loc_service_register(vc
, &term
->dsid
);
765 widget_deinit(&term
->widget
);
769 list_append(&term
->link
, &terms
);
770 getterm(vc
, "/app/bdsh");
775 terminal_t
*create_terminal(widget_t
*parent
, const void *data
, sysarg_t width
,
778 terminal_t
*term
= (terminal_t
*) malloc(sizeof(terminal_t
));
782 bool ret
= init_terminal(term
, parent
, data
, width
, height
);