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
RevLine 
[51c1b003]1/*
[68a552f]2 * Copyright (c) 2021 Jiri Svoboda
[7c014d1]3 * Copyright (c) 2011 Martin Decky
[51c1b003]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 */
[ce5bcb4]29
[231a60a]30/** @addtogroup console
[1601f3c]31 * @{
[ce5bcb4]32 */
33/** @file
34 */
35
[7c014d1]36#include <async.h>
37#include <stdio.h>
38#include <adt/prodcons.h>
[111d2d6]39#include <io/input.h>
[7c014d1]40#include <ipc/vfs.h>
[51c1b003]41#include <errno.h>
[30db06c]42#include <str_error.h>
[15f3c3f]43#include <loc.h>
[5d94b16c]44#include <io/con_srv.h>
[111d2d6]45#include <io/kbd_event.h>
[7c014d1]46#include <io/keycode.h>
[6d5e378]47#include <io/chargrid.h>
48#include <io/output.h>
[7c014d1]49#include <align.h>
50#include <as.h>
[2f6ad06]51#include <task.h>
[1e4cada]52#include <fibril_synch.h>
[508b0df1]53#include <stdatomic.h>
[38d150e]54#include <stdlib.h>
[1d6dd2a]55#include <str.h>
[9805cde]56#include "console.h"
[369a5f8]57
[1313ee9]58#define NAME "console"
59#define NAMESPACE "term"
[79ae36dd]60
[6d5e378]61#define UTF8_CHAR_BUFFER_SIZE (STR_BOUNDS(1) + 1)
[a58bc8b]62
[79460ae]63typedef struct {
[508b0df1]64 atomic_flag refcnt; /**< Connection reference count */
[5d1ff11]65 prodcons_t input_pc; /**< Incoming console events */
[a35b458]66
[6d5e378]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. */
[a35b458]72
[6d5e378]73 fibril_mutex_t mtx; /**< Lock protecting mutable fields */
[a35b458]74
[6d5e378]75 size_t index; /**< Console index */
76 service_id_t dsid; /**< Service handle */
[a35b458]77
[6d5e378]78 sysarg_t cols; /**< Number of columns */
79 sysarg_t rows; /**< Number of rows */
80 console_caps_t ccaps; /**< Console capabilities */
[a35b458]81
[68a552f]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
[6d5e378]86 chargrid_t *frontbuf; /**< Front buffer */
87 frontbuf_handle_t fbid; /**< Front buffer handle */
[5d94b16c]88 con_srvs_t srvs; /**< Console service setup */
[424cd43]89} console_t;
[79460ae]90
[111d2d6]91/** Input server proxy */
92static input_t *input;
[593e023]93static bool active = false;
[7c014d1]94
[6d5e378]95/** Session to the output server */
96static async_sess_t *output_sess;
[7c014d1]97
[6d5e378]98/** Output dimensions */
99static sysarg_t cols;
100static sysarg_t rows;
[7c014d1]101
[5d1ff11]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
[1601f3c]112/** Array of data for virtual consoles */
[424cd43]113static console_t consoles[CONSOLE_COUNT];
114
[7c014d1]115/** Mutex for console switching */
116static FIBRIL_MUTEX_INITIALIZE(switch_mtx);
117
118static console_t *active_console = &consoles[0];
[1601f3c]119
[b7fd2a0]120static errno_t input_ev_active(input_t *);
121static errno_t input_ev_deactive(input_t *);
[28a5ebd]122static errno_t input_ev_key(input_t *, kbd_event_type_t, keycode_t, keymod_t, char32_t);
[b7fd2a0]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);
[8edec53]126static errno_t input_ev_dclick(input_t *, int);
[111d2d6]127
128static input_ev_ops_t input_ev_ops = {
[593e023]129 .active = input_ev_active,
130 .deactive = input_ev_deactive,
[111d2d6]131 .key = input_ev_key,
132 .move = input_ev_move,
133 .abs_move = input_ev_abs_move,
[8edec53]134 .button = input_ev_button,
135 .dclick = input_ev_dclick
[111d2d6]136};
137
[b7fd2a0]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 *);
[5d94b16c]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);
[b7fd2a0]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 *);
[5d94b16c]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);
[b48e680f]153static errno_t cons_set_caption(con_srv_t *, const char *);
[b7fd2a0]154static errno_t cons_get_event(con_srv_t *, cons_event_t *);
[68a552f]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);
[5d94b16c]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,
[b48e680f]175 .set_caption = cons_set_caption,
[68a552f]176 .get_event = cons_get_event,
177 .map = cons_map,
178 .unmap = cons_unmap,
179 .update = cons_buf_update
[5d94b16c]180};
181
[5d1ff11]182static void pointer_draw(void);
183static void pointer_undraw(void);
184
[5d94b16c]185static console_t *srv_to_console(con_srv_t *srv)
186{
187 return srv->srvs->sarg;
188}
189
[7c014d1]190static void cons_update(console_t *cons)
[9805cde]191{
[7c014d1]192 fibril_mutex_lock(&switch_mtx);
193 fibril_mutex_lock(&cons->mtx);
[a35b458]194
[593e023]195 if ((active) && (cons == active_console)) {
[6d5e378]196 output_update(output_sess, cons->fbid);
197 output_cursor_update(output_sess, cons->fbid);
[7c014d1]198 }
[a35b458]199
[7c014d1]200 fibril_mutex_unlock(&cons->mtx);
201 fibril_mutex_unlock(&switch_mtx);
[9805cde]202}
203
[7c014d1]204static void cons_update_cursor(console_t *cons)
[9805cde]205{
[7c014d1]206 fibril_mutex_lock(&switch_mtx);
207 fibril_mutex_lock(&cons->mtx);
[a35b458]208
[593e023]209 if ((active) && (cons == active_console))
[6d5e378]210 output_cursor_update(output_sess, cons->fbid);
[a35b458]211
[7c014d1]212 fibril_mutex_unlock(&cons->mtx);
213 fibril_mutex_unlock(&switch_mtx);
[429acb9]214}
215
[6d5e378]216static void cons_damage(console_t *cons)
[d2cc7e1]217{
[7c014d1]218 fibril_mutex_lock(&switch_mtx);
219 fibril_mutex_lock(&cons->mtx);
[a35b458]220
[593e023]221 if ((active) && (cons == active_console)) {
[6d5e378]222 output_damage(output_sess, cons->fbid, 0, 0, cons->cols,
[7c014d1]223 cons->rows);
[6d5e378]224 output_cursor_update(output_sess, cons->fbid);
[dc033a1]225 }
[a35b458]226
[7c014d1]227 fibril_mutex_unlock(&cons->mtx);
228 fibril_mutex_unlock(&switch_mtx);
[d2cc7e1]229}
230
[593e023]231static void cons_switch(unsigned int index)
[d2cc7e1]232{
[593e023]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;
[a35b458]240
[593e023]241 return;
242 }
[a35b458]243
[593e023]244 if (index > CONSOLE_COUNT)
245 return;
[a35b458]246
[593e023]247 console_t *cons = &consoles[index];
[a35b458]248
[7c014d1]249 fibril_mutex_lock(&switch_mtx);
[5d1ff11]250 pointer_undraw();
[a35b458]251
[7c014d1]252 if (cons == active_console) {
253 fibril_mutex_unlock(&switch_mtx);
254 return;
255 }
[a35b458]256
[7c014d1]257 active_console = cons;
[a35b458]258
[5d1ff11]259 pointer_draw();
[7c014d1]260 fibril_mutex_unlock(&switch_mtx);
[a35b458]261
[6d5e378]262 cons_damage(cons);
[d2cc7e1]263}
264
[5d1ff11]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
[b7fd2a0]342static errno_t input_ev_active(input_t *input)
[024fcc5]343{
[593e023]344 active = true;
345 output_claim(output_sess);
346 cons_damage(active_console);
[a35b458]347
[593e023]348 return EOK;
349}
350
[b7fd2a0]351static errno_t input_ev_deactive(input_t *input)
[593e023]352{
353 active = false;
354 output_yield(output_sess);
[a35b458]355
[593e023]356 return EOK;
[429acb9]357}
[10569b1]358
[b7fd2a0]359static errno_t input_ev_key(input_t *input, kbd_event_type_t type, keycode_t key,
[28a5ebd]360 keymod_t mods, char32_t c)
[51c1b003]361{
[5d1ff11]362 cons_event_t event;
363
[593e023]364 if ((key >= KC_F1) && (key <= KC_F1 + CONSOLE_COUNT) &&
[111d2d6]365 ((mods & KM_CTRL) == 0)) {
[593e023]366 cons_switch(key - KC_F1);
[111d2d6]367 } else {
368 /* Got key press/release event */
[5d1ff11]369 event.type = CEV_KEY;
[a35b458]370
[5d1ff11]371 event.ev.key.type = type;
372 event.ev.key.key = key;
373 event.ev.key.mods = mods;
374 event.ev.key.c = c;
[a35b458]375
[5d1ff11]376 console_queue_cons_event(active_console, &event);
[7c014d1]377 }
[a35b458]378
[111d2d6]379 return EOK;
380}
381
[5d1ff11]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
[b7fd2a0]419static errno_t input_ev_move(input_t *input, int dx, int dy)
[111d2d6]420{
[5d1ff11]421 pointer_update(pointer_x + dx, pointer_y + dy);
[111d2d6]422 return EOK;
423}
424
[1433ecda]425static errno_t input_ev_abs_move(input_t *input, unsigned x, unsigned y,
[111d2d6]426 unsigned max_x, unsigned max_y)
427{
[5d1ff11]428 pointer_update(mouse_scale_x * cols * x / max_x, mouse_scale_y * rows * y / max_y);
[111d2d6]429 return EOK;
430}
431
[b7fd2a0]432static errno_t input_ev_button(input_t *input, int bnum, int bpress)
[111d2d6]433{
[5d1ff11]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);
[111d2d6]443 return EOK;
[7c014d1]444}
[1875a0c]445
[8edec53]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
[7c014d1]460/** Process a character from the client (TTY emulation). */
[28a5ebd]461static void cons_write_char(console_t *cons, char32_t ch)
[7c014d1]462{
463 sysarg_t updated = 0;
[a35b458]464
[7c014d1]465 fibril_mutex_lock(&cons->mtx);
[5d1ff11]466 pointer_undraw();
[a35b458]467
[7c014d1]468 switch (ch) {
469 case '\n':
[6d5e378]470 updated = chargrid_newline(cons->frontbuf);
[7c014d1]471 break;
472 case '\r':
473 break;
474 case '\t':
[6d5e378]475 updated = chargrid_tabstop(cons->frontbuf, 8);
[7c014d1]476 break;
477 case '\b':
[6d5e378]478 updated = chargrid_backspace(cons->frontbuf);
[7c014d1]479 break;
480 default:
[28a5ebd]481 updated = chargrid_putuchar(cons->frontbuf, ch, true);
[9f51afc]482 }
[a35b458]483
[5d1ff11]484 pointer_draw();
[7c014d1]485 fibril_mutex_unlock(&cons->mtx);
[a35b458]486
[7c014d1]487 if (updated > 1)
488 cons_update(cons);
489}
490
[5d94b16c]491static void cons_set_cursor_vis(console_t *cons, bool visible)
[7c014d1]492{
493 fibril_mutex_lock(&cons->mtx);
[5d1ff11]494 pointer_undraw();
[6d5e378]495 chargrid_set_cursor_visibility(cons->frontbuf, visible);
[5d1ff11]496 pointer_draw();
[7c014d1]497 fibril_mutex_unlock(&cons->mtx);
[a35b458]498
[7c014d1]499 cons_update_cursor(cons);
500}
501
[b7fd2a0]502static errno_t cons_open(con_srvs_t *srvs, con_srv_t *srv)
[7c014d1]503{
[5d94b16c]504 return EOK;
[9f51afc]505}
506
[b7fd2a0]507static errno_t cons_close(con_srv_t *srv)
[d2cc7e1]508{
[5d94b16c]509 return EOK;
[424cd43]510}
511
[b7fd2a0]512static errno_t cons_read(con_srv_t *srv, void *buf, size_t size, size_t *nread)
[424cd43]513{
[5d94b16c]514 uint8_t *bbuf = buf;
515 console_t *cons = srv_to_console(srv);
[424cd43]516 size_t pos = 0;
[a35b458]517
[a58bc8b]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 */
[7c014d1]523 while (pos < size) {
[a58bc8b]524 /* Copy to the buffer remaining characters. */
525 while ((pos < size) && (cons->char_remains_len > 0)) {
[5d94b16c]526 bbuf[pos] = cons->char_remains[0];
[424cd43]527 pos++;
[a35b458]528
[a58bc8b]529 /* Unshift the array. */
[e435537]530 for (size_t i = 1; i < cons->char_remains_len; i++)
[a58bc8b]531 cons->char_remains[i - 1] = cons->char_remains[i];
[a35b458]532
[a58bc8b]533 cons->char_remains_len--;
534 }
[a35b458]535
[a58bc8b]536 /* Still not enough? Then get another key from the queue. */
537 if (pos < size) {
538 link_t *link = prodcons_consume(&cons->input_pc);
[5d1ff11]539 cons_event_t *event = list_get_instance(link,
540 cons_event_t, link);
[a35b458]541
[a58bc8b]542 /* Accept key presses of printable chars only. */
[5d1ff11]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 };
[a58bc8b]546 wstr_to_str(cons->char_remains, UTF8_CHAR_BUFFER_SIZE, tmp);
547 cons->char_remains_len = str_size(cons->char_remains);
548 }
[a35b458]549
[a58bc8b]550 free(event);
[424cd43]551 }
552 }
[a35b458]553
[c8211849]554 *nread = size;
555 return EOK;
[5d94b16c]556}
557
[b7fd2a0]558static errno_t cons_write(con_srv_t *srv, void *data, size_t size, size_t *nwritten)
[5d94b16c]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));
[a35b458]565
[c8211849]566 *nwritten = size;
567 return EOK;
[5d94b16c]568}
569
570static void cons_sync(con_srv_t *srv)
571{
572 console_t *cons = srv_to_console(srv);
[a35b458]573
[5d94b16c]574 cons_update(cons);
[424cd43]575}
576
[5d94b16c]577static void cons_clear(con_srv_t *srv)
[424cd43]578{
[5d94b16c]579 console_t *cons = srv_to_console(srv);
[a35b458]580
[5d94b16c]581 fibril_mutex_lock(&cons->mtx);
[5d1ff11]582 pointer_undraw();
[5d94b16c]583 chargrid_clear(cons->frontbuf);
[5d1ff11]584 pointer_draw();
[5d94b16c]585 fibril_mutex_unlock(&cons->mtx);
[a35b458]586
[5d94b16c]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);
[a35b458]593
[5d94b16c]594 fibril_mutex_lock(&cons->mtx);
[5d1ff11]595 pointer_undraw();
[5d94b16c]596 chargrid_set_cursor(cons->frontbuf, col, row);
[5d1ff11]597 pointer_draw();
[5d94b16c]598 fibril_mutex_unlock(&cons->mtx);
[a35b458]599
[5d94b16c]600 cons_update_cursor(cons);
601}
602
[b7fd2a0]603static errno_t cons_get_pos(con_srv_t *srv, sysarg_t *col, sysarg_t *row)
[5d94b16c]604{
605 console_t *cons = srv_to_console(srv);
[a35b458]606
[5d94b16c]607 fibril_mutex_lock(&cons->mtx);
608 chargrid_get_cursor(cons->frontbuf, col, row);
609 fibril_mutex_unlock(&cons->mtx);
[a35b458]610
[5d94b16c]611 return EOK;
612}
613
[b7fd2a0]614static errno_t cons_get_size(con_srv_t *srv, sysarg_t *cols, sysarg_t *rows)
[5d94b16c]615{
616 console_t *cons = srv_to_console(srv);
[a35b458]617
[5d94b16c]618 fibril_mutex_lock(&cons->mtx);
619 *cols = cons->cols;
620 *rows = cons->rows;
621 fibril_mutex_unlock(&cons->mtx);
[a35b458]622
[5d94b16c]623 return EOK;
624}
625
[b7fd2a0]626static errno_t cons_get_color_cap(con_srv_t *srv, console_caps_t *ccaps)
[5d94b16c]627{
628 console_t *cons = srv_to_console(srv);
[a35b458]629
[5d94b16c]630 fibril_mutex_lock(&cons->mtx);
631 *ccaps = cons->ccaps;
632 fibril_mutex_unlock(&cons->mtx);
[a35b458]633
[5d94b16c]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);
[a35b458]640
[7c014d1]641 fibril_mutex_lock(&cons->mtx);
[6d5e378]642 chargrid_set_style(cons->frontbuf, style);
[7c014d1]643 fibril_mutex_unlock(&cons->mtx);
644}
645
[5d94b16c]646static void cons_set_color(con_srv_t *srv, console_color_t bgcolor,
[7c014d1]647 console_color_t fgcolor, console_color_attr_t attr)
648{
[5d94b16c]649 console_t *cons = srv_to_console(srv);
[a35b458]650
[7c014d1]651 fibril_mutex_lock(&cons->mtx);
[6d5e378]652 chargrid_set_color(cons->frontbuf, bgcolor, fgcolor, attr);
[7c014d1]653 fibril_mutex_unlock(&cons->mtx);
654}
655
[5d94b16c]656static void cons_set_rgb_color(con_srv_t *srv, pixel_t bgcolor,
[7c014d1]657 pixel_t fgcolor)
658{
[5d94b16c]659 console_t *cons = srv_to_console(srv);
[a35b458]660
[7c014d1]661 fibril_mutex_lock(&cons->mtx);
[6d5e378]662 chargrid_set_rgb_color(cons->frontbuf, bgcolor, fgcolor);
[7c014d1]663 fibril_mutex_unlock(&cons->mtx);
664}
665
[5d94b16c]666static void cons_set_cursor_visibility(con_srv_t *srv, bool visible)
667{
668 console_t *cons = srv_to_console(srv);
[a35b458]669
[5d94b16c]670 cons_set_cursor_vis(cons, visible);
671}
672
[b48e680f]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
[b7fd2a0]682static errno_t cons_get_event(con_srv_t *srv, cons_event_t *event)
[7c014d1]683{
[5d94b16c]684 console_t *cons = srv_to_console(srv);
[7c014d1]685 link_t *link = prodcons_consume(&cons->input_pc);
[5d1ff11]686 cons_event_t *cevent = list_get_instance(link, cons_event_t, link);
[a35b458]687
[5d1ff11]688 *event = *cevent;
689 free(cevent);
[5d94b16c]690 return EOK;
[d2cc7e1]691}
692
[68a552f]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
[5d1ff11]794 pointer_undraw();
795
[68a552f]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
[5d1ff11]803 pointer_draw();
[68a552f]804 fibril_mutex_unlock(&cons->mtx);
805
806 /* Update console */
807 cons_update(cons);
808}
809
[984a9ba]810static void client_connection(ipc_call_t *icall, void *arg)
[eaf34f7]811{
[424cd43]812 console_t *cons = NULL;
[a35b458]813
[7c014d1]814 for (size_t i = 0; i < CONSOLE_COUNT; i++) {
[fafb8e5]815 if (consoles[i].dsid == (service_id_t) ipc_get_arg2(icall)) {
[424cd43]816 cons = &consoles[i];
817 break;
818 }
819 }
[a35b458]820
[424cd43]821 if (cons == NULL) {
[984a9ba]822 async_answer_0(icall, ENOENT);
[eaf34f7]823 return;
824 }
[a35b458]825
[508b0df1]826 if (!atomic_flag_test_and_set(&cons->refcnt))
[5d94b16c]827 cons_set_cursor_vis(cons, true);
[a35b458]828
[984a9ba]829 con_conn(icall, &cons->srvs);
[eaf34f7]830}
831
[b7fd2a0]832static errno_t input_connect(const char *svc)
[eaf34f7]833{
[a40dea3]834 async_sess_t *sess;
[7c014d1]835 service_id_t dsid;
[a35b458]836
[b7fd2a0]837 errno_t rc = loc_service_get_id(svc, &dsid, 0);
[111d2d6]838 if (rc != EOK) {
839 printf("%s: Input service %s not found\n", NAME, svc);
840 return rc;
841 }
842
[f9b2cb4c]843 sess = loc_service_connect(dsid, INTERFACE_INPUT, 0);
[111d2d6]844 if (sess == NULL) {
845 printf("%s: Unable to connect to input service %s\n", NAME,
846 svc);
847 return EIO;
848 }
[a35b458]849
[111d2d6]850 rc = input_open(sess, &input_ev_ops, NULL, &input);
[700af62]851 if (rc != EOK) {
[a40dea3]852 async_hangup(sess);
[111d2d6]853 printf("%s: Unable to communicate with service %s (%s)\n",
[7c014d1]854 NAME, svc, str_error(rc));
[111d2d6]855 return rc;
[4904de8]856 }
[a35b458]857
[111d2d6]858 return EOK;
[700af62]859}
860
[6d5e378]861static async_sess_t *output_connect(const char *svc)
[700af62]862{
[7c014d1]863 async_sess_t *sess;
864 service_id_t dsid;
[a35b458]865
[b7fd2a0]866 errno_t rc = loc_service_get_id(svc, &dsid, 0);
[7c014d1]867 if (rc == EOK) {
[f9b2cb4c]868 sess = loc_service_connect(dsid, INTERFACE_OUTPUT, 0);
[7c014d1]869 if (sess == NULL) {
[6d5e378]870 printf("%s: Unable to connect to output service %s\n",
[7c014d1]871 NAME, svc);
872 return NULL;
873 }
874 } else
875 return NULL;
[a35b458]876
[7c014d1]877 return sess;
878}
879
[6d5e378]880static bool console_srv_init(char *input_svc, char *output_svc)
[7c014d1]881{
882 /* Connect to input service */
[b7fd2a0]883 errno_t rc = input_connect(input_svc);
[111d2d6]884 if (rc != EOK)
[700af62]885 return false;
[a35b458]886
[6d5e378]887 /* Connect to output service */
888 output_sess = output_connect(output_svc);
889 if (output_sess == NULL)
[79ae36dd]890 return false;
[a35b458]891
[15f3c3f]892 /* Register server */
[b688fd8]893 async_set_fallback_port_handler(client_connection, NULL);
[111d2d6]894 rc = loc_server_register(NAME);
[2f90b46]895 if (rc != EOK) {
[7c014d1]896 printf("%s: Unable to register server (%s)\n", NAME,
897 str_error(rc));
[424cd43]898 return false;
899 }
[a35b458]900
[6d5e378]901 output_get_dimensions(output_sess, &cols, &rows);
902 output_set_style(output_sess, STYLE_NORMAL);
[a35b458]903
[7c014d1]904 console_caps_t ccaps;
[6d5e378]905 output_get_caps(output_sess, &ccaps);
[a35b458]906
[593e023]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;
[508b0df1]914 atomic_flag_clear(&consoles[i].refcnt);
[593e023]915 fibril_mutex_initialize(&consoles[i].mtx);
916 prodcons_initialize(&consoles[i].input_pc);
917 consoles[i].char_remains_len = 0;
[a35b458]918
[593e023]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);
[a35b458]924
[593e023]925 if (consoles[i].frontbuf == NULL) {
926 printf("%s: Unable to allocate frontbuffer %zu\n", NAME, i);
927 return false;
928 }
[a35b458]929
[593e023]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 }
[a35b458]936
[593e023]937 con_srvs_init(&consoles[i].srvs);
938 consoles[i].srvs.ops = &con_ops;
939 consoles[i].srvs.sarg = &consoles[i];
[a35b458]940
[593e023]941 char vc[LOC_NAME_MAXLEN + 1];
942 snprintf(vc, LOC_NAME_MAXLEN, "%s/vc%zu", NAMESPACE, i);
[a35b458]943
[593e023]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 }
[7c014d1]948 }
[a35b458]949
[593e023]950 input_activate(input);
[0350033]951 active = true;
952 cons_damage(active_console);
[424cd43]953 }
[a35b458]954
[424cd43]955 return true;
956}
957
[6d5e378]958static void usage(char *name)
[47a350f]959{
[6d5e378]960 printf("Usage: %s <input_dev> <output_dev>\n", name);
[47a350f]961}
962
[424cd43]963int main(int argc, char *argv[])
964{
[7c014d1]965 if (argc < 3) {
[6d5e378]966 usage(argv[0]);
[47a350f]967 return -1;
968 }
[a35b458]969
[7c014d1]970 printf("%s: HelenOS Console service\n", NAME);
[a35b458]971
[7c014d1]972 if (!console_srv_init(argv[1], argv[2]))
[424cd43]973 return -1;
[a35b458]974
[7c014d1]975 printf("%s: Accepting connections\n", NAME);
976 task_retval(0);
[eaf34f7]977 async_manager();
[a35b458]978
[6d5e378]979 /* Never reached */
[3ad953c]980 return 0;
[51c1b003]981}
[516ff92]982
[ce5bcb4]983/** @}
984 */
Note: See TracBrowser for help on using the repository browser.