source: mainline/uspace/srv/hid/console/console.c@ 5d1ff11

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

Implement mouse/tablet support in the console

That is, we can now draw text based cursor in the console and deliver
button press events to the application.

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