2 * Copyright (c) 2024 Jiri Svoboda
3 * Copyright (c) 2012 Petr Koupy
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 /** @addtogroup terminal
34 * @file Terminal application
38 #include <adt/prodcons.h>
41 #include <fbfont/font-8x16.h>
43 #include <gfx/bitmap.h>
44 #include <gfx/context.h>
45 #include <gfx/render.h>
46 #include <io/con_srv.h>
47 #include <io/concaps.h>
48 #include <io/console.h>
49 #include <io/pixelmap.h>
54 #include <str_error.h>
57 #include <ui/resource.h>
59 #include <ui/wdecor.h>
60 #include <ui/window.h>
64 #define NAME "terminal"
65 #define NAMESPACE "terminal"
67 #define LOCFS_MOUNT_POINT "/loc"
69 #define APP_GETTERM "/app/getterm"
72 (CONSOLE_CAP_CURSORCTL | CONSOLE_CAP_STYLE | CONSOLE_CAP_INDEXED | \
75 #define SCROLLBACK_MAX_LINES 1000
76 #define MIN_WINDOW_COLS 8
77 #define MIN_WINDOW_ROWS 4
79 static LIST_INITIALIZE(terms
);
81 #define COLOR_BRIGHT 8
83 static const pixel_t _basic_colors
[16] = {
84 [COLOR_BLACK
] = PIXEL(255, 0, 0, 0),
85 [COLOR_RED
] = PIXEL(255, 170, 0, 0),
86 [COLOR_GREEN
] = PIXEL(255, 0, 170, 0),
87 [COLOR_YELLOW
] = PIXEL(255, 170, 85, 0),
88 [COLOR_BLUE
] = PIXEL(255, 0, 0, 170),
89 [COLOR_MAGENTA
] = PIXEL(255, 170, 0, 170),
90 [COLOR_CYAN
] = PIXEL(255, 0, 170, 170),
91 [COLOR_WHITE
] = PIXEL(255, 170, 170, 170),
93 [COLOR_BLACK
| COLOR_BRIGHT
] = PIXEL(255, 85, 85, 85),
94 [COLOR_RED
| COLOR_BRIGHT
] = PIXEL(255, 255, 85, 85),
95 [COLOR_GREEN
| COLOR_BRIGHT
] = PIXEL(255, 85, 255, 85),
96 [COLOR_YELLOW
| COLOR_BRIGHT
] = PIXEL(255, 255, 255, 85),
97 [COLOR_BLUE
| COLOR_BRIGHT
] = PIXEL(255, 85, 85, 255),
98 [COLOR_MAGENTA
| COLOR_BRIGHT
] = PIXEL(255, 255, 85, 255),
99 [COLOR_CYAN
| COLOR_BRIGHT
] = PIXEL(255, 85, 255, 255),
100 [COLOR_WHITE
| COLOR_BRIGHT
] = PIXEL(255, 255, 255, 255),
103 static errno_t
term_open(con_srvs_t
*, con_srv_t
*);
104 static errno_t
term_close(con_srv_t
*);
105 static errno_t
term_read(con_srv_t
*, void *, size_t, size_t *);
106 static errno_t
term_write(con_srv_t
*, void *, size_t, size_t *);
107 static void term_sync(con_srv_t
*);
108 static void term_clear(con_srv_t
*);
109 static void term_set_pos(con_srv_t
*, sysarg_t col
, sysarg_t row
);
110 static errno_t
term_get_pos(con_srv_t
*, sysarg_t
*, sysarg_t
*);
111 static errno_t
term_get_size(con_srv_t
*, sysarg_t
*, sysarg_t
*);
112 static errno_t
term_get_color_cap(con_srv_t
*, console_caps_t
*);
113 static void term_set_style(con_srv_t
*, console_style_t
);
114 static void term_set_color(con_srv_t
*, console_color_t
, console_color_t
,
115 console_color_attr_t
);
116 static void term_set_rgb_color(con_srv_t
*, pixel_t
, pixel_t
);
117 static void term_set_cursor_visibility(con_srv_t
*, bool);
118 static errno_t
term_set_caption(con_srv_t
*, const char *);
119 static errno_t
term_get_event(con_srv_t
*, cons_event_t
*);
120 static errno_t
term_map(con_srv_t
*, sysarg_t
, sysarg_t
, charfield_t
**);
121 static void term_unmap(con_srv_t
*);
122 static void term_buf_update(con_srv_t
*, sysarg_t
, sysarg_t
, sysarg_t
,
125 static con_ops_t con_ops
= {
132 .set_pos
= term_set_pos
,
133 .get_pos
= term_get_pos
,
134 .get_size
= term_get_size
,
135 .get_color_cap
= term_get_color_cap
,
136 .set_style
= term_set_style
,
137 .set_color
= term_set_color
,
138 .set_rgb_color
= term_set_rgb_color
,
139 .set_cursor_visibility
= term_set_cursor_visibility
,
140 .set_caption
= term_set_caption
,
141 .get_event
= term_get_event
,
144 .update
= term_buf_update
147 static void terminal_close_event(ui_window_t
*, void *);
148 static void terminal_focus_event(ui_window_t
*, void *, unsigned);
149 static void terminal_resize_event(ui_window_t
*, void *);
150 static void terminal_kbd_event(ui_window_t
*, void *, kbd_event_t
*);
151 static void terminal_pos_event(ui_window_t
*, void *, pos_event_t
*);
152 static void terminal_unfocus_event(ui_window_t
*, void *, unsigned);
153 static void terminal_maximize_event(ui_window_t
*, void *);
154 static void terminal_unmaximize_event(ui_window_t
*, void *);
156 static ui_window_cb_t terminal_window_cb
= {
157 .close
= terminal_close_event
,
158 .focus
= terminal_focus_event
,
159 .resize
= terminal_resize_event
,
160 .kbd
= terminal_kbd_event
,
161 .pos
= terminal_pos_event
,
162 .unfocus
= terminal_unfocus_event
,
163 .maximize
= terminal_maximize_event
,
164 .unmaximize
= terminal_unmaximize_event
,
167 static errno_t
terminal_wait_fibril(void *);
169 static terminal_t
*srv_to_terminal(con_srv_t
*srv
)
171 return srv
->srvs
->sarg
;
174 static errno_t
getterm(task_wait_t
*wait
, const char *svc
, const char *app
)
176 return task_spawnl(NULL
, wait
, APP_GETTERM
, APP_GETTERM
, svc
,
177 LOCFS_MOUNT_POINT
, "--msg", "--wait", "--", app
, NULL
);
180 static pixel_t
termui_color_to_pixel(termui_color_t c
)
183 termui_color_to_rgb(c
, &r
, &g
, &b
);
184 return PIXEL(255, r
, g
, b
);
187 static termui_color_t
termui_color_from_pixel(pixel_t pixel
)
189 return termui_color_from_rgb(RED(pixel
), GREEN(pixel
), BLUE(pixel
));
192 static termui_cell_t
charfield_to_termui_cell(terminal_t
*term
, const charfield_t
*cf
)
194 termui_cell_t cell
= { };
196 cell
.glyph_idx
= fb_font_glyph(cf
->ch
, NULL
);
198 switch (cf
->attrs
.type
) {
199 case CHAR_ATTR_STYLE
:
200 switch (cf
->attrs
.val
.style
) {
202 cell
.bgcolor
= term
->default_bgcolor
;
203 cell
.fgcolor
= term
->default_fgcolor
;
206 cell
.bgcolor
= term
->emphasis_bgcolor
;
207 cell
.fgcolor
= term
->emphasis_fgcolor
;
210 cell
.bgcolor
= term
->default_bgcolor
;
211 cell
.fgcolor
= term
->default_fgcolor
;
215 cell
.bgcolor
= term
->selection_bgcolor
;
216 cell
.fgcolor
= term
->selection_fgcolor
;
221 case CHAR_ATTR_INDEX
:
222 char_attr_index_t index
= cf
->attrs
.val
.index
;
224 int bright
= (index
.attr
& CATTR_BRIGHT
) ? COLOR_BRIGHT
: 0;
225 pixel_t bgcolor
= _basic_colors
[index
.bgcolor
];
226 pixel_t fgcolor
= _basic_colors
[index
.fgcolor
| bright
];
227 cell
.bgcolor
= termui_color_from_pixel(bgcolor
);
228 cell
.fgcolor
= termui_color_from_pixel(fgcolor
);
230 if (index
.attr
& CATTR_BLINK
)
236 cell
.bgcolor
= termui_color_from_pixel(cf
->attrs
.val
.rgb
.bgcolor
);
237 cell
.fgcolor
= termui_color_from_pixel(cf
->attrs
.val
.rgb
.fgcolor
);
244 static void term_update_region(terminal_t
*term
, sysarg_t x
, sysarg_t y
,
245 sysarg_t w
, sysarg_t h
)
255 gfx_rect_envelope(&term
->update
, &rect
, &nupdate
);
256 term
->update
= nupdate
;
259 static void term_draw_cell(terminal_t
*term
, pixelmap_t
*pixelmap
, int col
, int row
, const termui_cell_t
*cell
)
261 termui_color_t bg
= cell
->bgcolor
;
262 if (bg
== TERMUI_COLOR_DEFAULT
)
263 bg
= term
->default_bgcolor
;
265 termui_color_t fg
= cell
->fgcolor
;
266 if (fg
== TERMUI_COLOR_DEFAULT
)
267 fg
= term
->default_fgcolor
;
269 pixel_t bgcolor
= termui_color_to_pixel(bg
);
270 pixel_t fgcolor
= termui_color_to_pixel(fg
);
272 int bx
= col
* FONT_WIDTH
;
273 int by
= row
* FONT_SCANLINES
;
275 // TODO: support bold/italic/underline/strike/blink styling
277 if (cell
->inverted
^ cell
->cursor
) {
278 pixel_t tmp
= bgcolor
;
283 uint32_t glyph
= cell
->glyph_idx
;
284 assert(glyph
< FONT_GLYPHS
);
287 glyph
= fb_font_glyph(U
' ', NULL
);
289 for (unsigned int y
= 0; y
< FONT_SCANLINES
; y
++) {
290 pixel_t
*dst
= pixelmap_pixel_at(pixelmap
, bx
, by
+ y
);
291 pixel_t
*dst_max
= pixelmap_pixel_at(pixelmap
, bx
+ FONT_WIDTH
- 1, by
+ y
);
292 if (!dst
|| !dst_max
)
294 int count
= FONT_WIDTH
;
295 while (count
-- != 0) {
296 *dst
++ = (fb_font
[glyph
][y
] & (1 << count
)) ? fgcolor
: bgcolor
;
300 term_update_region(term
, bx
, by
, FONT_WIDTH
, FONT_SCANLINES
);
303 static void term_render(terminal_t
*term
)
305 (void) gfx_bitmap_render(term
->bmp
, &term
->update
, &term
->off
);
307 term
->update
.p0
.x
= 0;
308 term
->update
.p0
.y
= 0;
309 term
->update
.p1
.x
= 0;
310 term
->update
.p1
.y
= 0;
313 static void termui_refresh_cb(void *userdata
)
315 terminal_t
*term
= userdata
;
317 termui_force_viewport_update(term
->termui
, 0, termui_get_rows(term
->termui
));
320 static void termui_scroll_cb(void *userdata
, int delta
)
324 // Until we have support for hardware accelerated scrolling, just redraw everything.
325 termui_refresh_cb(userdata
);
328 static pixelmap_t
term_get_pixelmap(terminal_t
*term
)
330 pixelmap_t pixelmap
= { };
331 gfx_bitmap_alloc_t alloc
;
333 errno_t rc
= gfx_bitmap_get_alloc(term
->bmp
, &alloc
);
337 pixelmap
.width
= term
->w
;
338 pixelmap
.height
= term
->h
;
339 pixelmap
.data
= alloc
.pixels
;
343 static void term_clear_bitmap(terminal_t
*term
, pixel_t color
)
345 pixelmap_t pixelmap
= term_get_pixelmap(term
);
346 if (pixelmap
.data
== NULL
)
349 sysarg_t pixels
= pixelmap
.height
* pixelmap
.width
;
350 for (sysarg_t i
= 0; i
< pixels
; i
++)
351 pixelmap
.data
[i
] = color
;
353 term_update_region(term
, 0, 0, pixelmap
.width
, pixelmap
.height
);
356 static void termui_update_cb(void *userdata
, int col
, int row
, const termui_cell_t
*cell
, int len
)
358 terminal_t
*term
= userdata
;
360 pixelmap_t pixelmap
= term_get_pixelmap(term
);
361 if (pixelmap
.data
== NULL
)
364 for (int i
= 0; i
< len
; i
++)
365 term_draw_cell(term
, &pixelmap
, col
+ i
, row
, &cell
[i
]);
368 static errno_t
term_open(con_srvs_t
*srvs
, con_srv_t
*srv
)
373 static errno_t
term_close(con_srv_t
*srv
)
378 static errno_t
term_read(con_srv_t
*srv
, void *buf
, size_t size
, size_t *nread
)
380 terminal_t
*term
= srv_to_terminal(srv
);
385 * Read input from keyboard and copy it to the buffer.
386 * We need to handle situation when wchar is split by 2 following
390 /* Copy to the buffer remaining characters. */
391 while ((pos
< size
) && (term
->char_remains_len
> 0)) {
392 bbuf
[pos
] = term
->char_remains
[0];
395 /* Unshift the array. */
396 for (size_t i
= 1; i
< term
->char_remains_len
; i
++)
397 term
->char_remains
[i
- 1] = term
->char_remains
[i
];
399 term
->char_remains_len
--;
402 /* Still not enough? Then get another key from the queue. */
404 link_t
*link
= prodcons_consume(&term
->input_pc
);
405 terminal_event_t
*qevent
= list_get_instance(link
,
406 terminal_event_t
, link
);
407 cons_event_t
*event
= &qevent
->ev
;
409 /* Accept key presses of printable chars only. */
410 if (event
->type
== CEV_KEY
&& event
->ev
.key
.type
== KEY_PRESS
&&
411 event
->ev
.key
.c
!= 0) {
417 wstr_to_str(term
->char_remains
, UTF8_CHAR_BUFFER_SIZE
, tmp
);
418 term
->char_remains_len
= str_size(term
->char_remains
);
429 static void term_write_char(terminal_t
*term
, wchar_t ch
)
433 termui_put_crlf(term
->termui
);
436 termui_put_cr(term
->termui
);
439 termui_put_tab(term
->termui
);
442 termui_put_backspace(term
->termui
);
445 // TODO: For some languages, we might need support for combining
446 // characters. Currently, we assume every unicode code point is
447 // an individual printed character, which is not always the case.
448 termui_put_glyph(term
->termui
, fb_font_glyph(ch
, NULL
), 1);
453 static errno_t
term_write(con_srv_t
*srv
, void *data
, size_t size
, size_t *nwritten
)
455 terminal_t
*term
= srv_to_terminal(srv
);
457 fibril_mutex_lock(&term
->mtx
);
461 term_write_char(term
, str_decode(data
, &off
, size
));
463 fibril_mutex_unlock(&term
->mtx
);
466 gfx_update(term
->gc
);
472 static void term_sync(con_srv_t
*srv
)
474 terminal_t
*term
= srv_to_terminal(srv
);
477 gfx_update(term
->gc
);
480 static void term_clear(con_srv_t
*srv
)
482 terminal_t
*term
= srv_to_terminal(srv
);
484 fibril_mutex_lock(&term
->mtx
);
485 termui_clear_screen(term
->termui
);
486 fibril_mutex_unlock(&term
->mtx
);
489 gfx_update(term
->gc
);
492 static void term_set_pos(con_srv_t
*srv
, sysarg_t col
, sysarg_t row
)
494 terminal_t
*term
= srv_to_terminal(srv
);
496 fibril_mutex_lock(&term
->mtx
);
497 termui_set_pos(term
->termui
, col
, row
);
498 fibril_mutex_unlock(&term
->mtx
);
501 gfx_update(term
->gc
);
504 static errno_t
term_get_pos(con_srv_t
*srv
, sysarg_t
*col
, sysarg_t
*row
)
506 terminal_t
*term
= srv_to_terminal(srv
);
508 fibril_mutex_lock(&term
->mtx
);
510 termui_get_pos(term
->termui
, &icol
, &irow
);
511 fibril_mutex_unlock(&term
->mtx
);
519 static errno_t
term_get_size(con_srv_t
*srv
, sysarg_t
*cols
, sysarg_t
*rows
)
521 terminal_t
*term
= srv_to_terminal(srv
);
523 fibril_mutex_lock(&term
->mtx
);
524 *cols
= termui_get_cols(term
->termui
);
525 *rows
= termui_get_rows(term
->termui
);
526 fibril_mutex_unlock(&term
->mtx
);
531 static errno_t
term_get_color_cap(con_srv_t
*srv
, console_caps_t
*caps
)
539 static void term_set_style(con_srv_t
*srv
, console_style_t style
)
541 terminal_t
*term
= srv_to_terminal(srv
);
543 termui_cell_t cellstyle
= { };
547 cellstyle
.bgcolor
= term
->default_bgcolor
;
548 cellstyle
.fgcolor
= term
->default_fgcolor
;
551 cellstyle
.bgcolor
= term
->emphasis_bgcolor
;
552 cellstyle
.fgcolor
= term
->emphasis_fgcolor
;
555 cellstyle
.bgcolor
= term
->default_bgcolor
;
556 cellstyle
.fgcolor
= term
->default_fgcolor
;
557 cellstyle
.inverted
= 1;
560 cellstyle
.bgcolor
= term
->selection_bgcolor
;
561 cellstyle
.fgcolor
= term
->selection_fgcolor
;
565 fibril_mutex_lock(&term
->mtx
);
566 termui_set_style(term
->termui
, cellstyle
);
567 fibril_mutex_unlock(&term
->mtx
);
570 static void term_set_color(con_srv_t
*srv
, console_color_t bgcolor
,
571 console_color_t fgcolor
, console_color_attr_t attr
)
573 terminal_t
*term
= srv_to_terminal(srv
);
575 int bright
= (attr
& CATTR_BRIGHT
) ? COLOR_BRIGHT
: 0;
577 termui_cell_t cellstyle
= { };
578 cellstyle
.bgcolor
= termui_color_from_pixel(_basic_colors
[bgcolor
]);
579 cellstyle
.fgcolor
= termui_color_from_pixel(_basic_colors
[fgcolor
| bright
]);
581 if (attr
& CATTR_BLINK
)
584 fibril_mutex_lock(&term
->mtx
);
585 termui_set_style(term
->termui
, cellstyle
);
586 fibril_mutex_unlock(&term
->mtx
);
589 static void term_set_rgb_color(con_srv_t
*srv
, pixel_t bgcolor
,
592 terminal_t
*term
= srv_to_terminal(srv
);
593 termui_cell_t cellstyle
= {
594 .bgcolor
= termui_color_from_pixel(bgcolor
),
595 .fgcolor
= termui_color_from_pixel(fgcolor
),
598 fibril_mutex_lock(&term
->mtx
);
599 termui_set_style(term
->termui
, cellstyle
);
600 fibril_mutex_unlock(&term
->mtx
);
603 static void term_set_cursor_visibility(con_srv_t
*srv
, bool visible
)
605 terminal_t
*term
= srv_to_terminal(srv
);
607 fibril_mutex_lock(&term
->mtx
);
608 termui_set_cursor_visibility(term
->termui
, visible
);
609 fibril_mutex_unlock(&term
->mtx
);
612 gfx_update(term
->gc
);
615 static errno_t
term_set_caption(con_srv_t
*srv
, const char *caption
)
617 terminal_t
*term
= srv_to_terminal(srv
);
620 fibril_mutex_lock(&term
->mtx
);
622 if (str_size(caption
) > 0)
627 ui_window_set_caption(term
->window
, cap
);
628 fibril_mutex_unlock(&term
->mtx
);
631 gfx_update(term
->gc
);
635 static errno_t
term_get_event(con_srv_t
*srv
, cons_event_t
*event
)
637 terminal_t
*term
= srv_to_terminal(srv
);
638 link_t
*link
= prodcons_consume(&term
->input_pc
);
639 terminal_event_t
*ev
= list_get_instance(link
, terminal_event_t
, link
);
646 /** Create shared buffer for efficient rendering.
648 * @param srv Console server
649 * @param cols Number of columns in buffer
650 * @param rows Number of rows in buffer
651 * @param rbuf Place to store pointer to new sharable buffer
653 * @return EOK on sucess or an error code
655 static errno_t
term_map(con_srv_t
*srv
, sysarg_t cols
, sysarg_t rows
,
658 terminal_t
*term
= srv_to_terminal(srv
);
661 fibril_mutex_lock(&term
->mtx
);
663 if (term
->ubuf
!= NULL
) {
664 fibril_mutex_unlock(&term
->mtx
);
668 buf
= as_area_create(AS_AREA_ANY
, cols
* rows
* sizeof(charfield_t
),
669 AS_AREA_READ
| AS_AREA_WRITE
| AS_AREA_CACHEABLE
, AS_AREA_UNPAGED
);
670 if (buf
== AS_MAP_FAILED
) {
671 fibril_mutex_unlock(&term
->mtx
);
679 /* Scroll back to active screen. */
680 termui_history_scroll(term
->termui
, INT_MAX
);
682 fibril_mutex_unlock(&term
->mtx
);
688 /** Delete shared buffer.
690 * @param srv Console server
692 static void term_unmap(con_srv_t
*srv
)
694 terminal_t
*term
= srv_to_terminal(srv
);
697 fibril_mutex_lock(&term
->mtx
);
702 termui_wipe_screen(term
->termui
, 0);
704 fibril_mutex_unlock(&term
->mtx
);
706 /* Update terminal */
708 gfx_update(term
->gc
);
711 as_area_destroy(buf
);
714 /** Update area of terminal from shared buffer.
716 * @param srv Console server
717 * @param c0 Column coordinate of top-left corner (inclusive)
718 * @param r0 Row coordinate of top-left corner (inclusive)
719 * @param c1 Column coordinate of bottom-right corner (exclusive)
720 * @param r1 Row coordinate of bottom-right corner (exclusive)
722 static void term_buf_update(con_srv_t
*srv
, sysarg_t c0
, sysarg_t r0
,
723 sysarg_t c1
, sysarg_t r1
)
725 terminal_t
*term
= srv_to_terminal(srv
);
727 fibril_mutex_lock(&term
->mtx
);
729 if (term
->ubuf
== NULL
) {
730 fibril_mutex_unlock(&term
->mtx
);
734 /* Make sure we have meaningful coordinates, within bounds */
735 c1
= min(c1
, term
->ucols
);
736 c1
= min(c1
, (sysarg_t
) termui_get_cols(term
->termui
));
737 r1
= min(r1
, term
->urows
);
738 r1
= min(r1
, (sysarg_t
) termui_get_rows(term
->termui
));
740 if (c0
>= c1
|| r0
>= r1
) {
741 fibril_mutex_unlock(&term
->mtx
);
745 /* Update front buffer from user buffer */
747 for (sysarg_t row
= r0
; row
< r1
; row
++) {
748 termui_cell_t
*cells
= termui_get_active_row(term
->termui
, row
);
750 for (sysarg_t col
= c0
; col
< c1
; col
++) {
751 cells
[col
] = charfield_to_termui_cell(term
, &term
->ubuf
[row
* term
->ucols
+ col
]);
754 termui_update_cb(term
, c0
, row
, &cells
[c0
], c1
- c0
);
757 fibril_mutex_unlock(&term
->mtx
);
759 /* Update terminal */
761 gfx_update(term
->gc
);
764 static errno_t
terminal_window_resize(terminal_t
*term
)
767 ui_window_get_app_rect(term
->window
, &rect
);
769 int width
= rect
.p1
.x
- rect
.p0
.x
;
770 int height
= rect
.p1
.y
- rect
.p0
.y
;
773 term
->gc
= ui_window_get_gc(term
->window
);
775 assert(term
->gc
== ui_window_get_gc(term
->window
));
778 term
->ui_res
= ui_window_get_res(term
->window
);
780 assert(term
->ui_res
== ui_window_get_res(term
->window
));
782 gfx_bitmap_t
*new_bmp
;
783 gfx_bitmap_params_t params
;
784 gfx_bitmap_params_init(¶ms
);
785 params
.rect
.p0
.x
= 0;
786 params
.rect
.p0
.y
= 0;
787 params
.rect
.p1
.x
= width
;
788 params
.rect
.p1
.y
= height
;
790 errno_t rc
= gfx_bitmap_create(term
->gc
, ¶ms
, NULL
, &new_bmp
);
792 fprintf(stderr
, "Error allocating new screen bitmap: %s\n", str_error(rc
));
797 rc
= gfx_bitmap_destroy(term
->bmp
);
799 fprintf(stderr
, "Error deallocating old screen bitmap: %s\n", str_error(rc
));
806 term_clear_bitmap(term
, termui_color_to_pixel(term
->default_bgcolor
));
811 void terminal_destroy(terminal_t
*term
)
813 list_remove(&term
->link
);
815 termui_destroy(term
->termui
);
818 as_area_destroy(term
->ubuf
);
820 ui_destroy(term
->ui
);
824 static void terminal_queue_cons_event(terminal_t
*term
, cons_event_t
*ev
)
826 /* Got key press/release event */
827 terminal_event_t
*event
=
828 (terminal_event_t
*) malloc(sizeof(terminal_event_t
));
833 link_initialize(&event
->link
);
835 prodcons_produce(&term
->input_pc
, &event
->link
);
838 /** Handle window close event. */
839 static void terminal_close_event(ui_window_t
*window
, void *arg
)
841 terminal_t
*term
= (terminal_t
*) arg
;
846 /** Handle window focus event. */
847 static void terminal_focus_event(ui_window_t
*window
, void *arg
,
850 terminal_t
*term
= (terminal_t
*) arg
;
853 term
->is_focused
= true;
855 gfx_update(term
->gc
);
858 static void terminal_resize_handler(ui_window_t
*window
, void *arg
)
860 terminal_t
*term
= (terminal_t
*) arg
;
862 fibril_mutex_lock(&term
->mtx
);
864 errno_t rc
= terminal_window_resize(term
);
866 (void) termui_resize(term
->termui
, term
->w
/ FONT_WIDTH
, term
->h
/ FONT_SCANLINES
, SCROLLBACK_MAX_LINES
);
867 termui_refresh_cb(term
);
869 gfx_update(term
->gc
);
871 cons_event_t event
= { .type
= CEV_RESIZE
};
872 terminal_queue_cons_event(term
, &event
);
875 fibril_mutex_unlock(&term
->mtx
);
878 static void terminal_resize_event(ui_window_t
*window
, void *arg
)
880 ui_window_def_resize(window
);
881 terminal_resize_handler(window
, arg
);
884 static void terminal_maximize_event(ui_window_t
*window
, void *arg
)
886 ui_window_def_maximize(window
);
887 terminal_resize_handler(window
, arg
);
890 static void terminal_unmaximize_event(ui_window_t
*window
, void *arg
)
892 ui_window_def_unmaximize(window
);
893 terminal_resize_handler(window
, arg
);
896 /** Handle window keyboard event */
897 static void terminal_kbd_event(ui_window_t
*window
, void *arg
,
898 kbd_event_t
*kbd_event
)
900 terminal_t
*term
= (terminal_t
*) arg
;
903 event
.type
= CEV_KEY
;
904 event
.ev
.key
= *kbd_event
;
906 const int PAGE_ROWS
= (termui_get_rows(term
->termui
) * 2) / 3;
908 fibril_mutex_lock(&term
->mtx
);
910 if (!term
->ubuf
&& kbd_event
->type
== KEY_PRESS
&&
911 (kbd_event
->key
== KC_PAGE_UP
|| kbd_event
->key
== KC_PAGE_DOWN
)) {
913 termui_history_scroll(term
->termui
,
914 (kbd_event
->key
== KC_PAGE_UP
) ? -PAGE_ROWS
: PAGE_ROWS
);
917 gfx_update(term
->gc
);
919 terminal_queue_cons_event(term
, &event
);
922 fibril_mutex_unlock(&term
->mtx
);
925 /** Handle window position event */
926 static void terminal_pos_event(ui_window_t
*window
, void *arg
, pos_event_t
*event
)
929 terminal_t
*term
= (terminal_t
*) arg
;
931 switch (event
->type
) {
940 /* Ignore mouse events when we're in scrollback mode. */
941 if (termui_scrollback_is_active(term
->termui
))
944 sysarg_t sx
= term
->off
.x
;
945 sysarg_t sy
= term
->off
.y
;
947 if (event
->hpos
< sx
|| event
->vpos
< sy
)
950 cevent
.type
= CEV_POS
;
951 cevent
.ev
.pos
.type
= event
->type
;
952 cevent
.ev
.pos
.pos_id
= event
->pos_id
;
953 cevent
.ev
.pos
.btn_num
= event
->btn_num
;
955 cevent
.ev
.pos
.hpos
= (event
->hpos
- sx
) / FONT_WIDTH
;
956 cevent
.ev
.pos
.vpos
= (event
->vpos
- sy
) / FONT_SCANLINES
;
958 /* Filter out events outside the terminal area. */
959 int cols
= termui_get_cols(term
->termui
);
960 int rows
= termui_get_rows(term
->termui
);
962 if (cevent
.ev
.pos
.hpos
< (sysarg_t
) cols
&& cevent
.ev
.pos
.vpos
< (sysarg_t
) rows
)
963 terminal_queue_cons_event(term
, &cevent
);
966 /** Handle window unfocus event. */
967 static void terminal_unfocus_event(ui_window_t
*window
, void *arg
,
970 terminal_t
*term
= (terminal_t
*) arg
;
973 term
->is_focused
= false;
975 gfx_update(term
->gc
);
979 static void term_connection(ipc_call_t
*icall
, void *arg
)
981 terminal_t
*term
= NULL
;
983 list_foreach(terms
, link
, terminal_t
, cur
) {
984 if (cur
->dsid
== (service_id_t
) ipc_get_arg2(icall
)) {
991 async_answer_0(icall
, ENOENT
);
995 if (!atomic_flag_test_and_set(&term
->refcnt
))
996 termui_set_cursor_visibility(term
->termui
, true);
998 con_conn(icall
, &term
->srvs
);
1001 static errno_t
term_init_window(terminal_t
*term
, const char *display_spec
,
1002 gfx_coord_t width
, gfx_coord_t height
,
1003 gfx_coord_t min_width
, gfx_coord_t min_height
,
1004 terminal_flags_t flags
)
1006 gfx_rect_t min_rect
= { { 0, 0 }, { min_width
, min_height
} };
1007 gfx_rect_t wmin_rect
;
1010 errno_t rc
= ui_create(display_spec
, &term
->ui
);
1012 printf("Error creating UI on %s.\n", display_spec
);
1016 ui_wnd_params_t wparams
;
1017 ui_wnd_params_init(&wparams
);
1018 wparams
.caption
= "Terminal";
1019 wparams
.style
|= ui_wds_maximize_btn
| ui_wds_resizable
;
1021 if ((flags
& tf_topleft
) != 0)
1022 wparams
.placement
= ui_wnd_place_top_left
;
1024 if (ui_is_fullscreen(term
->ui
)) {
1025 wparams
.placement
= ui_wnd_place_full_screen
;
1026 wparams
.style
&= ~ui_wds_decorated
;
1029 /* Compute wrect such that application area corresponds to rect. */
1030 ui_wdecor_rect_from_app(term
->ui
, wparams
.style
, &min_rect
, &wrect
);
1031 gfx_rect_rtranslate(&wrect
.p0
, &wrect
, &wmin_rect
);
1032 wparams
.min_size
= wmin_rect
.p1
;
1034 gfx_rect_t rect
= { { 0, 0 }, { width
, height
} };
1035 ui_wdecor_rect_from_app(term
->ui
, wparams
.style
, &rect
, &rect
);
1036 term
->off
.x
= -rect
.p0
.x
;
1037 term
->off
.y
= -rect
.p0
.y
;
1038 printf("off=%d,%d\n", term
->off
.x
, term
->off
.y
);
1039 gfx_rect_translate(&term
->off
, &rect
, &wparams
.rect
);
1040 printf("wparams.rect=%d,%d,%d,%d\n",
1046 rc
= ui_window_create(term
->ui
, &wparams
, &term
->window
);
1050 ui_window_set_cb(term
->window
, &terminal_window_cb
, (void *) term
);
1051 return terminal_window_resize(term
);
1054 errno_t
terminal_create(const char *display_spec
, sysarg_t width
,
1055 sysarg_t height
, terminal_flags_t flags
, const char *command
,
1060 terminal_t
*term
= calloc(1, sizeof(terminal_t
));
1062 printf("Out of memory.\n");
1066 link_initialize(&term
->link
);
1067 fibril_mutex_initialize(&term
->mtx
);
1068 atomic_flag_clear(&term
->refcnt
);
1070 prodcons_initialize(&term
->input_pc
);
1071 term
->char_remains_len
= 0;
1073 term
->default_bgcolor
= termui_color_from_pixel(_basic_colors
[COLOR_WHITE
| COLOR_BRIGHT
]);
1074 term
->default_fgcolor
= termui_color_from_pixel(_basic_colors
[COLOR_BLACK
]);
1076 term
->emphasis_bgcolor
= termui_color_from_pixel(_basic_colors
[COLOR_WHITE
| COLOR_BRIGHT
]);
1077 term
->emphasis_fgcolor
= termui_color_from_pixel(_basic_colors
[COLOR_RED
| COLOR_BRIGHT
]);
1079 term
->selection_bgcolor
= termui_color_from_pixel(_basic_colors
[COLOR_RED
| COLOR_BRIGHT
]);
1080 term
->selection_fgcolor
= termui_color_from_pixel(_basic_colors
[COLOR_WHITE
| COLOR_BRIGHT
]);
1082 rc
= term_init_window(term
, display_spec
, width
, height
,
1083 MIN_WINDOW_COLS
* FONT_WIDTH
, MIN_WINDOW_ROWS
* FONT_SCANLINES
, flags
);
1085 printf("Error creating window (%s).\n", str_error(rc
));
1089 term
->termui
= termui_create(term
->w
/ FONT_WIDTH
,
1090 term
->h
/ FONT_SCANLINES
, SCROLLBACK_MAX_LINES
);
1091 if (!term
->termui
) {
1092 printf("Error creating terminal UI.\n");
1097 termui_set_refresh_cb(term
->termui
, termui_refresh_cb
, term
);
1098 termui_set_scroll_cb(term
->termui
, termui_scroll_cb
, term
);
1099 termui_set_update_cb(term
->termui
, termui_update_cb
, term
);
1101 async_set_fallback_port_handler(term_connection
, NULL
);
1102 con_srvs_init(&term
->srvs
);
1103 term
->srvs
.ops
= &con_ops
;
1104 term
->srvs
.sarg
= term
;
1106 rc
= loc_server_register(NAME
, &term
->srv
);
1108 printf("Error registering server.\n");
1113 char vc
[LOC_NAME_MAXLEN
+ 1];
1114 snprintf(vc
, LOC_NAME_MAXLEN
, "%s/%" PRIu64
, NAMESPACE
,
1117 rc
= loc_service_register(term
->srv
, vc
, &term
->dsid
);
1119 printf("Error registering service.\n");
1124 list_append(&term
->link
, &terms
);
1125 rc
= getterm(&term
->wait
, vc
, command
);
1129 term
->wfid
= fibril_create(terminal_wait_fibril
, term
);
1130 if (term
->wfid
== 0)
1133 fibril_add_ready(term
->wfid
);
1135 term
->is_focused
= true;
1137 termui_refresh_cb(term
);
1142 if (term
->dsid
!= 0)
1143 loc_service_unregister(term
->srv
, term
->dsid
);
1144 if (term
->srv
!= NULL
)
1145 loc_server_unregister(term
->srv
);
1146 if (term
->window
!= NULL
)
1147 ui_window_destroy(term
->window
);
1148 if (term
->ui
!= NULL
)
1149 ui_destroy(term
->ui
);
1150 if (term
->termui
!= NULL
)
1151 termui_destroy(term
->termui
);
1156 static errno_t
terminal_wait_fibril(void *arg
)
1158 terminal_t
*term
= (terminal_t
*)arg
;
1163 * XXX There is no way to break the sleep if the task does not
1166 (void) task_wait(&term
->wait
, &texit
, &retval
);