source: mainline/uspace/srv/hid/console/console.c@ 08e103d4

Last change on this file since 08e103d4 was 08e103d4, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Use clearer naming for string length functions

This and the following commit change the names of functions, as well as
their documentation, to use unambiguous terms "bytes" and "code points"
instead of ambiguous terms "size", "length", and "characters".

  • Property mode set to 100644
File size: 16.2 KB
Line 
1/*
2 * Copyright (c) 2011 Martin Decky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup console
30 * @{
31 */
32/** @file
33 */
34
35#include <async.h>
36#include <stdio.h>
37#include <adt/prodcons.h>
38#include <io/input.h>
39#include <ipc/vfs.h>
40#include <errno.h>
41#include <str_error.h>
42#include <loc.h>
43#include <io/con_srv.h>
44#include <io/kbd_event.h>
45#include <io/keycode.h>
46#include <io/chargrid.h>
47#include <io/output.h>
48#include <align.h>
49#include <as.h>
50#include <task.h>
51#include <fibril_synch.h>
52#include <stdatomic.h>
53#include <stdlib.h>
54#include <str.h>
55#include "console.h"
56
57#define NAME "console"
58#define NAMESPACE "term"
59
60#define UTF8_CHAR_BUFFER_SIZE (STR_BOUNDS(1) + 1)
61
62typedef struct {
63 atomic_flag refcnt; /**< Connection reference count */
64 prodcons_t input_pc; /**< Incoming keyboard events */
65
66 /**
67 * Not yet sent bytes of last char event.
68 */
69 char char_remains[UTF8_CHAR_BUFFER_SIZE];
70 size_t char_remains_len; /**< Number of not yet sent bytes. */
71
72 fibril_mutex_t mtx; /**< Lock protecting mutable fields */
73
74 size_t index; /**< Console index */
75 service_id_t dsid; /**< Service handle */
76
77 sysarg_t cols; /**< Number of columns */
78 sysarg_t rows; /**< Number of rows */
79 console_caps_t ccaps; /**< Console capabilities */
80
81 chargrid_t *frontbuf; /**< Front buffer */
82 frontbuf_handle_t fbid; /**< Front buffer handle */
83 con_srvs_t srvs; /**< Console service setup */
84} console_t;
85
86/** Input server proxy */
87static input_t *input;
88static bool active = false;
89
90/** Session to the output server */
91static async_sess_t *output_sess;
92
93/** Output dimensions */
94static sysarg_t cols;
95static sysarg_t rows;
96
97/** Array of data for virtual consoles */
98static console_t consoles[CONSOLE_COUNT];
99
100/** Mutex for console switching */
101static FIBRIL_MUTEX_INITIALIZE(switch_mtx);
102
103static console_t *active_console = &consoles[0];
104
105static errno_t input_ev_active(input_t *);
106static errno_t input_ev_deactive(input_t *);
107static errno_t input_ev_key(input_t *, kbd_event_type_t, keycode_t, keymod_t, wchar_t);
108static errno_t input_ev_move(input_t *, int, int);
109static errno_t input_ev_abs_move(input_t *, unsigned, unsigned, unsigned, unsigned);
110static errno_t input_ev_button(input_t *, int, int);
111
112static input_ev_ops_t input_ev_ops = {
113 .active = input_ev_active,
114 .deactive = input_ev_deactive,
115 .key = input_ev_key,
116 .move = input_ev_move,
117 .abs_move = input_ev_abs_move,
118 .button = input_ev_button
119};
120
121static errno_t cons_open(con_srvs_t *, con_srv_t *);
122static errno_t cons_close(con_srv_t *);
123static errno_t cons_read(con_srv_t *, void *, size_t, size_t *);
124static errno_t cons_write(con_srv_t *, void *, size_t, size_t *);
125static void cons_sync(con_srv_t *);
126static void cons_clear(con_srv_t *);
127static void cons_set_pos(con_srv_t *, sysarg_t col, sysarg_t row);
128static errno_t cons_get_pos(con_srv_t *, sysarg_t *, sysarg_t *);
129static errno_t cons_get_size(con_srv_t *, sysarg_t *, sysarg_t *);
130static errno_t cons_get_color_cap(con_srv_t *, console_caps_t *);
131static void cons_set_style(con_srv_t *, console_style_t);
132static void cons_set_color(con_srv_t *, console_color_t, console_color_t,
133 console_color_attr_t);
134static void cons_set_rgb_color(con_srv_t *, pixel_t, pixel_t);
135static void cons_set_cursor_visibility(con_srv_t *, bool);
136static errno_t cons_get_event(con_srv_t *, cons_event_t *);
137
138static con_ops_t con_ops = {
139 .open = cons_open,
140 .close = cons_close,
141 .read = cons_read,
142 .write = cons_write,
143 .sync = cons_sync,
144 .clear = cons_clear,
145 .set_pos = cons_set_pos,
146 .get_pos = cons_get_pos,
147 .get_size = cons_get_size,
148 .get_color_cap = cons_get_color_cap,
149 .set_style = cons_set_style,
150 .set_color = cons_set_color,
151 .set_rgb_color = cons_set_rgb_color,
152 .set_cursor_visibility = cons_set_cursor_visibility,
153 .get_event = cons_get_event
154};
155
156static console_t *srv_to_console(con_srv_t *srv)
157{
158 return srv->srvs->sarg;
159}
160
161static void cons_update(console_t *cons)
162{
163 fibril_mutex_lock(&switch_mtx);
164 fibril_mutex_lock(&cons->mtx);
165
166 if ((active) && (cons == active_console)) {
167 output_update(output_sess, cons->fbid);
168 output_cursor_update(output_sess, cons->fbid);
169 }
170
171 fibril_mutex_unlock(&cons->mtx);
172 fibril_mutex_unlock(&switch_mtx);
173}
174
175static void cons_update_cursor(console_t *cons)
176{
177 fibril_mutex_lock(&switch_mtx);
178 fibril_mutex_lock(&cons->mtx);
179
180 if ((active) && (cons == active_console))
181 output_cursor_update(output_sess, cons->fbid);
182
183 fibril_mutex_unlock(&cons->mtx);
184 fibril_mutex_unlock(&switch_mtx);
185}
186
187static void cons_damage(console_t *cons)
188{
189 fibril_mutex_lock(&switch_mtx);
190 fibril_mutex_lock(&cons->mtx);
191
192 if ((active) && (cons == active_console)) {
193 output_damage(output_sess, cons->fbid, 0, 0, cons->cols,
194 cons->rows);
195 output_cursor_update(output_sess, cons->fbid);
196 }
197
198 fibril_mutex_unlock(&cons->mtx);
199 fibril_mutex_unlock(&switch_mtx);
200}
201
202static void cons_switch(unsigned int index)
203{
204 /*
205 * The first undefined index is reserved
206 * for switching to the kernel console.
207 */
208 if (index == CONSOLE_COUNT) {
209 if (console_kcon())
210 active = false;
211
212 return;
213 }
214
215 if (index > CONSOLE_COUNT)
216 return;
217
218 console_t *cons = &consoles[index];
219
220 fibril_mutex_lock(&switch_mtx);
221
222 if (cons == active_console) {
223 fibril_mutex_unlock(&switch_mtx);
224 return;
225 }
226
227 active_console = cons;
228
229 fibril_mutex_unlock(&switch_mtx);
230
231 cons_damage(cons);
232}
233
234static errno_t input_ev_active(input_t *input)
235{
236 active = true;
237 output_claim(output_sess);
238 cons_damage(active_console);
239
240 return EOK;
241}
242
243static errno_t input_ev_deactive(input_t *input)
244{
245 active = false;
246 output_yield(output_sess);
247
248 return EOK;
249}
250
251static errno_t input_ev_key(input_t *input, kbd_event_type_t type, keycode_t key,
252 keymod_t mods, wchar_t c)
253{
254 if ((key >= KC_F1) && (key <= KC_F1 + CONSOLE_COUNT) &&
255 ((mods & KM_CTRL) == 0)) {
256 cons_switch(key - KC_F1);
257 } else {
258 /* Got key press/release event */
259 kbd_event_t *event =
260 (kbd_event_t *) malloc(sizeof(kbd_event_t));
261 if (event == NULL) {
262 return ENOMEM;
263 }
264
265 link_initialize(&event->link);
266 event->type = type;
267 event->key = key;
268 event->mods = mods;
269 event->c = c;
270
271 prodcons_produce(&active_console->input_pc,
272 &event->link);
273 }
274
275 return EOK;
276}
277
278static errno_t input_ev_move(input_t *input, int dx, int dy)
279{
280 return EOK;
281}
282
283static errno_t input_ev_abs_move(input_t *input, unsigned x, unsigned y,
284 unsigned max_x, unsigned max_y)
285{
286 return EOK;
287}
288
289static errno_t input_ev_button(input_t *input, int bnum, int bpress)
290{
291 return EOK;
292}
293
294/** Process a character from the client (TTY emulation). */
295static void cons_write_char(console_t *cons, wchar_t ch)
296{
297 sysarg_t updated = 0;
298
299 fibril_mutex_lock(&cons->mtx);
300
301 switch (ch) {
302 case '\n':
303 updated = chargrid_newline(cons->frontbuf);
304 break;
305 case '\r':
306 break;
307 case '\t':
308 updated = chargrid_tabstop(cons->frontbuf, 8);
309 break;
310 case '\b':
311 updated = chargrid_backspace(cons->frontbuf);
312 break;
313 default:
314 updated = chargrid_putwchar(cons->frontbuf, ch, true);
315 }
316
317 fibril_mutex_unlock(&cons->mtx);
318
319 if (updated > 1)
320 cons_update(cons);
321}
322
323static void cons_set_cursor_vis(console_t *cons, bool visible)
324{
325 fibril_mutex_lock(&cons->mtx);
326 chargrid_set_cursor_visibility(cons->frontbuf, visible);
327 fibril_mutex_unlock(&cons->mtx);
328
329 cons_update_cursor(cons);
330}
331
332static errno_t cons_open(con_srvs_t *srvs, con_srv_t *srv)
333{
334 return EOK;
335}
336
337static errno_t cons_close(con_srv_t *srv)
338{
339 return EOK;
340}
341
342static errno_t cons_read(con_srv_t *srv, void *buf, size_t size, size_t *nread)
343{
344 uint8_t *bbuf = buf;
345 console_t *cons = srv_to_console(srv);
346 size_t pos = 0;
347
348 /*
349 * Read input from keyboard and copy it to the buffer.
350 * We need to handle situation when wchar is split by 2 following
351 * reads.
352 */
353 while (pos < size) {
354 /* Copy to the buffer remaining characters. */
355 while ((pos < size) && (cons->char_remains_len > 0)) {
356 bbuf[pos] = cons->char_remains[0];
357 pos++;
358
359 /* Unshift the array. */
360 for (size_t i = 1; i < cons->char_remains_len; i++)
361 cons->char_remains[i - 1] = cons->char_remains[i];
362
363 cons->char_remains_len--;
364 }
365
366 /* Still not enough? Then get another key from the queue. */
367 if (pos < size) {
368 link_t *link = prodcons_consume(&cons->input_pc);
369 kbd_event_t *event = list_get_instance(link, kbd_event_t, link);
370
371 /* Accept key presses of printable chars only. */
372 if ((event->type == KEY_PRESS) && (event->c != 0)) {
373 wchar_t tmp[2] = { event->c, 0 };
374 wstr_to_str(cons->char_remains, UTF8_CHAR_BUFFER_SIZE, tmp);
375 cons->char_remains_len = str_bytes(cons->char_remains);
376 }
377
378 free(event);
379 }
380 }
381
382 *nread = size;
383 return EOK;
384}
385
386static errno_t cons_write(con_srv_t *srv, void *data, size_t size, size_t *nwritten)
387{
388 console_t *cons = srv_to_console(srv);
389
390 size_t off = 0;
391 while (off < size)
392 cons_write_char(cons, str_decode(data, &off, size));
393
394 *nwritten = size;
395 return EOK;
396}
397
398static void cons_sync(con_srv_t *srv)
399{
400 console_t *cons = srv_to_console(srv);
401
402 cons_update(cons);
403}
404
405static void cons_clear(con_srv_t *srv)
406{
407 console_t *cons = srv_to_console(srv);
408
409 fibril_mutex_lock(&cons->mtx);
410 chargrid_clear(cons->frontbuf);
411 fibril_mutex_unlock(&cons->mtx);
412
413 cons_update(cons);
414}
415
416static void cons_set_pos(con_srv_t *srv, sysarg_t col, sysarg_t row)
417{
418 console_t *cons = srv_to_console(srv);
419
420 fibril_mutex_lock(&cons->mtx);
421 chargrid_set_cursor(cons->frontbuf, col, row);
422 fibril_mutex_unlock(&cons->mtx);
423
424 cons_update_cursor(cons);
425}
426
427static errno_t cons_get_pos(con_srv_t *srv, sysarg_t *col, sysarg_t *row)
428{
429 console_t *cons = srv_to_console(srv);
430
431 fibril_mutex_lock(&cons->mtx);
432 chargrid_get_cursor(cons->frontbuf, col, row);
433 fibril_mutex_unlock(&cons->mtx);
434
435 return EOK;
436}
437
438static errno_t cons_get_size(con_srv_t *srv, sysarg_t *cols, sysarg_t *rows)
439{
440 console_t *cons = srv_to_console(srv);
441
442 fibril_mutex_lock(&cons->mtx);
443 *cols = cons->cols;
444 *rows = cons->rows;
445 fibril_mutex_unlock(&cons->mtx);
446
447 return EOK;
448}
449
450static errno_t cons_get_color_cap(con_srv_t *srv, console_caps_t *ccaps)
451{
452 console_t *cons = srv_to_console(srv);
453
454 fibril_mutex_lock(&cons->mtx);
455 *ccaps = cons->ccaps;
456 fibril_mutex_unlock(&cons->mtx);
457
458 return EOK;
459}
460
461static void cons_set_style(con_srv_t *srv, console_style_t style)
462{
463 console_t *cons = srv_to_console(srv);
464
465 fibril_mutex_lock(&cons->mtx);
466 chargrid_set_style(cons->frontbuf, style);
467 fibril_mutex_unlock(&cons->mtx);
468}
469
470static void cons_set_color(con_srv_t *srv, console_color_t bgcolor,
471 console_color_t fgcolor, console_color_attr_t attr)
472{
473 console_t *cons = srv_to_console(srv);
474
475 fibril_mutex_lock(&cons->mtx);
476 chargrid_set_color(cons->frontbuf, bgcolor, fgcolor, attr);
477 fibril_mutex_unlock(&cons->mtx);
478}
479
480static void cons_set_rgb_color(con_srv_t *srv, pixel_t bgcolor,
481 pixel_t fgcolor)
482{
483 console_t *cons = srv_to_console(srv);
484
485 fibril_mutex_lock(&cons->mtx);
486 chargrid_set_rgb_color(cons->frontbuf, bgcolor, fgcolor);
487 fibril_mutex_unlock(&cons->mtx);
488}
489
490static void cons_set_cursor_visibility(con_srv_t *srv, bool visible)
491{
492 console_t *cons = srv_to_console(srv);
493
494 cons_set_cursor_vis(cons, visible);
495}
496
497static errno_t cons_get_event(con_srv_t *srv, cons_event_t *event)
498{
499 console_t *cons = srv_to_console(srv);
500 link_t *link = prodcons_consume(&cons->input_pc);
501 kbd_event_t *kevent = list_get_instance(link, kbd_event_t, link);
502
503 event->type = CEV_KEY;
504 event->ev.key = *kevent;
505
506 free(kevent);
507 return EOK;
508}
509
510static void client_connection(ipc_call_t *icall, void *arg)
511{
512 console_t *cons = NULL;
513
514 for (size_t i = 0; i < CONSOLE_COUNT; i++) {
515 if (consoles[i].dsid == (service_id_t) IPC_GET_ARG2(*icall)) {
516 cons = &consoles[i];
517 break;
518 }
519 }
520
521 if (cons == NULL) {
522 async_answer_0(icall, ENOENT);
523 return;
524 }
525
526 if (!atomic_flag_test_and_set(&cons->refcnt))
527 cons_set_cursor_vis(cons, true);
528
529 con_conn(icall, &cons->srvs);
530}
531
532static errno_t input_connect(const char *svc)
533{
534 async_sess_t *sess;
535 service_id_t dsid;
536
537 errno_t rc = loc_service_get_id(svc, &dsid, 0);
538 if (rc != EOK) {
539 printf("%s: Input service %s not found\n", NAME, svc);
540 return rc;
541 }
542
543 sess = loc_service_connect(dsid, INTERFACE_INPUT, 0);
544 if (sess == NULL) {
545 printf("%s: Unable to connect to input service %s\n", NAME,
546 svc);
547 return EIO;
548 }
549
550 rc = input_open(sess, &input_ev_ops, NULL, &input);
551 if (rc != EOK) {
552 async_hangup(sess);
553 printf("%s: Unable to communicate with service %s (%s)\n",
554 NAME, svc, str_error(rc));
555 return rc;
556 }
557
558 return EOK;
559}
560
561static async_sess_t *output_connect(const char *svc)
562{
563 async_sess_t *sess;
564 service_id_t dsid;
565
566 errno_t rc = loc_service_get_id(svc, &dsid, 0);
567 if (rc == EOK) {
568 sess = loc_service_connect(dsid, INTERFACE_OUTPUT, 0);
569 if (sess == NULL) {
570 printf("%s: Unable to connect to output service %s\n",
571 NAME, svc);
572 return NULL;
573 }
574 } else
575 return NULL;
576
577 return sess;
578}
579
580static bool console_srv_init(char *input_svc, char *output_svc)
581{
582 /* Connect to input service */
583 errno_t rc = input_connect(input_svc);
584 if (rc != EOK)
585 return false;
586
587 /* Connect to output service */
588 output_sess = output_connect(output_svc);
589 if (output_sess == NULL)
590 return false;
591
592 /* Register server */
593 async_set_fallback_port_handler(client_connection, NULL);
594 rc = loc_server_register(NAME);
595 if (rc != EOK) {
596 printf("%s: Unable to register server (%s)\n", NAME,
597 str_error(rc));
598 return false;
599 }
600
601 output_get_dimensions(output_sess, &cols, &rows);
602 output_set_style(output_sess, STYLE_NORMAL);
603
604 console_caps_t ccaps;
605 output_get_caps(output_sess, &ccaps);
606
607 /*
608 * Inititalize consoles only if there are
609 * actually some output devices.
610 */
611 if (ccaps != 0) {
612 for (size_t i = 0; i < CONSOLE_COUNT; i++) {
613 consoles[i].index = i;
614 atomic_flag_clear(&consoles[i].refcnt);
615 fibril_mutex_initialize(&consoles[i].mtx);
616 prodcons_initialize(&consoles[i].input_pc);
617 consoles[i].char_remains_len = 0;
618
619 consoles[i].cols = cols;
620 consoles[i].rows = rows;
621 consoles[i].ccaps = ccaps;
622 consoles[i].frontbuf =
623 chargrid_create(cols, rows, CHARGRID_FLAG_SHARED);
624
625 if (consoles[i].frontbuf == NULL) {
626 printf("%s: Unable to allocate frontbuffer %zu\n", NAME, i);
627 return false;
628 }
629
630 consoles[i].fbid = output_frontbuf_create(output_sess,
631 consoles[i].frontbuf);
632 if (consoles[i].fbid == 0) {
633 printf("%s: Unable to create frontbuffer %zu\n", NAME, i);
634 return false;
635 }
636
637 con_srvs_init(&consoles[i].srvs);
638 consoles[i].srvs.ops = &con_ops;
639 consoles[i].srvs.sarg = &consoles[i];
640
641 char vc[LOC_NAME_MAXLEN + 1];
642 snprintf(vc, LOC_NAME_MAXLEN, "%s/vc%zu", NAMESPACE, i);
643
644 if (loc_service_register(vc, &consoles[i].dsid) != EOK) {
645 printf("%s: Unable to register device %s\n", NAME, vc);
646 return false;
647 }
648 }
649
650 input_activate(input);
651 }
652
653 return true;
654}
655
656static void usage(char *name)
657{
658 printf("Usage: %s <input_dev> <output_dev>\n", name);
659}
660
661int main(int argc, char *argv[])
662{
663 if (argc < 3) {
664 usage(argv[0]);
665 return -1;
666 }
667
668 printf("%s: HelenOS Console service\n", NAME);
669
670 if (!console_srv_init(argv[1], argv[2]))
671 return -1;
672
673 printf("%s: Accepting connections\n", NAME);
674 task_retval(0);
675 async_manager();
676
677 /* Never reached */
678 return 0;
679}
680
681/** @}
682 */
Note: See TracBrowser for help on using the repository browser.