source: mainline/uspace/srv/hid/console/console.c@ 494f417

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 494f417 was 8a99c7e, checked in by Martin Sucha <sucha14@…>, 13 years ago

Add basic support for absolute mouse/pointing devices.

  • Property mode set to 100644
File size: 26.2 KB
RevLine 
[51c1b003]1/*
[7c014d1]2 * Copyright (c) 2011 Martin Decky
[51c1b003]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 */
[ce5bcb4]28
[231a60a]29/** @addtogroup console
[1601f3c]30 * @{
[ce5bcb4]31 */
32/** @file
33 */
34
[7c014d1]35#include <async.h>
36#include <stdio.h>
37#include <adt/prodcons.h>
[5f88293]38#include <ipc/input.h>
[7c014d1]39#include <ipc/console.h>
40#include <ipc/vfs.h>
[51c1b003]41#include <errno.h>
[30db06c]42#include <str_error.h>
[15f3c3f]43#include <loc.h>
[7c014d1]44#include <event.h>
45#include <io/keycode.h>
46#include <screenbuffer.h>
47#include <fb.h>
48#include <imgmap.h>
49#include <align.h>
50#include <malloc.h>
51#include <as.h>
[1e4cada]52#include <fibril_synch.h>
[7c014d1]53#include "images.h"
[9805cde]54#include "console.h"
[369a5f8]55
[1313ee9]56#define NAME "console"
57#define NAMESPACE "term"
[79ae36dd]58
[7c014d1]59#define CONSOLE_TOP 66
60#define CONSOLE_MARGIN 12
61
[cccc091]62#define STATE_START 100
[7c014d1]63#define STATE_TOP 8
64#define STATE_SPACE 4
65#define STATE_WIDTH 48
66#define STATE_HEIGHT 48
67
68typedef enum {
69 CONS_DISCONNECTED = 0,
70 CONS_DISCONNECTED_SELECTED,
71 CONS_SELECTED,
72 CONS_IDLE,
73 CONS_DATA,
74 CONS_KERNEL,
75 CONS_LAST
76} console_state_t;
[3993b3d]77
[a58bc8b]78#define UTF8_CHAR_BUFFER_SIZE (STR_BOUNDS(1) + 1)
79
[79460ae]80typedef struct {
[7c014d1]81 atomic_t refcnt; /**< Connection reference count */
82 prodcons_t input_pc; /**< Incoming keyboard events */
[a58bc8b]83 char char_remains[UTF8_CHAR_BUFFER_SIZE]; /**< Not yet sent bytes of last char event. */
84 size_t char_remains_len; /**< Number of not yet sent bytes. */
[7c014d1]85
86 fibril_mutex_t mtx; /**< Lock protecting mutable fields */
87
88 size_t index; /**< Console index */
89 console_state_t state; /**< Console state */
90 service_id_t dsid; /**< Service handle */
91
92 vp_handle_t state_vp; /**< State icon viewport */
93 sysarg_t cols; /**< Number of columns */
94 sysarg_t rows; /**< Number of rows */
95 console_caps_t ccaps; /**< Console capabilities */
96
97 screenbuffer_t *frontbuf; /**< Front buffer */
98 frontbuf_handle_t fbid; /**< Front buffer handle */
[424cd43]99} console_t;
[79460ae]100
[7c014d1]101typedef enum {
102 GRAPHICS_NONE = 0,
103 GRAPHICS_BASIC = 1,
104 GRAPHICS_FULL = 2
105} graphics_state_t;
106
107/** Current console state */
108static graphics_state_t graphics_state = GRAPHICS_NONE;
109
110/** State icons */
111static imagemap_handle_t state_icons[CONS_LAST];
112
113/** Session to the input server */
114static async_sess_t *input_sess;
115
116/** Session to the framebuffer server */
117static async_sess_t *fb_sess;
118
119/** Framebuffer resolution */
120static sysarg_t xres;
121static sysarg_t yres;
122
[1601f3c]123/** Array of data for virtual consoles */
[424cd43]124static console_t consoles[CONSOLE_COUNT];
125
[7c014d1]126/** Mutex for console switching */
127static FIBRIL_MUTEX_INITIALIZE(switch_mtx);
128
[424cd43]129static console_t *prev_console = &consoles[0];
[7c014d1]130static console_t *active_console = &consoles[0];
[424cd43]131static console_t *kernel_console = &consoles[KERNEL_CONSOLE];
[1601f3c]132
[7c014d1]133static imgmap_t *logo_img;
134static imgmap_t *nameic_img;
[d2cc7e1]135
[7c014d1]136static imgmap_t *anim_1_img;
137static imgmap_t *anim_2_img;
138static imgmap_t *anim_3_img;
139static imgmap_t *anim_4_img;
[d2cc7e1]140
[7c014d1]141static imagemap_handle_t anim_1;
142static imagemap_handle_t anim_2;
143static imagemap_handle_t anim_3;
144static imagemap_handle_t anim_4;
[429acb9]145
[7c014d1]146static sequence_handle_t anim_seq;
[a40dea3]147
[7c014d1]148static imgmap_t *cons_data_img;
149static imgmap_t *cons_dis_img;
150static imgmap_t *cons_dis_sel_img;
151static imgmap_t *cons_idle_img;
152static imgmap_t *cons_kernel_img;
153static imgmap_t *cons_sel_img;
[a40dea3]154
[7c014d1]155static vp_handle_t logo_vp;
156static imagemap_handle_t logo_handle;
[a40dea3]157
[7c014d1]158static vp_handle_t nameic_vp;
159static imagemap_handle_t nameic_handle;
[8c6337d]160
[7c014d1]161static vp_handle_t screen_vp;
162static vp_handle_t console_vp;
[424cd43]163
[7c014d1]164struct {
165 sysarg_t x;
166 sysarg_t y;
167
168 sysarg_t btn_x;
169 sysarg_t btn_y;
170
171 bool pressed;
172} mouse;
[429acb9]173
[7c014d1]174static void cons_redraw_state(console_t *cons)
[10270a8]175{
[7c014d1]176 if (graphics_state == GRAPHICS_FULL) {
177 fibril_mutex_lock(&cons->mtx);
178
179 fb_vp_imagemap_damage(fb_sess, cons->state_vp,
180 state_icons[cons->state], 0, 0, STATE_WIDTH, STATE_HEIGHT);
181
182 if ((cons->state != CONS_DISCONNECTED) &&
183 (cons->state != CONS_KERNEL) &&
184 (cons->state != CONS_DISCONNECTED_SELECTED)) {
185 char data[5];
186 snprintf(data, 5, "%zu", cons->index + 1);
187
188 for (size_t i = 0; data[i] != 0; i++)
189 fb_vp_putchar(fb_sess, cons->state_vp, i + 2, 1, data[i]);
190 }
191
192 fibril_mutex_unlock(&cons->mtx);
193 }
[10270a8]194}
195
[7c014d1]196static void cons_kernel_sequence_start(console_t *cons)
[10270a8]197{
[7c014d1]198 if (graphics_state == GRAPHICS_FULL) {
199 fibril_mutex_lock(&cons->mtx);
200
201 fb_vp_sequence_start(fb_sess, cons->state_vp, anim_seq);
202 fb_vp_imagemap_damage(fb_sess, cons->state_vp,
203 state_icons[cons->state], 0, 0, STATE_WIDTH, STATE_HEIGHT);
204
205 fibril_mutex_unlock(&cons->mtx);
206 }
[10270a8]207}
208
[7c014d1]209static void cons_update_state(console_t *cons, console_state_t state)
[ccd1a14]210{
[7c014d1]211 bool update = false;
212
213 fibril_mutex_lock(&cons->mtx);
214
215 if (cons->state != state) {
216 cons->state = state;
217 update = true;
[a40dea3]218 }
219
[7c014d1]220 fibril_mutex_unlock(&cons->mtx);
221
222 if (update)
223 cons_redraw_state(cons);
[ccd1a14]224}
225
[7c014d1]226static void cons_notify_data(console_t *cons)
[ccd1a14]227{
[7c014d1]228 fibril_mutex_lock(&switch_mtx);
[a40dea3]229
[7c014d1]230 if (cons != active_console)
231 cons_update_state(cons, CONS_DATA);
232
233 fibril_mutex_unlock(&switch_mtx);
[ccd1a14]234}
235
[7c014d1]236static void cons_notify_connect(console_t *cons)
[429acb9]237{
[7c014d1]238 fibril_mutex_lock(&switch_mtx);
239
240 if (cons == active_console)
241 cons_update_state(cons, CONS_SELECTED);
242 else
243 cons_update_state(cons, CONS_IDLE);
244
245 fibril_mutex_unlock(&switch_mtx);
[429acb9]246}
247
[7c014d1]248static void cons_notify_disconnect(console_t *cons)
[429acb9]249{
[7c014d1]250 fibril_mutex_lock(&switch_mtx);
251
252 if (cons == active_console)
253 cons_update_state(cons, CONS_DISCONNECTED_SELECTED);
254 else
255 cons_update_state(cons, CONS_DISCONNECTED);
256
257 fibril_mutex_unlock(&switch_mtx);
[9805cde]258}
259
[7c014d1]260static void cons_update(console_t *cons)
[9805cde]261{
[7c014d1]262 fibril_mutex_lock(&switch_mtx);
263 fibril_mutex_lock(&cons->mtx);
264
265 if ((cons == active_console) && (active_console != kernel_console)) {
266 fb_vp_update(fb_sess, console_vp, cons->fbid);
267 fb_vp_cursor_update(fb_sess, console_vp, cons->fbid);
268 }
269
270 fibril_mutex_unlock(&cons->mtx);
271 fibril_mutex_unlock(&switch_mtx);
[9805cde]272}
273
[7c014d1]274static void cons_update_cursor(console_t *cons)
[9805cde]275{
[7c014d1]276 fibril_mutex_lock(&switch_mtx);
277 fibril_mutex_lock(&cons->mtx);
278
279 if ((cons == active_console) && (active_console != kernel_console))
280 fb_vp_cursor_update(fb_sess, console_vp, cons->fbid);
281
282 fibril_mutex_unlock(&cons->mtx);
283 fibril_mutex_unlock(&switch_mtx);
[429acb9]284}
285
[7c014d1]286static void cons_clear(console_t *cons)
[50cfa6c]287{
[7c014d1]288 fibril_mutex_lock(&cons->mtx);
289 screenbuffer_clear(cons->frontbuf);
290 fibril_mutex_unlock(&cons->mtx);
[9f1362d4]291
[7c014d1]292 cons_update(cons);
[50cfa6c]293}
294
[7c014d1]295static void cons_damage_all(console_t *cons)
[d2cc7e1]296{
[7c014d1]297 fibril_mutex_lock(&switch_mtx);
298 fibril_mutex_lock(&cons->mtx);
299
300 if ((cons == active_console) && (active_console != kernel_console)) {
301 fb_vp_damage(fb_sess, console_vp, cons->fbid, 0, 0, cons->cols,
302 cons->rows);
303 fb_vp_cursor_update(fb_sess, console_vp, cons->fbid);
[dc033a1]304 }
[7c014d1]305
306 fibril_mutex_unlock(&cons->mtx);
307 fibril_mutex_unlock(&switch_mtx);
[d2cc7e1]308}
309
[7c014d1]310static void cons_switch(console_t *cons)
[d2cc7e1]311{
[7c014d1]312 fibril_mutex_lock(&switch_mtx);
313
314 if (cons == active_console) {
315 fibril_mutex_unlock(&switch_mtx);
316 return;
317 }
318
319 if (cons == kernel_console) {
320 fb_yield(fb_sess);
321 if (!console_kcon()) {
322 fb_claim(fb_sess);
323 fibril_mutex_unlock(&switch_mtx);
324 return;
325 }
[d2cc7e1]326 }
[7c014d1]327
328 if (active_console == kernel_console)
329 fb_claim(fb_sess);
330
331 prev_console = active_console;
332 active_console = cons;
333
334 if (prev_console->state == CONS_DISCONNECTED_SELECTED)
335 cons_update_state(prev_console, CONS_DISCONNECTED);
336 else
337 cons_update_state(prev_console, CONS_IDLE);
338
339 if ((cons->state == CONS_DISCONNECTED) ||
340 (cons->state == CONS_DISCONNECTED_SELECTED))
341 cons_update_state(cons, CONS_DISCONNECTED_SELECTED);
342 else
343 cons_update_state(cons, CONS_SELECTED);
344
345 fibril_mutex_unlock(&switch_mtx);
346
347 cons_damage_all(cons);
[d2cc7e1]348}
349
[024fcc5]350static console_t *cons_get_active_uspace(void)
351{
352 fibril_mutex_lock(&switch_mtx);
353
354 console_t *active_uspace = active_console;
355 if (active_uspace == kernel_console) {
356 active_uspace = prev_console;
357 }
358 assert(active_uspace != kernel_console);
359
360 fibril_mutex_unlock(&switch_mtx);
361
362 return active_uspace;
363}
364
[7c014d1]365static ssize_t limit(ssize_t val, ssize_t lo, ssize_t hi)
[429acb9]366{
[7c014d1]367 if (val > hi)
368 return hi;
[1601f3c]369
[7c014d1]370 if (val < lo)
371 return lo;
[1601f3c]372
[7c014d1]373 return val;
[429acb9]374}
375
[7c014d1]376static void cons_mouse_move(sysarg_t dx, sysarg_t dy)
[dc033a1]377{
[7c014d1]378 ssize_t sx = (ssize_t) dx;
379 ssize_t sy = (ssize_t) dy;
380
381 mouse.x = limit(mouse.x + sx, 0, xres);
382 mouse.y = limit(mouse.y + sy, 0, yres);
383
384 fb_pointer_update(fb_sess, mouse.x, mouse.y, true);
[dc033a1]385}
386
[8a99c7e]387static void cons_mouse_abs_move(sysarg_t x, sysarg_t y,
388 sysarg_t max_x, sysarg_t max_y)
389{
390 if (max_x && max_y) {
391 mouse.x = limit(x * xres / max_x, 0, xres);
392 mouse.y = limit(y * yres / max_y, 0, yres);
393
394 fb_pointer_update(fb_sess, mouse.x, mouse.y, true);
395 }
396}
397
[7c014d1]398static console_t *cons_find_icon(sysarg_t x, sysarg_t y)
[10569b1]399{
[cccc091]400 sysarg_t status_start =
401 STATE_START + (xres - 800) / 2 + CONSOLE_MARGIN;
[9f1362d4]402
[7c014d1]403 if ((y < STATE_TOP) || (y >= STATE_TOP + STATE_HEIGHT))
404 return NULL;
[1601f3c]405
[7c014d1]406 if (x < status_start)
407 return NULL;
[10569b1]408
[7c014d1]409 if (x >= status_start + (STATE_WIDTH + STATE_SPACE) * CONSOLE_COUNT)
410 return NULL;
411
[cccc091]412 if (((x - status_start) % (STATE_WIDTH + STATE_SPACE)) >= STATE_WIDTH)
[7c014d1]413 return NULL;
414
415 sysarg_t btn = (x - status_start) / (STATE_WIDTH + STATE_SPACE);
416
417 if (btn < CONSOLE_COUNT)
418 return consoles + btn;
[9f1362d4]419
[7c014d1]420 return NULL;
[10569b1]421}
422
[7c014d1]423/** Handle mouse click
424 *
425 * @param state Button state (true - pressed, false - depressed)
426 *
427 */
428static console_t *cons_mouse_button(bool state)
[429acb9]429{
[7c014d1]430 if (graphics_state != GRAPHICS_FULL)
431 return NULL;
[6d5005c]432
[7c014d1]433 if (state) {
434 console_t *cons = cons_find_icon(mouse.x, mouse.y);
435 if (cons != NULL) {
436 mouse.btn_x = mouse.x;
437 mouse.btn_y = mouse.y;
438 mouse.pressed = true;
[10270a8]439 }
[76fca31]440
[7c014d1]441 return NULL;
[429acb9]442 }
[7c014d1]443
444 if ((!state) && (!mouse.pressed))
445 return NULL;
446
447 console_t *cons = cons_find_icon(mouse.x, mouse.y);
448 if (cons == cons_find_icon(mouse.btn_x, mouse.btn_y))
449 return cons;
450
451 mouse.pressed = false;
452 return NULL;
[429acb9]453}
[10569b1]454
[854eddd6]455static void input_events(ipc_callid_t iid, ipc_call_t *icall, void *arg)
[51c1b003]456{
[424cd43]457 /* Ignore parameters, the connection is already opened */
[1601f3c]458 while (true) {
[424cd43]459 ipc_call_t call;
460 ipc_callid_t callid = async_get_call(&call);
461
[79ae36dd]462 if (!IPC_GET_IMETHOD(call)) {
[eaf34f7]463 /* TODO: Handle hangup */
[a40dea3]464 async_hangup(input_sess);
[eaf34f7]465 return;
[79ae36dd]466 }
467
[7c014d1]468 kbd_event_type_t type;
469 keycode_t key;
470 keymod_t mods;
471 wchar_t c;
472
[79ae36dd]473 switch (IPC_GET_IMETHOD(call)) {
[854eddd6]474 case INPUT_EVENT_KEY:
[7c014d1]475 type = IPC_GET_ARG1(call);
476 key = IPC_GET_ARG2(call);
477 mods = IPC_GET_ARG3(call);
478 c = IPC_GET_ARG4(call);
[fa09449]479
[7c014d1]480 if ((key >= KC_F1) && (key < KC_F1 + CONSOLE_COUNT) &&
481 ((mods & KM_CTRL) == 0))
482 cons_switch(&consoles[key - KC_F1]);
483 else {
484 /* Got key press/release event */
485 kbd_event_t *event =
486 (kbd_event_t *) malloc(sizeof(kbd_event_t));
487 if (event == NULL) {
488 async_answer_0(callid, ENOMEM);
489 break;
490 }
491
492 link_initialize(&event->link);
493 event->type = type;
494 event->key = key;
495 event->mods = mods;
496 event->c = c;
497
[e37eddc]498 /*
499 * Kernel console does not read events
[024fcc5]500 * from us, so we will redirect them
501 * to the (last) active userspace console
502 * if necessary.
503 */
504 console_t *target_console = cons_get_active_uspace();
[e37eddc]505
[024fcc5]506 prodcons_produce(&target_console->input_pc,
507 &event->link);
[eaf34f7]508 }
[cf28036c]509
[7c014d1]510 async_answer_0(callid, EOK);
[eaf34f7]511 break;
[854eddd6]512 case INPUT_EVENT_MOVE:
[7c014d1]513 cons_mouse_move(IPC_GET_ARG1(call), IPC_GET_ARG2(call));
514 async_answer_0(callid, EOK);
[854eddd6]515 break;
[8a99c7e]516 case INPUT_EVENT_ABS_MOVE:
517 cons_mouse_abs_move(IPC_GET_ARG1(call), IPC_GET_ARG2(call),
518 IPC_GET_ARG3(call), IPC_GET_ARG4(call));
519 async_answer_0(callid, EOK);
520 break;
[854eddd6]521 case INPUT_EVENT_BUTTON:
522 /* Got pointer button press/release event */
[9f1362d4]523 if (IPC_GET_ARG1(call) == 1) {
[7c014d1]524 console_t *cons =
525 cons_mouse_button((bool) IPC_GET_ARG2(call));
526 if (cons != NULL)
527 cons_switch(cons);
[9f51afc]528 }
[7c014d1]529 async_answer_0(callid, EOK);
[9f51afc]530 break;
531 default:
[7c014d1]532 async_answer_0(callid, EINVAL);
[9f51afc]533 }
[7c014d1]534 }
535}
[1875a0c]536
[7c014d1]537/** Process a character from the client (TTY emulation). */
538static void cons_write_char(console_t *cons, wchar_t ch)
539{
540 sysarg_t updated = 0;
541
542 fibril_mutex_lock(&cons->mtx);
543
544 switch (ch) {
545 case '\n':
546 updated = screenbuffer_newline(cons->frontbuf);
547 break;
548 case '\r':
549 break;
550 case '\t':
551 updated = screenbuffer_tabstop(cons->frontbuf, 8);
552 break;
553 case '\b':
554 updated = screenbuffer_backspace(cons->frontbuf);
555 break;
556 default:
557 updated = screenbuffer_putchar(cons->frontbuf, ch, true);
[9f51afc]558 }
[7c014d1]559
560 fibril_mutex_unlock(&cons->mtx);
561
562 if (updated > 1)
563 cons_update(cons);
564}
565
566static void cons_set_cursor(console_t *cons, sysarg_t col, sysarg_t row)
567{
568 fibril_mutex_lock(&cons->mtx);
569 screenbuffer_set_cursor(cons->frontbuf, col, row);
570 fibril_mutex_unlock(&cons->mtx);
571
572 cons_update_cursor(cons);
573}
574
575static void cons_set_cursor_visibility(console_t *cons, bool visible)
576{
577 fibril_mutex_lock(&cons->mtx);
578 screenbuffer_set_cursor_visibility(cons->frontbuf, visible);
579 fibril_mutex_unlock(&cons->mtx);
580
581 cons_update_cursor(cons);
582}
583
584static void cons_get_cursor(console_t *cons, ipc_callid_t iid, ipc_call_t *icall)
585{
586 sysarg_t col;
587 sysarg_t row;
588
589 fibril_mutex_lock(&cons->mtx);
590 screenbuffer_get_cursor(cons->frontbuf, &col, &row);
591 fibril_mutex_unlock(&cons->mtx);
592
593 async_answer_2(iid, EOK, col, row);
[9f51afc]594}
595
[7c014d1]596static void cons_write(console_t *cons, ipc_callid_t iid, ipc_call_t *icall)
[d2cc7e1]597{
[472c09d]598 void *buf;
[171f9a1]599 size_t size;
[eda925a]600 int rc = async_data_write_accept(&buf, false, 0, 0, 0, &size);
[1601f3c]601
[472c09d]602 if (rc != EOK) {
[7c014d1]603 async_answer_0(iid, rc);
[424cd43]604 return;
605 }
[1601f3c]606
[424cd43]607 size_t off = 0;
[7c014d1]608 while (off < size)
609 cons_write_char(cons, str_decode(buf, &off, size));
[424cd43]610
[7c014d1]611 async_answer_1(iid, EOK, size);
[424cd43]612 free(buf);
[7c014d1]613
614 cons_notify_data(cons);
[424cd43]615}
616
[7c014d1]617static void cons_read(console_t *cons, ipc_callid_t iid, ipc_call_t *icall)
[424cd43]618{
619 ipc_callid_t callid;
620 size_t size;
[0da4e41]621 if (!async_data_read_receive(&callid, &size)) {
[ffa2c8ef]622 async_answer_0(callid, EINVAL);
[7c014d1]623 async_answer_0(iid, EINVAL);
[424cd43]624 return;
625 }
626
627 char *buf = (char *) malloc(size);
628 if (buf == NULL) {
[ffa2c8ef]629 async_answer_0(callid, ENOMEM);
[7c014d1]630 async_answer_0(iid, ENOMEM);
[424cd43]631 return;
632 }
633
634 size_t pos = 0;
[e435537]635
[a58bc8b]636 /*
637 * Read input from keyboard and copy it to the buffer.
638 * We need to handle situation when wchar is split by 2 following
639 * reads.
640 */
[7c014d1]641 while (pos < size) {
[a58bc8b]642 /* Copy to the buffer remaining characters. */
643 while ((pos < size) && (cons->char_remains_len > 0)) {
644 buf[pos] = cons->char_remains[0];
[424cd43]645 pos++;
[e435537]646
[a58bc8b]647 /* Unshift the array. */
[e435537]648 for (size_t i = 1; i < cons->char_remains_len; i++)
[a58bc8b]649 cons->char_remains[i - 1] = cons->char_remains[i];
[e435537]650
[a58bc8b]651 cons->char_remains_len--;
652 }
[e435537]653
[a58bc8b]654 /* Still not enough? Then get another key from the queue. */
655 if (pos < size) {
656 link_t *link = prodcons_consume(&cons->input_pc);
657 kbd_event_t *event = list_get_instance(link, kbd_event_t, link);
[e435537]658
[a58bc8b]659 /* Accept key presses of printable chars only. */
660 if ((event->type == KEY_PRESS) && (event->c != 0)) {
661 wchar_t tmp[2] = { event->c, 0 };
662 wstr_to_str(cons->char_remains, UTF8_CHAR_BUFFER_SIZE, tmp);
663 cons->char_remains_len = str_size(cons->char_remains);
664 }
[e435537]665
[a58bc8b]666 free(event);
[424cd43]667 }
668 }
669
[7c014d1]670 (void) async_data_read_finalize(callid, buf, size);
671 async_answer_1(iid, EOK, size);
672 free(buf);
[424cd43]673}
674
[7c014d1]675static void cons_set_style(console_t *cons, console_style_t style)
[424cd43]676{
[7c014d1]677 fibril_mutex_lock(&cons->mtx);
678 screenbuffer_set_style(cons->frontbuf, style);
679 fibril_mutex_unlock(&cons->mtx);
680}
681
682static void cons_set_color(console_t *cons, console_color_t bgcolor,
683 console_color_t fgcolor, console_color_attr_t attr)
684{
685 fibril_mutex_lock(&cons->mtx);
686 screenbuffer_set_color(cons->frontbuf, bgcolor, fgcolor, attr);
687 fibril_mutex_unlock(&cons->mtx);
688}
689
690static void cons_set_rgb_color(console_t *cons, pixel_t bgcolor,
691 pixel_t fgcolor)
692{
693 fibril_mutex_lock(&cons->mtx);
694 screenbuffer_set_rgb_color(cons->frontbuf, bgcolor, fgcolor);
695 fibril_mutex_unlock(&cons->mtx);
696}
697
698static void cons_get_event(console_t *cons, ipc_callid_t iid, ipc_call_t *icall)
699{
700 link_t *link = prodcons_consume(&cons->input_pc);
701 kbd_event_t *event = list_get_instance(link, kbd_event_t, link);
[9f1362d4]702
[7c014d1]703 async_answer_4(iid, EOK, event->type, event->key, event->mods, event->c);
704 free(event);
[d2cc7e1]705}
706
[9934f7d]707static void client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
[eaf34f7]708{
[424cd43]709 console_t *cons = NULL;
[3ad953c]710
[7c014d1]711 for (size_t i = 0; i < CONSOLE_COUNT; i++) {
[424cd43]712 if (i == KERNEL_CONSOLE)
713 continue;
714
[7c014d1]715 if (consoles[i].dsid == (service_id_t) IPC_GET_ARG1(*icall)) {
[424cd43]716 cons = &consoles[i];
717 break;
718 }
719 }
720
721 if (cons == NULL) {
[ffa2c8ef]722 async_answer_0(iid, ENOENT);
[eaf34f7]723 return;
724 }
[424cd43]725
[7c014d1]726 if (atomic_postinc(&cons->refcnt) == 0) {
727 cons_set_cursor_visibility(cons, true);
728 cons_notify_connect(cons);
729 }
[10569b1]730
[eaf34f7]731 /* Accept the connection */
[ffa2c8ef]732 async_answer_0(iid, EOK);
[3ad953c]733
[1601f3c]734 while (true) {
[7c014d1]735 ipc_call_t call;
736 ipc_callid_t callid = async_get_call(&call);
[1601f3c]737
[79ae36dd]738 if (!IPC_GET_IMETHOD(call)) {
[7c014d1]739 if (atomic_postdec(&cons->refcnt) == 1)
740 cons_notify_disconnect(cons);
741
[eaf34f7]742 return;
[79ae36dd]743 }
744
745 switch (IPC_GET_IMETHOD(call)) {
[4198f9c3]746 case VFS_OUT_READ:
[424cd43]747 cons_read(cons, callid, &call);
[7c014d1]748 break;
[4198f9c3]749 case VFS_OUT_WRITE:
[424cd43]750 cons_write(cons, callid, &call);
[7c014d1]751 break;
[4198f9c3]752 case VFS_OUT_SYNC:
[7c014d1]753 cons_update(cons);
754 async_answer_0(callid, EOK);
[424cd43]755 break;
[ad123964]756 case CONSOLE_CLEAR:
[7c014d1]757 cons_clear(cons);
758 async_answer_0(callid, EOK);
[eaf34f7]759 break;
[ad123964]760 case CONSOLE_GOTO:
[7c014d1]761 cons_set_cursor(cons, IPC_GET_ARG1(call), IPC_GET_ARG2(call));
762 async_answer_0(callid, EOK);
[ad123964]763 break;
[19528516]764 case CONSOLE_GET_POS:
[7c014d1]765 cons_get_cursor(cons, callid, &call);
[19528516]766 break;
[424cd43]767 case CONSOLE_GET_SIZE:
[7c014d1]768 async_answer_2(callid, EOK, cons->cols, cons->rows);
[a9bd960c]769 break;
[50cfa6c]770 case CONSOLE_GET_COLOR_CAP:
[7c014d1]771 async_answer_1(callid, EOK, cons->ccaps);
[50cfa6c]772 break;
[a9bd960c]773 case CONSOLE_SET_STYLE:
[7c014d1]774 cons_set_style(cons, IPC_GET_ARG1(call));
775 async_answer_0(callid, EOK);
[9805cde]776 break;
777 case CONSOLE_SET_COLOR:
[7c014d1]778 cons_set_color(cons, IPC_GET_ARG1(call), IPC_GET_ARG2(call),
779 IPC_GET_ARG3(call));
780 async_answer_0(callid, EOK);
[9805cde]781 break;
782 case CONSOLE_SET_RGB_COLOR:
[7c014d1]783 cons_set_rgb_color(cons, IPC_GET_ARG1(call), IPC_GET_ARG2(call));
784 async_answer_0(callid, EOK);
[0c6984e]785 break;
[a8b2b5b2]786 case CONSOLE_CURSOR_VISIBILITY:
[7c014d1]787 cons_set_cursor_visibility(cons, IPC_GET_ARG1(call));
788 async_answer_0(callid, EOK);
[a8b2b5b2]789 break;
[424cd43]790 case CONSOLE_GET_EVENT:
791 cons_get_event(cons, callid, &call);
[7c014d1]792 break;
793 default:
794 async_answer_0(callid, EINVAL);
[eaf34f7]795 }
796 }
797}
798
[7c014d1]799static async_sess_t *input_connect(const char *svc)
[eaf34f7]800{
[a40dea3]801 async_sess_t *sess;
[7c014d1]802 service_id_t dsid;
[9f1362d4]803
[7c014d1]804 int rc = loc_service_get_id(svc, &dsid, 0);
[79ae36dd]805 if (rc == EOK) {
[7c014d1]806 sess = loc_service_connect(EXCHANGE_ATOMIC, dsid, 0);
[a40dea3]807 if (sess == NULL) {
[7c014d1]808 printf("%s: Unable to connect to input service %s\n", NAME,
809 svc);
[a40dea3]810 return NULL;
[79ae36dd]811 }
[7c014d1]812 } else
[a40dea3]813 return NULL;
[9f1362d4]814
[7c014d1]815 async_exch_t *exch = async_exchange_begin(sess);
[3015f4d]816 rc = async_connect_to_me(exch, 0, 0, 0, input_events, NULL);
[a40dea3]817 async_exchange_end(exch);
[7c014d1]818
[700af62]819 if (rc != EOK) {
[a40dea3]820 async_hangup(sess);
[7c014d1]821 printf("%s: Unable to create callback connection to service %s (%s)\n",
822 NAME, svc, str_error(rc));
[a40dea3]823 return NULL;
[4904de8]824 }
[9f1362d4]825
[a40dea3]826 return sess;
[700af62]827}
828
[7c014d1]829static void interrupt_received(ipc_callid_t callid, ipc_call_t *call)
830{
831 cons_switch(prev_console);
832}
833
834static async_sess_t *fb_connect(const char *svc)
[700af62]835{
[7c014d1]836 async_sess_t *sess;
837 service_id_t dsid;
838
839 int rc = loc_service_get_id(svc, &dsid, 0);
840 if (rc == EOK) {
841 sess = loc_service_connect(EXCHANGE_SERIALIZE, dsid, 0);
842 if (sess == NULL) {
843 printf("%s: Unable to connect to framebuffer 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 *fb_svc)
854{
855 /* Avoid double initialization */
856 if (graphics_state != GRAPHICS_NONE)
857 return false;
858
859 /* Connect to input service */
860 input_sess = input_connect(input_svc);
[a40dea3]861 if (input_sess == NULL)
[700af62]862 return false;
[79ae36dd]863
[7c014d1]864 /* Connect to framebuffer service */
865 fb_sess = fb_connect(fb_svc);
866 if (fb_sess == NULL)
[79ae36dd]867 return false;
[9f1362d4]868
[15f3c3f]869 /* Register server */
[f302586]870 async_set_client_connection(client_connection);
871 int rc = loc_server_register(NAME);
[2f90b46]872 if (rc != EOK) {
[7c014d1]873 printf("%s: Unable to register server (%s)\n", NAME,
874 str_error(rc));
[424cd43]875 return false;
876 }
[516ff92]877
[7c014d1]878 fb_get_resolution(fb_sess, &xres, &yres);
[3993b3d]879
[7c014d1]880 /* Initialize the screen */
881 screen_vp = fb_vp_create(fb_sess, 0, 0, xres, yres);
[1601f3c]882
[7c014d1]883 if ((xres >= 800) && (yres >= 600)) {
884 logo_vp = fb_vp_create(fb_sess, xres - 66, 2, 64, 60);
885 logo_img = imgmap_decode_tga((void *) helenos_tga,
886 helenos_tga_size, IMGMAP_FLAG_SHARED);
887 logo_handle = fb_imagemap_create(fb_sess, logo_img);
888
889 nameic_vp = fb_vp_create(fb_sess, 5, 17, 100, 26);
890 nameic_img = imgmap_decode_tga((void *) nameic_tga,
891 nameic_tga_size, IMGMAP_FLAG_SHARED);
892 nameic_handle = fb_imagemap_create(fb_sess, nameic_img);
893
894 cons_data_img = imgmap_decode_tga((void *) cons_data_tga,
895 cons_data_tga_size, IMGMAP_FLAG_SHARED);
896 cons_dis_img = imgmap_decode_tga((void *) cons_dis_tga,
897 cons_dis_tga_size, IMGMAP_FLAG_SHARED);
898 cons_dis_sel_img = imgmap_decode_tga((void *) cons_dis_sel_tga,
899 cons_dis_sel_tga_size, IMGMAP_FLAG_SHARED);
900 cons_idle_img = imgmap_decode_tga((void *) cons_idle_tga,
901 cons_idle_tga_size, IMGMAP_FLAG_SHARED);
902 cons_kernel_img = imgmap_decode_tga((void *) cons_kernel_tga,
903 cons_kernel_tga_size, IMGMAP_FLAG_SHARED);
904 cons_sel_img = imgmap_decode_tga((void *) cons_sel_tga,
905 cons_sel_tga_size, IMGMAP_FLAG_SHARED);
906
907 state_icons[CONS_DISCONNECTED] =
908 fb_imagemap_create(fb_sess, cons_dis_img);
909 state_icons[CONS_DISCONNECTED_SELECTED] =
910 fb_imagemap_create(fb_sess, cons_dis_sel_img);
911 state_icons[CONS_SELECTED] =
912 fb_imagemap_create(fb_sess, cons_sel_img);
913 state_icons[CONS_IDLE] =
914 fb_imagemap_create(fb_sess, cons_idle_img);
915 state_icons[CONS_DATA] =
916 fb_imagemap_create(fb_sess, cons_data_img);
917 state_icons[CONS_KERNEL] =
918 fb_imagemap_create(fb_sess, cons_kernel_img);
919
920 anim_1_img = imgmap_decode_tga((void *) anim_1_tga,
921 anim_1_tga_size, IMGMAP_FLAG_SHARED);
922 anim_2_img = imgmap_decode_tga((void *) anim_2_tga,
923 anim_2_tga_size, IMGMAP_FLAG_SHARED);
924 anim_3_img = imgmap_decode_tga((void *) anim_3_tga,
925 anim_3_tga_size, IMGMAP_FLAG_SHARED);
926 anim_4_img = imgmap_decode_tga((void *) anim_4_tga,
927 anim_4_tga_size, IMGMAP_FLAG_SHARED);
928
929 anim_1 = fb_imagemap_create(fb_sess, anim_1_img);
930 anim_2 = fb_imagemap_create(fb_sess, anim_2_img);
931 anim_3 = fb_imagemap_create(fb_sess, anim_3_img);
932 anim_4 = fb_imagemap_create(fb_sess, anim_4_img);
933
934 anim_seq = fb_sequence_create(fb_sess);
935 fb_sequence_add_imagemap(fb_sess, anim_seq, anim_1);
936 fb_sequence_add_imagemap(fb_sess, anim_seq, anim_2);
937 fb_sequence_add_imagemap(fb_sess, anim_seq, anim_3);
938 fb_sequence_add_imagemap(fb_sess, anim_seq, anim_4);
939
940 console_vp = fb_vp_create(fb_sess, CONSOLE_MARGIN, CONSOLE_TOP,
941 xres - 2 * CONSOLE_MARGIN, yres - (CONSOLE_TOP + CONSOLE_MARGIN));
942
943 fb_vp_clear(fb_sess, screen_vp);
944 fb_vp_imagemap_damage(fb_sess, logo_vp, logo_handle,
945 0, 0, 64, 60);
946 fb_vp_imagemap_damage(fb_sess, nameic_vp, nameic_handle,
947 0, 0, 100, 26);
948
949 graphics_state = GRAPHICS_FULL;
950 } else {
951 console_vp = screen_vp;
952 graphics_state = GRAPHICS_BASIC;
953 }
[1601f3c]954
[7c014d1]955 fb_vp_set_style(fb_sess, console_vp, STYLE_NORMAL);
956 fb_vp_clear(fb_sess, console_vp);
[1601f3c]957
[7c014d1]958 sysarg_t cols;
959 sysarg_t rows;
960 fb_vp_get_dimensions(fb_sess, console_vp, &cols, &rows);
961
962 console_caps_t ccaps;
963 fb_vp_get_caps(fb_sess, console_vp, &ccaps);
[3ad953c]964
[b7c33b0]965 mouse.x = xres / 2;
966 mouse.y = yres / 2;
[7c014d1]967 mouse.pressed = false;
[3ad953c]968
[424cd43]969 /* Inititalize consoles */
[7c014d1]970 for (size_t i = 0; i < CONSOLE_COUNT; i++) {
971 consoles[i].index = i;
972 atomic_set(&consoles[i].refcnt, 0);
973 fibril_mutex_initialize(&consoles[i].mtx);
[d03da17]974 prodcons_initialize(&consoles[i].input_pc);
[a58bc8b]975 consoles[i].char_remains_len = 0;
[7c014d1]976
977 if (graphics_state == GRAPHICS_FULL) {
978 /* Create state buttons */
979 consoles[i].state_vp =
980 fb_vp_create(fb_sess, STATE_START + (xres - 800) / 2 +
981 CONSOLE_MARGIN + i * (STATE_WIDTH + STATE_SPACE),
982 STATE_TOP, STATE_WIDTH, STATE_HEIGHT);
983 }
984
985 if (i == KERNEL_CONSOLE) {
986 consoles[i].state = CONS_KERNEL;
987 cons_redraw_state(&consoles[i]);
988 cons_kernel_sequence_start(&consoles[i]);
989 continue;
990 }
991
992 if (i == 0)
993 consoles[i].state = CONS_DISCONNECTED_SELECTED;
994 else
995 consoles[i].state = CONS_DISCONNECTED;
996
997 consoles[i].cols = cols;
998 consoles[i].rows = rows;
999 consoles[i].ccaps = ccaps;
1000 consoles[i].frontbuf =
1001 screenbuffer_create(cols, rows, SCREENBUFFER_FLAG_SHARED);
1002
1003 if (consoles[i].frontbuf == NULL) {
1004 printf("%s: Unable to allocate frontbuffer %zu\n", NAME, i);
1005 return false;
1006 }
1007
1008 consoles[i].fbid = fb_frontbuf_create(fb_sess, consoles[i].frontbuf);
1009 if (consoles[i].fbid == 0) {
1010 printf("%s: Unable to create frontbuffer %zu\n", NAME, i);
1011 return false;
1012 }
1013
1014 cons_redraw_state(&consoles[i]);
1015
1016 char vc[LOC_NAME_MAXLEN + 1];
1017 snprintf(vc, LOC_NAME_MAXLEN, "%s/vc%zu", NAMESPACE, i);
1018
1019 if (loc_service_register(vc, &consoles[i].dsid) != EOK) {
1020 printf("%s: Unable to register device %s\n", NAME, vc);
1021 return false;
[424cd43]1022 }
1023 }
1024
[3ad953c]1025 /* Receive kernel notifications */
[007e6efa]1026 async_set_interrupt_received(interrupt_received);
[7c014d1]1027 rc = event_subscribe(EVENT_KCONSOLE, 0);
1028 if (rc != EOK)
1029 printf("%s: Failed to register kconsole notifications (%s)\n",
1030 NAME, str_error(rc));
[1601f3c]1031
[424cd43]1032 return true;
1033}
1034
[47a350f]1035static void usage(void)
1036{
[7c014d1]1037 printf("Usage: console <input_dev> <framebuffer_dev>\n");
[47a350f]1038}
1039
[424cd43]1040int main(int argc, char *argv[])
1041{
[7c014d1]1042 if (argc < 3) {
[47a350f]1043 usage();
1044 return -1;
1045 }
1046
[7c014d1]1047 printf("%s: HelenOS Console service\n", NAME);
[424cd43]1048
[7c014d1]1049 if (!console_srv_init(argv[1], argv[2]))
[424cd43]1050 return -1;
[79ae36dd]1051
[7c014d1]1052 printf("%s: Accepting connections\n", NAME);
1053 task_retval(0);
[eaf34f7]1054 async_manager();
[3ad953c]1055
1056 return 0;
[51c1b003]1057}
[516ff92]1058
[ce5bcb4]1059/** @}
1060 */
Note: See TracBrowser for help on using the repository browser.