source: mainline/uspace/srv/hid/console/console.c@ b48e680f

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

Allow console application to set the terminal window caption

  • Property mode set to 100644
File size: 23.3 KB
Line 
1/*
2 * Copyright (c) 2021 Jiri Svoboda
3 * Copyright (c) 2011 Martin Decky
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 console
31 * @{
32 */
33/** @file
34 */
35
36#include <async.h>
37#include <stdio.h>
38#include <adt/prodcons.h>
39#include <io/input.h>
40#include <ipc/vfs.h>
41#include <errno.h>
42#include <str_error.h>
43#include <loc.h>
44#include <io/con_srv.h>
45#include <io/kbd_event.h>
46#include <io/keycode.h>
47#include <io/chargrid.h>
48#include <io/output.h>
49#include <align.h>
50#include <as.h>
51#include <task.h>
52#include <fibril_synch.h>
53#include <stdatomic.h>
54#include <stdlib.h>
55#include <str.h>
56#include "console.h"
57
58#define NAME "console"
59#define NAMESPACE "term"
60
61#define UTF8_CHAR_BUFFER_SIZE (STR_BOUNDS(1) + 1)
62
63typedef struct {
64 atomic_flag refcnt; /**< Connection reference count */
65 prodcons_t input_pc; /**< Incoming console events */
66
67 /**
68 * Not yet sent bytes of last char event.
69 */
70 char char_remains[UTF8_CHAR_BUFFER_SIZE];
71 size_t char_remains_len; /**< Number of not yet sent bytes. */
72
73 fibril_mutex_t mtx; /**< Lock protecting mutable fields */
74
75 size_t index; /**< Console index */
76 service_id_t dsid; /**< Service handle */
77
78 sysarg_t cols; /**< Number of columns */
79 sysarg_t rows; /**< Number of rows */
80 console_caps_t ccaps; /**< Console capabilities */
81
82 sysarg_t ucols; /**< Number of columns in user buffer */
83 sysarg_t urows; /**< Number of rows in user buffer */
84 charfield_t *ubuf; /**< User buffer */
85
86 chargrid_t *frontbuf; /**< Front buffer */
87 frontbuf_handle_t fbid; /**< Front buffer handle */
88 con_srvs_t srvs; /**< Console service setup */
89} console_t;
90
91/** Input server proxy */
92static input_t *input;
93static bool active = false;
94
95/** Session to the output server */
96static async_sess_t *output_sess;
97
98/** Output dimensions */
99static sysarg_t cols;
100static sysarg_t rows;
101
102/** Mouse pointer X coordinate */
103static int pointer_x;
104/** Mouse pointer Y coordinate */
105static int pointer_y;
106/** Character under mouse cursor */
107static charfield_t pointer_bg;
108
109static int mouse_scale_x = 4;
110static int mouse_scale_y = 8;
111
112/** Array of data for virtual consoles */
113static console_t consoles[CONSOLE_COUNT];
114
115/** Mutex for console switching */
116static FIBRIL_MUTEX_INITIALIZE(switch_mtx);
117
118static console_t *active_console = &consoles[0];
119
120static errno_t input_ev_active(input_t *);
121static errno_t input_ev_deactive(input_t *);
122static errno_t input_ev_key(input_t *, kbd_event_type_t, keycode_t, keymod_t, char32_t);
123static errno_t input_ev_move(input_t *, int, int);
124static errno_t input_ev_abs_move(input_t *, unsigned, unsigned, unsigned, unsigned);
125static errno_t input_ev_button(input_t *, int, int);
126static errno_t input_ev_dclick(input_t *, int);
127
128static input_ev_ops_t input_ev_ops = {
129 .active = input_ev_active,
130 .deactive = input_ev_deactive,
131 .key = input_ev_key,
132 .move = input_ev_move,
133 .abs_move = input_ev_abs_move,
134 .button = input_ev_button,
135 .dclick = input_ev_dclick
136};
137
138static errno_t cons_open(con_srvs_t *, con_srv_t *);
139static errno_t cons_close(con_srv_t *);
140static errno_t cons_read(con_srv_t *, void *, size_t, size_t *);
141static errno_t cons_write(con_srv_t *, void *, size_t, size_t *);
142static void cons_sync(con_srv_t *);
143static void cons_clear(con_srv_t *);
144static void cons_set_pos(con_srv_t *, sysarg_t col, sysarg_t row);
145static errno_t cons_get_pos(con_srv_t *, sysarg_t *, sysarg_t *);
146static errno_t cons_get_size(con_srv_t *, sysarg_t *, sysarg_t *);
147static errno_t cons_get_color_cap(con_srv_t *, console_caps_t *);
148static void cons_set_style(con_srv_t *, console_style_t);
149static void cons_set_color(con_srv_t *, console_color_t, console_color_t,
150 console_color_attr_t);
151static void cons_set_rgb_color(con_srv_t *, pixel_t, pixel_t);
152static void cons_set_cursor_visibility(con_srv_t *, bool);
153static errno_t cons_set_caption(con_srv_t *, const char *);
154static errno_t cons_get_event(con_srv_t *, cons_event_t *);
155static errno_t cons_map(con_srv_t *, sysarg_t, sysarg_t, charfield_t **);
156static void cons_unmap(con_srv_t *);
157static void cons_buf_update(con_srv_t *, sysarg_t, sysarg_t, sysarg_t,
158 sysarg_t);
159
160static con_ops_t con_ops = {
161 .open = cons_open,
162 .close = cons_close,
163 .read = cons_read,
164 .write = cons_write,
165 .sync = cons_sync,
166 .clear = cons_clear,
167 .set_pos = cons_set_pos,
168 .get_pos = cons_get_pos,
169 .get_size = cons_get_size,
170 .get_color_cap = cons_get_color_cap,
171 .set_style = cons_set_style,
172 .set_color = cons_set_color,
173 .set_rgb_color = cons_set_rgb_color,
174 .set_cursor_visibility = cons_set_cursor_visibility,
175 .set_caption = cons_set_caption,
176 .get_event = cons_get_event,
177 .map = cons_map,
178 .unmap = cons_unmap,
179 .update = cons_buf_update
180};
181
182static void pointer_draw(void);
183static void pointer_undraw(void);
184
185static console_t *srv_to_console(con_srv_t *srv)
186{
187 return srv->srvs->sarg;
188}
189
190static void cons_update(console_t *cons)
191{
192 fibril_mutex_lock(&switch_mtx);
193 fibril_mutex_lock(&cons->mtx);
194
195 if ((active) && (cons == active_console)) {
196 output_update(output_sess, cons->fbid);
197 output_cursor_update(output_sess, cons->fbid);
198 }
199
200 fibril_mutex_unlock(&cons->mtx);
201 fibril_mutex_unlock(&switch_mtx);
202}
203
204static void cons_update_cursor(console_t *cons)
205{
206 fibril_mutex_lock(&switch_mtx);
207 fibril_mutex_lock(&cons->mtx);
208
209 if ((active) && (cons == active_console))
210 output_cursor_update(output_sess, cons->fbid);
211
212 fibril_mutex_unlock(&cons->mtx);
213 fibril_mutex_unlock(&switch_mtx);
214}
215
216static void cons_damage(console_t *cons)
217{
218 fibril_mutex_lock(&switch_mtx);
219 fibril_mutex_lock(&cons->mtx);
220
221 if ((active) && (cons == active_console)) {
222 output_damage(output_sess, cons->fbid, 0, 0, cons->cols,
223 cons->rows);
224 output_cursor_update(output_sess, cons->fbid);
225 }
226
227 fibril_mutex_unlock(&cons->mtx);
228 fibril_mutex_unlock(&switch_mtx);
229}
230
231static void cons_switch(unsigned int index)
232{
233 /*
234 * The first undefined index is reserved
235 * for switching to the kernel console.
236 */
237 if (index == CONSOLE_COUNT) {
238 if (console_kcon())
239 active = false;
240
241 return;
242 }
243
244 if (index > CONSOLE_COUNT)
245 return;
246
247 console_t *cons = &consoles[index];
248
249 fibril_mutex_lock(&switch_mtx);
250 pointer_undraw();
251
252 if (cons == active_console) {
253 fibril_mutex_unlock(&switch_mtx);
254 return;
255 }
256
257 active_console = cons;
258
259 pointer_draw();
260 fibril_mutex_unlock(&switch_mtx);
261
262 cons_damage(cons);
263}
264
265/** Draw mouse pointer. */
266static void pointer_draw(void)
267{
268 charfield_t *ch;
269 int col, row;
270
271 /* Downscale coordinates to text resolution */
272 col = pointer_x / mouse_scale_x;
273 row = pointer_y / mouse_scale_y;
274
275 /* Make sure they are in range */
276 if (col < 0 || row < 0 || col >= (int)cols || row >= (int)rows)
277 return;
278
279 ch = chargrid_charfield_at(active_console->frontbuf, col, row);
280
281 /*
282 * Store background attributes for undrawing the pointer.
283 * This is necessary as styles cannot be inverted with
284 * round trip (unlike RGB or INDEX)
285 */
286 pointer_bg = *ch;
287
288 /* In general the color should be a one's complement of the background */
289 if (ch->attrs.type == CHAR_ATTR_INDEX) {
290 ch->attrs.val.index.bgcolor ^= 0xf;
291 ch->attrs.val.index.fgcolor ^= 0xf;
292 } else if (ch->attrs.type == CHAR_ATTR_RGB) {
293 ch->attrs.val.rgb.fgcolor ^= 0xffffff;
294 ch->attrs.val.rgb.bgcolor ^= 0xffffff;
295 } else if (ch->attrs.type == CHAR_ATTR_STYLE) {
296 /* Don't have a proper inverse for each style */
297 if (ch->attrs.val.style == STYLE_INVERTED)
298 ch->attrs.val.style = STYLE_NORMAL;
299 else
300 ch->attrs.val.style = STYLE_INVERTED;
301 }
302
303 /* Make sure the cell gets updated */
304 ch->flags |= CHAR_FLAG_DIRTY;
305}
306
307/** Undraw mouse pointer. */
308static void pointer_undraw(void)
309{
310 charfield_t *ch;
311 int col, row;
312
313 col = pointer_x / mouse_scale_x;
314 row = pointer_y / mouse_scale_y;
315 if (col < 0 || row < 0 || col >= (int)cols || row >= (int)rows)
316 return;
317
318 ch = chargrid_charfield_at(active_console->frontbuf, col, row);
319 *ch = pointer_bg;
320 ch->flags |= CHAR_FLAG_DIRTY;
321}
322
323/** Queue console event.
324 *
325 * @param cons Console
326 * @param ev Console event
327 */
328static void console_queue_cons_event(console_t *cons, cons_event_t *ev)
329{
330 /* Got key press/release event */
331 cons_event_t *event =
332 (cons_event_t *) malloc(sizeof(cons_event_t));
333 if (event == NULL)
334 return;
335
336 *event = *ev;
337 link_initialize(&event->link);
338
339 prodcons_produce(&cons->input_pc, &event->link);
340}
341
342static errno_t input_ev_active(input_t *input)
343{
344 active = true;
345 output_claim(output_sess);
346 cons_damage(active_console);
347
348 return EOK;
349}
350
351static errno_t input_ev_deactive(input_t *input)
352{
353 active = false;
354 output_yield(output_sess);
355
356 return EOK;
357}
358
359static errno_t input_ev_key(input_t *input, kbd_event_type_t type, keycode_t key,
360 keymod_t mods, char32_t c)
361{
362 cons_event_t event;
363
364 if ((key >= KC_F1) && (key <= KC_F1 + CONSOLE_COUNT) &&
365 ((mods & KM_CTRL) == 0)) {
366 cons_switch(key - KC_F1);
367 } else {
368 /* Got key press/release event */
369 event.type = CEV_KEY;
370
371 event.ev.key.type = type;
372 event.ev.key.key = key;
373 event.ev.key.mods = mods;
374 event.ev.key.c = c;
375
376 console_queue_cons_event(active_console, &event);
377 }
378
379 return EOK;
380}
381
382/** Update pointer position.
383 *
384 * @param new_x New X coordinate (in pixels)
385 * @param new_y New Y coordinate (in pixels)
386 */
387static void pointer_update(int new_x, int new_y)
388{
389 bool upd_pointer;
390
391 /* Make sure coordinates are in range */
392
393 if (new_x < 0)
394 new_x = 0;
395 if (new_x >= (int)cols * mouse_scale_x)
396 new_x = cols * mouse_scale_x - 1;
397 if (new_y < 0)
398 new_y = 0;
399 if (new_y >= (int)rows * mouse_scale_y)
400 new_y = rows * mouse_scale_y - 1;
401
402 /* Determine if pointer moved to a different character cell */
403 upd_pointer = (new_x / mouse_scale_x != pointer_x / mouse_scale_x) ||
404 (new_y / mouse_scale_y != pointer_y / mouse_scale_y);
405
406 if (upd_pointer)
407 pointer_undraw();
408
409 /* Store new pointer position */
410 pointer_x = new_x;
411 pointer_y = new_y;
412
413 if (upd_pointer) {
414 pointer_draw();
415 cons_update(active_console);
416 }
417}
418
419static errno_t input_ev_move(input_t *input, int dx, int dy)
420{
421 pointer_update(pointer_x + dx, pointer_y + dy);
422 return EOK;
423}
424
425static errno_t input_ev_abs_move(input_t *input, unsigned x, unsigned y,
426 unsigned max_x, unsigned max_y)
427{
428 pointer_update(mouse_scale_x * cols * x / max_x, mouse_scale_y * rows * y / max_y);
429 return EOK;
430}
431
432static errno_t input_ev_button(input_t *input, int bnum, int bpress)
433{
434 cons_event_t event;
435
436 event.type = CEV_POS;
437 event.ev.pos.type = bpress ? POS_PRESS : POS_RELEASE;
438 event.ev.pos.btn_num = bnum;
439 event.ev.pos.hpos = pointer_x / mouse_scale_x;
440 event.ev.pos.vpos = pointer_y / mouse_scale_y;
441
442 console_queue_cons_event(active_console, &event);
443 return EOK;
444}
445
446static errno_t input_ev_dclick(input_t *input, int bnum)
447{
448 cons_event_t event;
449
450 event.type = CEV_POS;
451 event.ev.pos.type = POS_DCLICK;
452 event.ev.pos.btn_num = bnum;
453 event.ev.pos.hpos = pointer_x / mouse_scale_x;
454 event.ev.pos.vpos = pointer_y / mouse_scale_y;
455
456 console_queue_cons_event(active_console, &event);
457 return EOK;
458}
459
460/** Process a character from the client (TTY emulation). */
461static void cons_write_char(console_t *cons, char32_t ch)
462{
463 sysarg_t updated = 0;
464
465 fibril_mutex_lock(&cons->mtx);
466 pointer_undraw();
467
468 switch (ch) {
469 case '\n':
470 updated = chargrid_newline(cons->frontbuf);
471 break;
472 case '\r':
473 break;
474 case '\t':
475 updated = chargrid_tabstop(cons->frontbuf, 8);
476 break;
477 case '\b':
478 updated = chargrid_backspace(cons->frontbuf);
479 break;
480 default:
481 updated = chargrid_putuchar(cons->frontbuf, ch, true);
482 }
483
484 pointer_draw();
485 fibril_mutex_unlock(&cons->mtx);
486
487 if (updated > 1)
488 cons_update(cons);
489}
490
491static void cons_set_cursor_vis(console_t *cons, bool visible)
492{
493 fibril_mutex_lock(&cons->mtx);
494 pointer_undraw();
495 chargrid_set_cursor_visibility(cons->frontbuf, visible);
496 pointer_draw();
497 fibril_mutex_unlock(&cons->mtx);
498
499 cons_update_cursor(cons);
500}
501
502static errno_t cons_open(con_srvs_t *srvs, con_srv_t *srv)
503{
504 return EOK;
505}
506
507static errno_t cons_close(con_srv_t *srv)
508{
509 return EOK;
510}
511
512static errno_t cons_read(con_srv_t *srv, void *buf, size_t size, size_t *nread)
513{
514 uint8_t *bbuf = buf;
515 console_t *cons = srv_to_console(srv);
516 size_t pos = 0;
517
518 /*
519 * Read input from keyboard and copy it to the buffer.
520 * We need to handle situation when wchar is split by 2 following
521 * reads.
522 */
523 while (pos < size) {
524 /* Copy to the buffer remaining characters. */
525 while ((pos < size) && (cons->char_remains_len > 0)) {
526 bbuf[pos] = cons->char_remains[0];
527 pos++;
528
529 /* Unshift the array. */
530 for (size_t i = 1; i < cons->char_remains_len; i++)
531 cons->char_remains[i - 1] = cons->char_remains[i];
532
533 cons->char_remains_len--;
534 }
535
536 /* Still not enough? Then get another key from the queue. */
537 if (pos < size) {
538 link_t *link = prodcons_consume(&cons->input_pc);
539 cons_event_t *event = list_get_instance(link,
540 cons_event_t, link);
541
542 /* Accept key presses of printable chars only. */
543 if (event->type == CEV_KEY && event->ev.key.type == KEY_PRESS &&
544 (event->ev.key.c != 0)) {
545 char32_t tmp[2] = { event->ev.key.c, 0 };
546 wstr_to_str(cons->char_remains, UTF8_CHAR_BUFFER_SIZE, tmp);
547 cons->char_remains_len = str_size(cons->char_remains);
548 }
549
550 free(event);
551 }
552 }
553
554 *nread = size;
555 return EOK;
556}
557
558static errno_t cons_write(con_srv_t *srv, void *data, size_t size, size_t *nwritten)
559{
560 console_t *cons = srv_to_console(srv);
561
562 size_t off = 0;
563 while (off < size)
564 cons_write_char(cons, str_decode(data, &off, size));
565
566 *nwritten = size;
567 return EOK;
568}
569
570static void cons_sync(con_srv_t *srv)
571{
572 console_t *cons = srv_to_console(srv);
573
574 cons_update(cons);
575}
576
577static void cons_clear(con_srv_t *srv)
578{
579 console_t *cons = srv_to_console(srv);
580
581 fibril_mutex_lock(&cons->mtx);
582 pointer_undraw();
583 chargrid_clear(cons->frontbuf);
584 pointer_draw();
585 fibril_mutex_unlock(&cons->mtx);
586
587 cons_update(cons);
588}
589
590static void cons_set_pos(con_srv_t *srv, sysarg_t col, sysarg_t row)
591{
592 console_t *cons = srv_to_console(srv);
593
594 fibril_mutex_lock(&cons->mtx);
595 pointer_undraw();
596 chargrid_set_cursor(cons->frontbuf, col, row);
597 pointer_draw();
598 fibril_mutex_unlock(&cons->mtx);
599
600 cons_update_cursor(cons);
601}
602
603static errno_t cons_get_pos(con_srv_t *srv, sysarg_t *col, sysarg_t *row)
604{
605 console_t *cons = srv_to_console(srv);
606
607 fibril_mutex_lock(&cons->mtx);
608 chargrid_get_cursor(cons->frontbuf, col, row);
609 fibril_mutex_unlock(&cons->mtx);
610
611 return EOK;
612}
613
614static errno_t cons_get_size(con_srv_t *srv, sysarg_t *cols, sysarg_t *rows)
615{
616 console_t *cons = srv_to_console(srv);
617
618 fibril_mutex_lock(&cons->mtx);
619 *cols = cons->cols;
620 *rows = cons->rows;
621 fibril_mutex_unlock(&cons->mtx);
622
623 return EOK;
624}
625
626static errno_t cons_get_color_cap(con_srv_t *srv, console_caps_t *ccaps)
627{
628 console_t *cons = srv_to_console(srv);
629
630 fibril_mutex_lock(&cons->mtx);
631 *ccaps = cons->ccaps;
632 fibril_mutex_unlock(&cons->mtx);
633
634 return EOK;
635}
636
637static void cons_set_style(con_srv_t *srv, console_style_t style)
638{
639 console_t *cons = srv_to_console(srv);
640
641 fibril_mutex_lock(&cons->mtx);
642 chargrid_set_style(cons->frontbuf, style);
643 fibril_mutex_unlock(&cons->mtx);
644}
645
646static void cons_set_color(con_srv_t *srv, console_color_t bgcolor,
647 console_color_t fgcolor, console_color_attr_t attr)
648{
649 console_t *cons = srv_to_console(srv);
650
651 fibril_mutex_lock(&cons->mtx);
652 chargrid_set_color(cons->frontbuf, bgcolor, fgcolor, attr);
653 fibril_mutex_unlock(&cons->mtx);
654}
655
656static void cons_set_rgb_color(con_srv_t *srv, pixel_t bgcolor,
657 pixel_t fgcolor)
658{
659 console_t *cons = srv_to_console(srv);
660
661 fibril_mutex_lock(&cons->mtx);
662 chargrid_set_rgb_color(cons->frontbuf, bgcolor, fgcolor);
663 fibril_mutex_unlock(&cons->mtx);
664}
665
666static void cons_set_cursor_visibility(con_srv_t *srv, bool visible)
667{
668 console_t *cons = srv_to_console(srv);
669
670 cons_set_cursor_vis(cons, visible);
671}
672
673static errno_t cons_set_caption(con_srv_t *srv, const char *caption)
674{
675 console_t *cons = srv_to_console(srv);
676
677 (void) cons;
678 (void) caption;
679 return EOK;
680}
681
682static errno_t cons_get_event(con_srv_t *srv, cons_event_t *event)
683{
684 console_t *cons = srv_to_console(srv);
685 link_t *link = prodcons_consume(&cons->input_pc);
686 cons_event_t *cevent = list_get_instance(link, cons_event_t, link);
687
688 *event = *cevent;
689 free(cevent);
690 return EOK;
691}
692
693/** Create shared buffer for efficient rendering.
694 *
695 * @param srv Console server
696 * @param cols Number of columns in buffer
697 * @param rows Number of rows in buffer
698 * @param rbuf Place to store pointer to new sharable buffer
699 *
700 * @return EOK on sucess or an error code
701 */
702static errno_t cons_map(con_srv_t *srv, sysarg_t cols, sysarg_t rows,
703 charfield_t **rbuf)
704{
705 console_t *cons = srv_to_console(srv);
706 void *buf;
707
708 fibril_mutex_lock(&cons->mtx);
709
710 if (cons->ubuf != NULL) {
711 fibril_mutex_unlock(&cons->mtx);
712 return EBUSY;
713 }
714
715 buf = as_area_create(AS_AREA_ANY, cols * rows * sizeof(charfield_t),
716 AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE, AS_AREA_UNPAGED);
717 if (buf == AS_MAP_FAILED) {
718 fibril_mutex_unlock(&cons->mtx);
719 return ENOMEM;
720 }
721
722 cons->ucols = cols;
723 cons->urows = rows;
724 cons->ubuf = buf;
725 fibril_mutex_unlock(&cons->mtx);
726
727 *rbuf = buf;
728 return EOK;
729}
730
731/** Delete shared buffer.
732 *
733 * @param srv Console server
734 */
735static void cons_unmap(con_srv_t *srv)
736{
737 console_t *cons = srv_to_console(srv);
738 void *buf;
739
740 fibril_mutex_lock(&cons->mtx);
741
742 buf = cons->ubuf;
743 cons->ubuf = NULL;
744
745 if (buf != NULL)
746 as_area_destroy(buf);
747
748 fibril_mutex_unlock(&cons->mtx);
749}
750
751/** Update area of console from shared buffer.
752 *
753 * @param srv Console server
754 * @param c0 Column coordinate of top-left corner (inclusive)
755 * @param r0 Row coordinate of top-left corner (inclusive)
756 * @param c1 Column coordinate of bottom-right corner (exclusive)
757 * @param r1 Row coordinate of bottom-right corner (exclusive)
758 */
759static void cons_buf_update(con_srv_t *srv, sysarg_t c0, sysarg_t r0,
760 sysarg_t c1, sysarg_t r1)
761{
762 console_t *cons = srv_to_console(srv);
763 charfield_t *ch;
764 sysarg_t col, row;
765
766 fibril_mutex_lock(&cons->mtx);
767
768 if (cons->ubuf == NULL) {
769 fibril_mutex_unlock(&cons->mtx);
770 return;
771 }
772
773 /* Make sure we have meaningful coordinates, within bounds */
774
775 if (c1 > cons->ucols)
776 c1 = cons->ucols;
777 if (c1 > cons->cols)
778 c1 = cons->cols;
779 if (c0 >= c1) {
780 fibril_mutex_unlock(&cons->mtx);
781 return;
782 }
783 if (r1 > cons->urows)
784 r1 = cons->urows;
785 if (r1 > cons->rows)
786 r1 = cons->rows;
787 if (r0 >= r1) {
788 fibril_mutex_unlock(&cons->mtx);
789 return;
790 }
791
792 /* Update front buffer from user buffer */
793
794 pointer_undraw();
795
796 for (row = r0; row < r1; row++) {
797 for (col = c0; col < c1; col++) {
798 ch = chargrid_charfield_at(cons->frontbuf, col, row);
799 *ch = cons->ubuf[row * cons->ucols + col];
800 }
801 }
802
803 pointer_draw();
804 fibril_mutex_unlock(&cons->mtx);
805
806 /* Update console */
807 cons_update(cons);
808}
809
810static void client_connection(ipc_call_t *icall, void *arg)
811{
812 console_t *cons = NULL;
813
814 for (size_t i = 0; i < CONSOLE_COUNT; i++) {
815 if (consoles[i].dsid == (service_id_t) ipc_get_arg2(icall)) {
816 cons = &consoles[i];
817 break;
818 }
819 }
820
821 if (cons == NULL) {
822 async_answer_0(icall, ENOENT);
823 return;
824 }
825
826 if (!atomic_flag_test_and_set(&cons->refcnt))
827 cons_set_cursor_vis(cons, true);
828
829 con_conn(icall, &cons->srvs);
830}
831
832static errno_t input_connect(const char *svc)
833{
834 async_sess_t *sess;
835 service_id_t dsid;
836
837 errno_t rc = loc_service_get_id(svc, &dsid, 0);
838 if (rc != EOK) {
839 printf("%s: Input service %s not found\n", NAME, svc);
840 return rc;
841 }
842
843 sess = loc_service_connect(dsid, INTERFACE_INPUT, 0);
844 if (sess == NULL) {
845 printf("%s: Unable to connect to input service %s\n", NAME,
846 svc);
847 return EIO;
848 }
849
850 rc = input_open(sess, &input_ev_ops, NULL, &input);
851 if (rc != EOK) {
852 async_hangup(sess);
853 printf("%s: Unable to communicate with service %s (%s)\n",
854 NAME, svc, str_error(rc));
855 return rc;
856 }
857
858 return EOK;
859}
860
861static async_sess_t *output_connect(const char *svc)
862{
863 async_sess_t *sess;
864 service_id_t dsid;
865
866 errno_t rc = loc_service_get_id(svc, &dsid, 0);
867 if (rc == EOK) {
868 sess = loc_service_connect(dsid, INTERFACE_OUTPUT, 0);
869 if (sess == NULL) {
870 printf("%s: Unable to connect to output service %s\n",
871 NAME, svc);
872 return NULL;
873 }
874 } else
875 return NULL;
876
877 return sess;
878}
879
880static bool console_srv_init(char *input_svc, char *output_svc)
881{
882 /* Connect to input service */
883 errno_t rc = input_connect(input_svc);
884 if (rc != EOK)
885 return false;
886
887 /* Connect to output service */
888 output_sess = output_connect(output_svc);
889 if (output_sess == NULL)
890 return false;
891
892 /* Register server */
893 async_set_fallback_port_handler(client_connection, NULL);
894 rc = loc_server_register(NAME);
895 if (rc != EOK) {
896 printf("%s: Unable to register server (%s)\n", NAME,
897 str_error(rc));
898 return false;
899 }
900
901 output_get_dimensions(output_sess, &cols, &rows);
902 output_set_style(output_sess, STYLE_NORMAL);
903
904 console_caps_t ccaps;
905 output_get_caps(output_sess, &ccaps);
906
907 /*
908 * Inititalize consoles only if there are
909 * actually some output devices.
910 */
911 if (ccaps != 0) {
912 for (size_t i = 0; i < CONSOLE_COUNT; i++) {
913 consoles[i].index = i;
914 atomic_flag_clear(&consoles[i].refcnt);
915 fibril_mutex_initialize(&consoles[i].mtx);
916 prodcons_initialize(&consoles[i].input_pc);
917 consoles[i].char_remains_len = 0;
918
919 consoles[i].cols = cols;
920 consoles[i].rows = rows;
921 consoles[i].ccaps = ccaps;
922 consoles[i].frontbuf =
923 chargrid_create(cols, rows, CHARGRID_FLAG_SHARED);
924
925 if (consoles[i].frontbuf == NULL) {
926 printf("%s: Unable to allocate frontbuffer %zu\n", NAME, i);
927 return false;
928 }
929
930 consoles[i].fbid = output_frontbuf_create(output_sess,
931 consoles[i].frontbuf);
932 if (consoles[i].fbid == 0) {
933 printf("%s: Unable to create frontbuffer %zu\n", NAME, i);
934 return false;
935 }
936
937 con_srvs_init(&consoles[i].srvs);
938 consoles[i].srvs.ops = &con_ops;
939 consoles[i].srvs.sarg = &consoles[i];
940
941 char vc[LOC_NAME_MAXLEN + 1];
942 snprintf(vc, LOC_NAME_MAXLEN, "%s/vc%zu", NAMESPACE, i);
943
944 if (loc_service_register(vc, &consoles[i].dsid) != EOK) {
945 printf("%s: Unable to register device %s\n", NAME, vc);
946 return false;
947 }
948 }
949
950 input_activate(input);
951 active = true;
952 cons_damage(active_console);
953 }
954
955 return true;
956}
957
958static void usage(char *name)
959{
960 printf("Usage: %s <input_dev> <output_dev>\n", name);
961}
962
963int main(int argc, char *argv[])
964{
965 if (argc < 3) {
966 usage(argv[0]);
967 return -1;
968 }
969
970 printf("%s: HelenOS Console service\n", NAME);
971
972 if (!console_srv_init(argv[1], argv[2]))
973 return -1;
974
975 printf("%s: Accepting connections\n", NAME);
976 task_retval(0);
977 async_manager();
978
979 /* Never reached */
980 return 0;
981}
982
983/** @}
984 */
Note: See TracBrowser for help on using the repository browser.