source: mainline/uspace/srv/hid/console/console.c@ 7bcd15f

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

Store positioning device ID in position events

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