source: mainline/uspace/app/terminal/terminal.c@ 034ce6bb

serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 034ce6bb was 77ffa01, checked in by Jiri Svoboda <jiri@…>, 5 years ago

Allow UI to run in the console

Note that everything is way too large.

  • Property mode set to 100644
File size: 25.0 KB
Line 
1/*
2 * Copyright (c) 2021 Jiri Svoboda
3 * Copyright (c) 2012 Petr Koupy
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
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.
17 *
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.
28 */
29
30/** @addtogroup terminal
31 * @{
32 */
33/**
34 * @file Terminal application
35 */
36
37#include <adt/list.h>
38#include <adt/prodcons.h>
39#include <as.h>
40#include <errno.h>
41#include <fbfont/font-8x16.h>
42#include <io/chargrid.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>
50#include <task.h>
51#include <stdarg.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <str.h>
55#include <ui/resource.h>
56#include <ui/ui.h>
57#include <ui/wdecor.h>
58#include <ui/window.h>
59
60#include "terminal.h"
61
62#define NAME "terminal"
63#define NAMESPACE "terminal"
64
65#define LOCFS_MOUNT_POINT "/loc"
66
67#define APP_GETTERM "/app/getterm"
68
69#define TERM_CAPS \
70 (CONSOLE_CAP_STYLE | CONSOLE_CAP_INDEXED | CONSOLE_CAP_RGB)
71
72static LIST_INITIALIZE(terms);
73
74static errno_t term_open(con_srvs_t *, con_srv_t *);
75static errno_t term_close(con_srv_t *);
76static errno_t term_read(con_srv_t *, void *, size_t, size_t *);
77static errno_t term_write(con_srv_t *, void *, size_t, size_t *);
78static void term_sync(con_srv_t *);
79static void term_clear(con_srv_t *);
80static void term_set_pos(con_srv_t *, sysarg_t col, sysarg_t row);
81static errno_t term_get_pos(con_srv_t *, sysarg_t *, sysarg_t *);
82static errno_t term_get_size(con_srv_t *, sysarg_t *, sysarg_t *);
83static errno_t term_get_color_cap(con_srv_t *, console_caps_t *);
84static void term_set_style(con_srv_t *, console_style_t);
85static void term_set_color(con_srv_t *, console_color_t, console_color_t,
86 console_color_attr_t);
87static void term_set_rgb_color(con_srv_t *, pixel_t, pixel_t);
88static void term_set_cursor_visibility(con_srv_t *, bool);
89static errno_t term_get_event(con_srv_t *, cons_event_t *);
90static errno_t term_map(con_srv_t *, sysarg_t, sysarg_t, charfield_t **);
91static void term_unmap(con_srv_t *);
92static void term_buf_update(con_srv_t *, sysarg_t, sysarg_t, sysarg_t,
93 sysarg_t);
94
95static con_ops_t con_ops = {
96 .open = term_open,
97 .close = term_close,
98 .read = term_read,
99 .write = term_write,
100 .sync = term_sync,
101 .clear = term_clear,
102 .set_pos = term_set_pos,
103 .get_pos = term_get_pos,
104 .get_size = term_get_size,
105 .get_color_cap = term_get_color_cap,
106 .set_style = term_set_style,
107 .set_color = term_set_color,
108 .set_rgb_color = term_set_rgb_color,
109 .set_cursor_visibility = term_set_cursor_visibility,
110 .get_event = term_get_event,
111 .map = term_map,
112 .unmap = term_unmap,
113 .update = term_buf_update
114};
115
116static void terminal_close_event(ui_window_t *, void *);
117static void terminal_focus_event(ui_window_t *, void *);
118static void terminal_kbd_event(ui_window_t *, void *, kbd_event_t *);
119static void terminal_pos_event(ui_window_t *, void *, pos_event_t *);
120static void terminal_unfocus_event(ui_window_t *, void *);
121
122static ui_window_cb_t terminal_window_cb = {
123 .close = terminal_close_event,
124 .focus = terminal_focus_event,
125 .kbd = terminal_kbd_event,
126 .pos = terminal_pos_event,
127 .unfocus = terminal_unfocus_event
128};
129
130static terminal_t *srv_to_terminal(con_srv_t *srv)
131{
132 return srv->srvs->sarg;
133}
134
135static void getterm(const char *svc, const char *app)
136{
137 task_spawnl(NULL, NULL, APP_GETTERM, APP_GETTERM, svc,
138 LOCFS_MOUNT_POINT, "--msg", "--wait", "--", app, NULL);
139}
140
141static pixel_t color_table[16] = {
142 [COLOR_BLACK] = PIXEL(255, 0, 0, 0),
143 [COLOR_BLUE] = PIXEL(255, 0, 0, 240),
144 [COLOR_GREEN] = PIXEL(255, 0, 240, 0),
145 [COLOR_CYAN] = PIXEL(255, 0, 240, 240),
146 [COLOR_RED] = PIXEL(255, 240, 0, 0),
147 [COLOR_MAGENTA] = PIXEL(255, 240, 0, 240),
148 [COLOR_YELLOW] = PIXEL(255, 240, 240, 0),
149 [COLOR_WHITE] = PIXEL(255, 240, 240, 240),
150
151 [COLOR_BLACK + 8] = PIXEL(255, 0, 0, 0),
152 [COLOR_BLUE + 8] = PIXEL(255, 0, 0, 255),
153 [COLOR_GREEN + 8] = PIXEL(255, 0, 255, 0),
154 [COLOR_CYAN + 8] = PIXEL(255, 0, 255, 255),
155 [COLOR_RED + 8] = PIXEL(255, 255, 0, 0),
156 [COLOR_MAGENTA + 8] = PIXEL(255, 255, 0, 255),
157 [COLOR_YELLOW + 8] = PIXEL(255, 255, 255, 0),
158 [COLOR_WHITE + 8] = PIXEL(255, 255, 255, 255),
159};
160
161static inline void attrs_rgb(char_attrs_t attrs, pixel_t *bgcolor, pixel_t *fgcolor)
162{
163 switch (attrs.type) {
164 case CHAR_ATTR_STYLE:
165 switch (attrs.val.style) {
166 case STYLE_NORMAL:
167 *bgcolor = color_table[COLOR_WHITE];
168 *fgcolor = color_table[COLOR_BLACK];
169 break;
170 case STYLE_EMPHASIS:
171 *bgcolor = color_table[COLOR_WHITE];
172 *fgcolor = color_table[COLOR_RED];
173 break;
174 case STYLE_INVERTED:
175 *bgcolor = color_table[COLOR_BLACK];
176 *fgcolor = color_table[COLOR_WHITE];
177 break;
178 case STYLE_SELECTED:
179 *bgcolor = color_table[COLOR_RED];
180 *fgcolor = color_table[COLOR_WHITE];
181 break;
182 }
183 break;
184 case CHAR_ATTR_INDEX:
185 *bgcolor = color_table[(attrs.val.index.bgcolor & 7) |
186 ((attrs.val.index.attr & CATTR_BRIGHT) ? 8 : 0)];
187 *fgcolor = color_table[(attrs.val.index.fgcolor & 7) |
188 ((attrs.val.index.attr & CATTR_BRIGHT) ? 8 : 0)];
189 break;
190 case CHAR_ATTR_RGB:
191 *bgcolor = 0xff000000 | attrs.val.rgb.bgcolor;
192 *fgcolor = 0xff000000 | attrs.val.rgb.fgcolor;
193 break;
194 }
195}
196
197static void term_update_region(terminal_t *term, sysarg_t x, sysarg_t y,
198 sysarg_t w, sysarg_t h)
199{
200 gfx_rect_t rect;
201 gfx_rect_t nupdate;
202
203 rect.p0.x = x;
204 rect.p0.y = y;
205 rect.p1.x = x + w;
206 rect.p1.y = y + h;
207
208 gfx_rect_envelope(&term->update, &rect, &nupdate);
209 term->update = nupdate;
210}
211
212static void term_update_char(terminal_t *term, pixelmap_t *pixelmap,
213 sysarg_t sx, sysarg_t sy, sysarg_t col, sysarg_t row)
214{
215 charfield_t *field =
216 chargrid_charfield_at(term->backbuf, col, row);
217
218 bool inverted = chargrid_cursor_at(term->backbuf, col, row);
219
220 sysarg_t bx = sx + (col * FONT_WIDTH);
221 sysarg_t by = sy + (row * FONT_SCANLINES);
222
223 pixel_t bgcolor = 0;
224 pixel_t fgcolor = 0;
225
226 if (inverted)
227 attrs_rgb(field->attrs, &fgcolor, &bgcolor);
228 else
229 attrs_rgb(field->attrs, &bgcolor, &fgcolor);
230
231 // FIXME: Glyph type should be actually uint32_t
232 // for full UTF-32 coverage.
233
234 uint16_t glyph = fb_font_glyph(field->ch, NULL);
235
236 for (unsigned int y = 0; y < FONT_SCANLINES; y++) {
237 pixel_t *dst = pixelmap_pixel_at(pixelmap, bx, by + y);
238 pixel_t *dst_max = pixelmap_pixel_at(pixelmap, bx + FONT_WIDTH - 1, by + y);
239 if (!dst || !dst_max)
240 continue;
241 int count = FONT_WIDTH;
242 while (count-- != 0) {
243 *dst++ = (fb_font[glyph][y] & (1 << count)) ? fgcolor : bgcolor;
244 }
245 }
246 term_update_region(term, bx, by, FONT_WIDTH, FONT_SCANLINES);
247}
248
249static bool term_update_scroll(terminal_t *term, pixelmap_t *pixelmap,
250 sysarg_t sx, sysarg_t sy)
251{
252 sysarg_t top_row = chargrid_get_top_row(term->frontbuf);
253
254 if (term->top_row == top_row) {
255 return false;
256 }
257
258 term->top_row = top_row;
259
260 for (sysarg_t row = 0; row < term->rows; row++) {
261 for (sysarg_t col = 0; col < term->cols; col++) {
262 charfield_t *front_field =
263 chargrid_charfield_at(term->frontbuf, col, row);
264 charfield_t *back_field =
265 chargrid_charfield_at(term->backbuf, col, row);
266 bool update = false;
267
268 if (front_field->ch != back_field->ch) {
269 back_field->ch = front_field->ch;
270 update = true;
271 }
272
273 if (!attrs_same(front_field->attrs, back_field->attrs)) {
274 back_field->attrs = front_field->attrs;
275 update = true;
276 }
277
278 front_field->flags &= ~CHAR_FLAG_DIRTY;
279
280 if (update) {
281 term_update_char(term, pixelmap, sx, sy, col, row);
282 }
283 }
284 }
285
286 return true;
287}
288
289static bool term_update_cursor(terminal_t *term, pixelmap_t *pixelmap,
290 sysarg_t sx, sysarg_t sy)
291{
292 bool update = false;
293
294 sysarg_t front_col;
295 sysarg_t front_row;
296 chargrid_get_cursor(term->frontbuf, &front_col, &front_row);
297
298 sysarg_t back_col;
299 sysarg_t back_row;
300 chargrid_get_cursor(term->backbuf, &back_col, &back_row);
301
302 bool front_visibility =
303 chargrid_get_cursor_visibility(term->frontbuf) &&
304 term->is_focused;
305 bool back_visibility =
306 chargrid_get_cursor_visibility(term->backbuf);
307
308 if (front_visibility != back_visibility) {
309 chargrid_set_cursor_visibility(term->backbuf,
310 front_visibility);
311 term_update_char(term, pixelmap, sx, sy, back_col, back_row);
312 update = true;
313 }
314
315 if ((front_col != back_col) || (front_row != back_row)) {
316 chargrid_set_cursor(term->backbuf, front_col, front_row);
317 term_update_char(term, pixelmap, sx, sy, back_col, back_row);
318 term_update_char(term, pixelmap, sx, sy, front_col, front_row);
319 update = true;
320 }
321
322 return update;
323}
324
325static void term_update(terminal_t *term)
326{
327 pixelmap_t pixelmap;
328 gfx_bitmap_alloc_t alloc;
329 gfx_coord2_t pos;
330 errno_t rc;
331
332 rc = gfx_bitmap_get_alloc(term->bmp, &alloc);
333 if (rc != EOK) {
334 return;
335 }
336
337 fibril_mutex_lock(&term->mtx);
338 pixelmap.width = term->w;
339 pixelmap.height = term->h;
340 pixelmap.data = alloc.pixels;
341
342 bool update = false;
343 sysarg_t sx = 0;
344 sysarg_t sy = 0;
345
346 if (term_update_scroll(term, &pixelmap, sx, sy)) {
347 update = true;
348 } else {
349 for (sysarg_t y = 0; y < term->rows; y++) {
350 for (sysarg_t x = 0; x < term->cols; x++) {
351 charfield_t *front_field =
352 chargrid_charfield_at(term->frontbuf, x, y);
353 charfield_t *back_field =
354 chargrid_charfield_at(term->backbuf, x, y);
355 bool cupdate = false;
356
357 if ((front_field->flags & CHAR_FLAG_DIRTY) ==
358 CHAR_FLAG_DIRTY) {
359 if (front_field->ch != back_field->ch) {
360 back_field->ch = front_field->ch;
361 cupdate = true;
362 }
363
364 if (!attrs_same(front_field->attrs,
365 back_field->attrs)) {
366 back_field->attrs = front_field->attrs;
367 cupdate = true;
368 }
369
370 front_field->flags &= ~CHAR_FLAG_DIRTY;
371 }
372
373 if (cupdate) {
374 term_update_char(term, &pixelmap, sx, sy, x, y);
375 update = true;
376 }
377 }
378 }
379 }
380
381 if (term_update_cursor(term, &pixelmap, sx, sy))
382 update = true;
383
384 if (update) {
385 pos.x = 4;
386 pos.y = 26;
387 (void) gfx_bitmap_render(term->bmp, &term->update, &pos);
388
389 term->update.p0.x = 0;
390 term->update.p0.y = 0;
391 term->update.p1.x = 0;
392 term->update.p1.y = 0;
393 }
394
395 fibril_mutex_unlock(&term->mtx);
396}
397
398static void term_repaint(terminal_t *term)
399{
400 pixelmap_t pixelmap;
401 gfx_bitmap_alloc_t alloc;
402 errno_t rc;
403
404 rc = gfx_bitmap_get_alloc(term->bmp, &alloc);
405 if (rc != EOK) {
406 printf("Error getting bitmap allocation info.\n");
407 return;
408 }
409
410 fibril_mutex_lock(&term->mtx);
411
412 pixelmap.width = term->w;
413 pixelmap.height = term->h;
414 pixelmap.data = alloc.pixels;
415
416 sysarg_t sx = 0;
417 sysarg_t sy = 0;
418
419 if (!term_update_scroll(term, &pixelmap, sx, sy)) {
420 for (sysarg_t y = 0; y < term->rows; y++) {
421 for (sysarg_t x = 0; x < term->cols; x++) {
422 charfield_t *front_field =
423 chargrid_charfield_at(term->frontbuf, x, y);
424 charfield_t *back_field =
425 chargrid_charfield_at(term->backbuf, x, y);
426
427 back_field->ch = front_field->ch;
428 back_field->attrs = front_field->attrs;
429 front_field->flags &= ~CHAR_FLAG_DIRTY;
430
431 term_update_char(term, &pixelmap, sx, sy, x, y);
432 }
433 }
434 }
435
436 term_update_cursor(term, &pixelmap, sx, sy);
437
438 fibril_mutex_unlock(&term->mtx);
439}
440
441static errno_t term_open(con_srvs_t *srvs, con_srv_t *srv)
442{
443 return EOK;
444}
445
446static errno_t term_close(con_srv_t *srv)
447{
448 return EOK;
449}
450
451static errno_t term_read(con_srv_t *srv, void *buf, size_t size, size_t *nread)
452{
453 terminal_t *term = srv_to_terminal(srv);
454 uint8_t *bbuf = buf;
455 size_t pos = 0;
456
457 /*
458 * Read input from keyboard and copy it to the buffer.
459 * We need to handle situation when wchar is split by 2 following
460 * reads.
461 */
462 while (pos < size) {
463 /* Copy to the buffer remaining characters. */
464 while ((pos < size) && (term->char_remains_len > 0)) {
465 bbuf[pos] = term->char_remains[0];
466 pos++;
467
468 /* Unshift the array. */
469 for (size_t i = 1; i < term->char_remains_len; i++)
470 term->char_remains[i - 1] = term->char_remains[i];
471
472 term->char_remains_len--;
473 }
474
475 /* Still not enough? Then get another key from the queue. */
476 if (pos < size) {
477 link_t *link = prodcons_consume(&term->input_pc);
478 cons_event_t *event = list_get_instance(link, cons_event_t, link);
479
480 /* Accept key presses of printable chars only. */
481 if (event->type == CEV_KEY && event->ev.key.type == KEY_PRESS &&
482 event->ev.key.c != 0) {
483 char32_t tmp[2] = {
484 event->ev.key.c,
485 0
486 };
487
488 wstr_to_str(term->char_remains, UTF8_CHAR_BUFFER_SIZE, tmp);
489 term->char_remains_len = str_size(term->char_remains);
490 }
491
492 free(event);
493 }
494 }
495
496 *nread = size;
497 return EOK;
498}
499
500static void term_write_char(terminal_t *term, wchar_t ch)
501{
502 sysarg_t updated = 0;
503
504 fibril_mutex_lock(&term->mtx);
505
506 switch (ch) {
507 case '\n':
508 updated = chargrid_newline(term->frontbuf);
509 break;
510 case '\r':
511 break;
512 case '\t':
513 updated = chargrid_tabstop(term->frontbuf, 8);
514 break;
515 case '\b':
516 updated = chargrid_backspace(term->frontbuf);
517 break;
518 default:
519 updated = chargrid_putuchar(term->frontbuf, ch, true);
520 }
521
522 fibril_mutex_unlock(&term->mtx);
523
524 if (updated > 1)
525 term_update(term);
526}
527
528static errno_t term_write(con_srv_t *srv, void *data, size_t size, size_t *nwritten)
529{
530 terminal_t *term = srv_to_terminal(srv);
531
532 size_t off = 0;
533 while (off < size)
534 term_write_char(term, str_decode(data, &off, size));
535
536 gfx_update(term->gc);
537 *nwritten = size;
538 return EOK;
539}
540
541static void term_sync(con_srv_t *srv)
542{
543 terminal_t *term = srv_to_terminal(srv);
544
545 term_update(term);
546 gfx_update(term->gc);
547}
548
549static void term_clear(con_srv_t *srv)
550{
551 terminal_t *term = srv_to_terminal(srv);
552
553 fibril_mutex_lock(&term->mtx);
554 chargrid_clear(term->frontbuf);
555 fibril_mutex_unlock(&term->mtx);
556
557 term_update(term);
558 gfx_update(term->gc);
559}
560
561static void term_set_pos(con_srv_t *srv, sysarg_t col, sysarg_t row)
562{
563 terminal_t *term = srv_to_terminal(srv);
564
565 fibril_mutex_lock(&term->mtx);
566 chargrid_set_cursor(term->frontbuf, col, row);
567 fibril_mutex_unlock(&term->mtx);
568
569 term_update(term);
570 gfx_update(term->gc);
571}
572
573static errno_t term_get_pos(con_srv_t *srv, sysarg_t *col, sysarg_t *row)
574{
575 terminal_t *term = srv_to_terminal(srv);
576
577 fibril_mutex_lock(&term->mtx);
578 chargrid_get_cursor(term->frontbuf, col, row);
579 fibril_mutex_unlock(&term->mtx);
580
581 return EOK;
582}
583
584static errno_t term_get_size(con_srv_t *srv, sysarg_t *cols, sysarg_t *rows)
585{
586 terminal_t *term = srv_to_terminal(srv);
587
588 fibril_mutex_lock(&term->mtx);
589 *cols = term->cols;
590 *rows = term->rows;
591 fibril_mutex_unlock(&term->mtx);
592
593 return EOK;
594}
595
596static errno_t term_get_color_cap(con_srv_t *srv, console_caps_t *caps)
597{
598 (void) srv;
599 *caps = TERM_CAPS;
600
601 return EOK;
602}
603
604static void term_set_style(con_srv_t *srv, console_style_t style)
605{
606 terminal_t *term = srv_to_terminal(srv);
607
608 fibril_mutex_lock(&term->mtx);
609 chargrid_set_style(term->frontbuf, style);
610 fibril_mutex_unlock(&term->mtx);
611}
612
613static void term_set_color(con_srv_t *srv, console_color_t bgcolor,
614 console_color_t fgcolor, console_color_attr_t attr)
615{
616 terminal_t *term = srv_to_terminal(srv);
617
618 fibril_mutex_lock(&term->mtx);
619 chargrid_set_color(term->frontbuf, bgcolor, fgcolor, attr);
620 fibril_mutex_unlock(&term->mtx);
621}
622
623static void term_set_rgb_color(con_srv_t *srv, pixel_t bgcolor,
624 pixel_t fgcolor)
625{
626 terminal_t *term = srv_to_terminal(srv);
627
628 fibril_mutex_lock(&term->mtx);
629 chargrid_set_rgb_color(term->frontbuf, bgcolor, fgcolor);
630 fibril_mutex_unlock(&term->mtx);
631}
632
633static void term_set_cursor_visibility(con_srv_t *srv, bool visible)
634{
635 terminal_t *term = srv_to_terminal(srv);
636
637 fibril_mutex_lock(&term->mtx);
638 chargrid_set_cursor_visibility(term->frontbuf, visible);
639 fibril_mutex_unlock(&term->mtx);
640
641 term_update(term);
642 gfx_update(term->gc);
643}
644
645static errno_t term_get_event(con_srv_t *srv, cons_event_t *event)
646{
647 terminal_t *term = srv_to_terminal(srv);
648 link_t *link = prodcons_consume(&term->input_pc);
649 cons_event_t *ev = list_get_instance(link, cons_event_t, link);
650
651 *event = *ev;
652 free(ev);
653 return EOK;
654}
655
656/** Create shared buffer for efficient rendering.
657 *
658 * @param srv Console server
659 * @param cols Number of columns in buffer
660 * @param rows Number of rows in buffer
661 * @param rbuf Place to store pointer to new sharable buffer
662 *
663 * @return EOK on sucess or an error code
664 */
665static errno_t term_map(con_srv_t *srv, sysarg_t cols, sysarg_t rows,
666 charfield_t **rbuf)
667{
668 terminal_t *term = srv_to_terminal(srv);
669 void *buf;
670
671 fibril_mutex_lock(&term->mtx);
672
673 if (term->ubuf != NULL) {
674 fibril_mutex_unlock(&term->mtx);
675 return EBUSY;
676 }
677
678 buf = as_area_create(AS_AREA_ANY, cols * rows * sizeof(charfield_t),
679 AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE, AS_AREA_UNPAGED);
680 if (buf == AS_MAP_FAILED) {
681 fibril_mutex_unlock(&term->mtx);
682 return ENOMEM;
683 }
684
685 term->ucols = cols;
686 term->urows = rows;
687 term->ubuf = buf;
688 fibril_mutex_unlock(&term->mtx);
689
690 *rbuf = buf;
691 return EOK;
692}
693
694/** Delete shared buffer.
695 *
696 * @param srv Console server
697 */
698static void term_unmap(con_srv_t *srv)
699{
700 terminal_t *term = srv_to_terminal(srv);
701 void *buf;
702
703 fibril_mutex_lock(&term->mtx);
704
705 buf = term->ubuf;
706 term->ubuf = NULL;
707
708 if (buf != NULL)
709 as_area_destroy(buf);
710
711 fibril_mutex_unlock(&term->mtx);
712}
713
714/** Update area of terminal from shared buffer.
715 *
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)
721 */
722static void term_buf_update(con_srv_t *srv, sysarg_t c0, sysarg_t r0,
723 sysarg_t c1, sysarg_t r1)
724{
725 terminal_t *term = srv_to_terminal(srv);
726 charfield_t *ch;
727 sysarg_t col, row;
728
729 fibril_mutex_lock(&term->mtx);
730
731 if (term->ubuf == NULL) {
732 fibril_mutex_unlock(&term->mtx);
733 return;
734 }
735
736 /* Make sure we have meaningful coordinates, within bounds */
737
738 if (c1 > term->ucols)
739 c1 = term->ucols;
740 if (c1 > term->cols)
741 c1 = term->cols;
742 if (c0 >= c1) {
743 fibril_mutex_unlock(&term->mtx);
744 return;
745 }
746 if (r1 > term->urows)
747 r1 = term->urows;
748 if (r1 > term->rows)
749 r1 = term->rows;
750 if (r0 >= r1) {
751 fibril_mutex_unlock(&term->mtx);
752 return;
753 }
754
755 /* Update front buffer from user buffer */
756
757 for (row = r0; row < r1; row++) {
758 for (col = c0; col < c1; col++) {
759 ch = chargrid_charfield_at(term->frontbuf, col, row);
760 *ch = term->ubuf[row * term->ucols + col];
761 }
762 }
763
764 fibril_mutex_unlock(&term->mtx);
765
766 /* Update terminal */
767 term_update(term);
768 gfx_update(term->gc);
769}
770
771static void deinit_terminal(terminal_t *term)
772{
773 list_remove(&term->link);
774
775 if (term->frontbuf)
776 chargrid_destroy(term->frontbuf);
777
778 if (term->backbuf)
779 chargrid_destroy(term->backbuf);
780}
781
782void terminal_destroy(terminal_t *term)
783{
784 deinit_terminal(term);
785 free(term);
786}
787
788static void terminal_queue_cons_event(terminal_t *term, cons_event_t *ev)
789{
790 /* Got key press/release event */
791 cons_event_t *event =
792 (cons_event_t *) malloc(sizeof(cons_event_t));
793 if (event == NULL)
794 return;
795
796 *event = *ev;
797 link_initialize(&event->link);
798
799 prodcons_produce(&term->input_pc, &event->link);
800}
801
802/** Handle window close event. */
803static void terminal_close_event(ui_window_t *window, void *arg)
804{
805 terminal_t *term = (terminal_t *) arg;
806
807 (void) term;
808
809 // XXX This is not really a clean way of terminating
810 exit(0);
811}
812
813/** Handle window focus event. */
814static void terminal_focus_event(ui_window_t *window, void *arg)
815{
816 terminal_t *term = (terminal_t *) arg;
817
818 term->is_focused = true;
819 term_update(term);
820 gfx_update(term->gc);
821}
822
823/** Handle window keyboard event */
824static void terminal_kbd_event(ui_window_t *window, void *arg,
825 kbd_event_t *kbd_event)
826{
827 terminal_t *term = (terminal_t *) arg;
828 cons_event_t event;
829
830 event.type = CEV_KEY;
831 event.ev.key = *kbd_event;
832
833 terminal_queue_cons_event(term, &event);
834}
835
836/** Handle window position event */
837static void terminal_pos_event(ui_window_t *window, void *arg, pos_event_t *event)
838{
839 cons_event_t cevent;
840 terminal_t *term = (terminal_t *) arg;
841
842 sysarg_t sx = -term->off.x;
843 sysarg_t sy = -term->off.y;
844
845 if (event->type == POS_PRESS || event->type == POS_RELEASE) {
846 cevent.type = CEV_POS;
847 cevent.ev.pos.type = event->type;
848 cevent.ev.pos.pos_id = event->pos_id;
849 cevent.ev.pos.btn_num = event->btn_num;
850
851 cevent.ev.pos.hpos = (event->hpos - sx) / FONT_WIDTH;
852 cevent.ev.pos.vpos = (event->vpos - sy) / FONT_SCANLINES;
853 terminal_queue_cons_event(term, &cevent);
854 }
855}
856
857/** Handle window unfocus event. */
858static void terminal_unfocus_event(ui_window_t *window, void *arg)
859{
860 terminal_t *term = (terminal_t *) arg;
861
862 term->is_focused = false;
863 term_update(term);
864 gfx_update(term->gc);
865}
866
867static void term_connection(ipc_call_t *icall, void *arg)
868{
869 terminal_t *term = NULL;
870
871 list_foreach(terms, link, terminal_t, cur) {
872 if (cur->dsid == (service_id_t) ipc_get_arg2(icall)) {
873 term = cur;
874 break;
875 }
876 }
877
878 if (term == NULL) {
879 async_answer_0(icall, ENOENT);
880 return;
881 }
882
883 if (!atomic_flag_test_and_set(&term->refcnt))
884 chargrid_set_cursor_visibility(term->frontbuf, true);
885
886 con_conn(icall, &term->srvs);
887}
888
889errno_t terminal_create(const char *display_spec, sysarg_t width,
890 sysarg_t height, terminal_flags_t flags, terminal_t **rterm)
891{
892 terminal_t *term;
893 gfx_bitmap_params_t params;
894 ui_wnd_params_t wparams;
895 gfx_rect_t rect;
896 gfx_coord2_t off;
897 gfx_rect_t wrect;
898 errno_t rc;
899
900 term = calloc(1, sizeof(terminal_t));
901 if (term == NULL) {
902 printf("Out of memory.\n");
903 return ENOMEM;
904 }
905
906 link_initialize(&term->link);
907 fibril_mutex_initialize(&term->mtx);
908 atomic_flag_clear(&term->refcnt);
909
910 prodcons_initialize(&term->input_pc);
911 term->char_remains_len = 0;
912
913 term->w = width;
914 term->h = height;
915
916 term->cols = width / FONT_WIDTH;
917 term->rows = height / FONT_SCANLINES;
918
919 term->frontbuf = NULL;
920 term->backbuf = NULL;
921
922 term->frontbuf = chargrid_create(term->cols, term->rows,
923 CHARGRID_FLAG_NONE);
924 if (!term->frontbuf) {
925 printf("Error creating front buffer.\n");
926 rc = ENOMEM;
927 goto error;
928 }
929
930 term->backbuf = chargrid_create(term->cols, term->rows,
931 CHARGRID_FLAG_NONE);
932 if (!term->backbuf) {
933 printf("Error creating back buffer.\n");
934 rc = ENOMEM;
935 goto error;
936 }
937
938 rect.p0.x = 0;
939 rect.p0.y = 0;
940 rect.p1.x = width;
941 rect.p1.y = height;
942
943 ui_wnd_params_init(&wparams);
944 wparams.caption = "Terminal";
945 if ((flags & tf_topleft) != 0)
946 wparams.placement = ui_wnd_place_top_left;
947
948 /*
949 * Compute window rectangle such that application area corresponds
950 * to rect
951 */
952 ui_wdecor_rect_from_app(wparams.style, &rect, &wrect);
953 off = wrect.p0;
954 gfx_rect_rtranslate(&off, &wrect, &wparams.rect);
955
956 term->off = off;
957
958 rc = ui_create(display_spec, &term->ui);
959 if (rc != EOK) {
960 printf("Error creating UI on %s.\n", display_spec);
961 goto error;
962 }
963
964 rc = ui_window_create(term->ui, &wparams, &term->window);
965 if (rc != EOK) {
966 printf("Error creating window.\n");
967 goto error;
968 }
969
970 term->gc = ui_window_get_gc(term->window);
971 term->ui_res = ui_window_get_res(term->window);
972
973 ui_window_set_cb(term->window, &terminal_window_cb, (void *) term);
974
975 gfx_bitmap_params_init(&params);
976 params.rect.p0.x = 0;
977 params.rect.p0.y = 0;
978 params.rect.p1.x = width;
979 params.rect.p1.y = height;
980
981 rc = gfx_bitmap_create(term->gc, &params, NULL, &term->bmp);
982 if (rc != EOK) {
983 printf("Error allocating screen bitmap.\n");
984 goto error;
985 }
986
987 chargrid_clear(term->frontbuf);
988 chargrid_clear(term->backbuf);
989 term->top_row = 0;
990
991 async_set_fallback_port_handler(term_connection, NULL);
992 con_srvs_init(&term->srvs);
993 term->srvs.ops = &con_ops;
994 term->srvs.sarg = term;
995
996 rc = loc_server_register(NAME);
997 if (rc != EOK) {
998 printf("Error registering server.\n");
999 rc = EIO;
1000 goto error;
1001 }
1002
1003 char vc[LOC_NAME_MAXLEN + 1];
1004 snprintf(vc, LOC_NAME_MAXLEN, "%s/%" PRIu64, NAMESPACE,
1005 task_get_id());
1006
1007 rc = loc_service_register(vc, &term->dsid);
1008 if (rc != EOK) {
1009 printf("Error registering service.\n");
1010 rc = EIO;
1011 goto error;
1012 }
1013
1014 list_append(&term->link, &terms);
1015 getterm(vc, "/app/bdsh");
1016
1017 term->is_focused = true;
1018
1019 term->update.p0.x = 0;
1020 term->update.p0.y = 0;
1021 term->update.p1.x = 0;
1022 term->update.p1.y = 0;
1023
1024 term_repaint(term);
1025
1026 *rterm = term;
1027 return EOK;
1028error:
1029 if (term->window != NULL)
1030 ui_window_destroy(term->window);
1031 if (term->ui != NULL)
1032 ui_destroy(term->ui);
1033 if (term->frontbuf != NULL)
1034 chargrid_destroy(term->frontbuf);
1035 if (term->backbuf != NULL)
1036 chargrid_destroy(term->backbuf);
1037 free(term);
1038 return rc;
1039}
1040
1041/** @}
1042 */
Note: See TracBrowser for help on using the repository browser.