HelenOS sources
This source file includes following definitions.
- srv_to_console
- cons_update
- cons_update_cursor
- cons_damage
- cons_switch
- pointer_draw
- pointer_undraw
- console_queue_cons_event
- input_ev_active
- input_ev_deactive
- input_ev_key
- pointer_update
- input_ev_move
- input_ev_abs_move
- input_ev_button
- input_ev_dclick
- cons_write_char
- cons_set_cursor_vis
- cons_open
- cons_close
- cons_read
- cons_write
- cons_sync
- cons_clear
- cons_set_pos
- cons_get_pos
- cons_get_size
- cons_get_color_cap
- cons_set_style
- cons_set_color
- cons_set_rgb_color
- cons_set_cursor_visibility
- cons_set_caption
- cons_get_event
- cons_map
- cons_unmap
- cons_buf_update
- client_connection
- input_connect
- output_connect
- console_srv_init
- usage
- main
#include <async.h>
#include <stdio.h>
#include <adt/prodcons.h>
#include <io/console.h>
#include <io/input.h>
#include <ipc/vfs.h>
#include <errno.h>
#include <str_error.h>
#include <loc.h>
#include <io/con_srv.h>
#include <io/kbd_event.h>
#include <io/keycode.h>
#include <io/chargrid.h>
#include <io/output.h>
#include <align.h>
#include <as.h>
#include <task.h>
#include <fibril_synch.h>
#include <stdatomic.h>
#include <stdlib.h>
#include <str.h>
#include "console.h"
#define NAME "console"
#define NAMESPACE "term"
#define UTF8_CHAR_BUFFER_SIZE (STR_BOUNDS(1) + 1)
typedef struct {
atomic_flag refcnt;
prodcons_t input_pc;
char char_remains[UTF8_CHAR_BUFFER_SIZE];
size_t char_remains_len;
fibril_mutex_t mtx;
size_t index;
service_id_t dsid;
sysarg_t cols;
sysarg_t rows;
console_caps_t ccaps;
sysarg_t ucols;
sysarg_t urows;
charfield_t *ubuf;
chargrid_t *frontbuf;
frontbuf_handle_t fbid;
con_srvs_t srvs;
} console_t;
static loc_srv_t *console_srv;
static input_t *input;
static bool active = false;
static async_sess_t *output_sess;
static sysarg_t cols;
static sysarg_t rows;
static int pointer_x;
static int pointer_y;
static charfield_t pointer_bg;
static int mouse_scale_x = 4;
static int mouse_scale_y = 8;
static console_t consoles[CONSOLE_COUNT];
static FIBRIL_MUTEX_INITIALIZE(switch_mtx);
static console_t *active_console = &consoles[0];
static errno_t input_ev_active(input_t *);
static errno_t input_ev_deactive(input_t *);
static errno_t input_ev_key(input_t *, unsigned, kbd_event_type_t, keycode_t,
keymod_t, char32_t);
static errno_t input_ev_move(input_t *, unsigned, int, int);
static errno_t input_ev_abs_move(input_t *, unsigned, unsigned, unsigned,
unsigned, unsigned);
static errno_t input_ev_button(input_t *, unsigned, int, int);
static errno_t input_ev_dclick(input_t *, unsigned, int);
static input_ev_ops_t input_ev_ops = {
.active = input_ev_active,
.deactive = input_ev_deactive,
.key = input_ev_key,
.move = input_ev_move,
.abs_move = input_ev_abs_move,
.button = input_ev_button,
.dclick = input_ev_dclick
};
static errno_t cons_open(con_srvs_t *, con_srv_t *);
static errno_t cons_close(con_srv_t *);
static errno_t cons_read(con_srv_t *, void *, size_t, size_t *);
static errno_t cons_write(con_srv_t *, void *, size_t, size_t *);
static void cons_sync(con_srv_t *);
static void cons_clear(con_srv_t *);
static void cons_set_pos(con_srv_t *, sysarg_t col, sysarg_t row);
static errno_t cons_get_pos(con_srv_t *, sysarg_t *, sysarg_t *);
static errno_t cons_get_size(con_srv_t *, sysarg_t *, sysarg_t *);
static errno_t cons_get_color_cap(con_srv_t *, console_caps_t *);
static void cons_set_style(con_srv_t *, console_style_t);
static void cons_set_color(con_srv_t *, console_color_t, console_color_t,
console_color_attr_t);
static void cons_set_rgb_color(con_srv_t *, pixel_t, pixel_t);
static void cons_set_cursor_visibility(con_srv_t *, bool);
static errno_t cons_set_caption(con_srv_t *, const char *);
static errno_t cons_get_event(con_srv_t *, cons_event_t *);
static errno_t cons_map(con_srv_t *, sysarg_t, sysarg_t, charfield_t **);
static void cons_unmap(con_srv_t *);
static void cons_buf_update(con_srv_t *, sysarg_t, sysarg_t, sysarg_t,
sysarg_t);
static con_ops_t con_ops = {
.open = cons_open,
.close = cons_close,
.read = cons_read,
.write = cons_write,
.sync = cons_sync,
.clear = cons_clear,
.set_pos = cons_set_pos,
.get_pos = cons_get_pos,
.get_size = cons_get_size,
.get_color_cap = cons_get_color_cap,
.set_style = cons_set_style,
.set_color = cons_set_color,
.set_rgb_color = cons_set_rgb_color,
.set_cursor_visibility = cons_set_cursor_visibility,
.set_caption = cons_set_caption,
.get_event = cons_get_event,
.map = cons_map,
.unmap = cons_unmap,
.update = cons_buf_update
};
static void pointer_draw(void);
static void pointer_undraw(void);
static console_t *srv_to_console(con_srv_t *srv)
{
return srv->srvs->sarg;
}
static void cons_update(console_t *cons)
{
fibril_mutex_lock(&switch_mtx);
fibril_mutex_lock(&cons->mtx);
if ((active) && (cons == active_console)) {
output_update(output_sess, cons->fbid);
output_cursor_update(output_sess, cons->fbid);
}
fibril_mutex_unlock(&cons->mtx);
fibril_mutex_unlock(&switch_mtx);
}
static void cons_update_cursor(console_t *cons)
{
fibril_mutex_lock(&switch_mtx);
fibril_mutex_lock(&cons->mtx);
if ((active) && (cons == active_console))
output_cursor_update(output_sess, cons->fbid);
fibril_mutex_unlock(&cons->mtx);
fibril_mutex_unlock(&switch_mtx);
}
static void cons_damage(console_t *cons)
{
fibril_mutex_lock(&switch_mtx);
fibril_mutex_lock(&cons->mtx);
if ((active) && (cons == active_console)) {
output_damage(output_sess, cons->fbid, 0, 0, cons->cols,
cons->rows);
output_cursor_update(output_sess, cons->fbid);
}
fibril_mutex_unlock(&cons->mtx);
fibril_mutex_unlock(&switch_mtx);
}
static void cons_switch(unsigned int index)
{
if (index == CONSOLE_COUNT) {
if (console_kcon())
active = false;
return;
}
if (index > CONSOLE_COUNT)
return;
console_t *cons = &consoles[index];
fibril_mutex_lock(&switch_mtx);
pointer_undraw();
if (cons == active_console) {
fibril_mutex_unlock(&switch_mtx);
return;
}
active_console = cons;
pointer_draw();
fibril_mutex_unlock(&switch_mtx);
cons_damage(cons);
}
static void pointer_draw(void)
{
charfield_t *ch;
int col, row;
col = pointer_x / mouse_scale_x;
row = pointer_y / mouse_scale_y;
if (col < 0 || row < 0 || col >= (int)cols || row >= (int)rows)
return;
ch = chargrid_charfield_at(active_console->frontbuf, col, row);
pointer_bg = *ch;
if (ch->attrs.type == CHAR_ATTR_INDEX) {
ch->attrs.val.index.bgcolor ^= 0xf;
ch->attrs.val.index.fgcolor ^= 0xf;
} else if (ch->attrs.type == CHAR_ATTR_RGB) {
ch->attrs.val.rgb.fgcolor ^= 0xffffff;
ch->attrs.val.rgb.bgcolor ^= 0xffffff;
} else if (ch->attrs.type == CHAR_ATTR_STYLE) {
if (ch->attrs.val.style == STYLE_INVERTED)
ch->attrs.val.style = STYLE_NORMAL;
else
ch->attrs.val.style = STYLE_INVERTED;
}
ch->flags |= CHAR_FLAG_DIRTY;
}
static void pointer_undraw(void)
{
charfield_t *ch;
int col, row;
col = pointer_x / mouse_scale_x;
row = pointer_y / mouse_scale_y;
if (col < 0 || row < 0 || col >= (int)cols || row >= (int)rows)
return;
ch = chargrid_charfield_at(active_console->frontbuf, col, row);
*ch = pointer_bg;
ch->flags |= CHAR_FLAG_DIRTY;
}
static void console_queue_cons_event(console_t *cons, cons_event_t *ev)
{
cons_qevent_t *event =
(cons_qevent_t *) malloc(sizeof(cons_qevent_t));
if (event == NULL)
return;
event->ev = *ev;
link_initialize(&event->link);
prodcons_produce(&cons->input_pc, &event->link);
}
static errno_t input_ev_active(input_t *input)
{
active = true;
output_claim(output_sess);
cons_damage(active_console);
return EOK;
}
static errno_t input_ev_deactive(input_t *input)
{
active = false;
output_yield(output_sess);
return EOK;
}
static errno_t input_ev_key(input_t *input, unsigned kbd_id,
kbd_event_type_t type, keycode_t key, keymod_t mods, char32_t c)
{
cons_event_t event;
bool alt;
bool shift;
alt = (mods & KM_ALT) != 0 && (mods & (KM_CTRL | KM_SHIFT)) == 0;
shift = (mods & KM_SHIFT) != 0 && (mods & (KM_CTRL | KM_ALT)) == 0;
if ((key >= KC_F1) && (key <= KC_F1 + CONSOLE_COUNT) &&
(alt || shift)) {
cons_switch(key - KC_F1);
} else {
event.type = CEV_KEY;
(void)kbd_id;
event.ev.key.type = type;
event.ev.key.key = key;
event.ev.key.mods = mods;
event.ev.key.c = c;
console_queue_cons_event(active_console, &event);
}
return EOK;
}
static void pointer_update(int new_x, int new_y)
{
bool upd_pointer;
if (new_x < 0)
new_x = 0;
if (new_x >= (int)cols * mouse_scale_x)
new_x = cols * mouse_scale_x - 1;
if (new_y < 0)
new_y = 0;
if (new_y >= (int)rows * mouse_scale_y)
new_y = rows * mouse_scale_y - 1;
upd_pointer = (new_x / mouse_scale_x != pointer_x / mouse_scale_x) ||
(new_y / mouse_scale_y != pointer_y / mouse_scale_y);
if (upd_pointer)
pointer_undraw();
pointer_x = new_x;
pointer_y = new_y;
if (upd_pointer) {
pointer_draw();
cons_update(active_console);
}
}
static errno_t input_ev_move(input_t *input, unsigned pos_id, int dx, int dy)
{
(void) pos_id;
pointer_update(pointer_x + dx, pointer_y + dy);
return EOK;
}
static errno_t input_ev_abs_move(input_t *input, unsigned pos_id, unsigned x,
unsigned y, unsigned max_x, unsigned max_y)
{
(void)pos_id;
pointer_update(mouse_scale_x * cols * x / max_x, mouse_scale_y * rows * y / max_y);
return EOK;
}
static errno_t input_ev_button(input_t *input, unsigned pos_id, int bnum,
int bpress)
{
cons_event_t event;
(void)pos_id;
event.type = CEV_POS;
event.ev.pos.type = bpress ? POS_PRESS : POS_RELEASE;
event.ev.pos.btn_num = bnum;
event.ev.pos.hpos = pointer_x / mouse_scale_x;
event.ev.pos.vpos = pointer_y / mouse_scale_y;
console_queue_cons_event(active_console, &event);
return EOK;
}
static errno_t input_ev_dclick(input_t *input, unsigned pos_id, int bnum)
{
cons_event_t event;
(void)pos_id;
event.type = CEV_POS;
event.ev.pos.type = POS_DCLICK;
event.ev.pos.btn_num = bnum;
event.ev.pos.hpos = pointer_x / mouse_scale_x;
event.ev.pos.vpos = pointer_y / mouse_scale_y;
console_queue_cons_event(active_console, &event);
return EOK;
}
static void cons_write_char(console_t *cons, char32_t ch)
{
sysarg_t updated = 0;
fibril_mutex_lock(&cons->mtx);
pointer_undraw();
switch (ch) {
case '\n':
updated = chargrid_newline(cons->frontbuf);
break;
case '\r':
break;
case '\t':
updated = chargrid_tabstop(cons->frontbuf, 8);
break;
case '\b':
updated = chargrid_backspace(cons->frontbuf);
break;
default:
updated = chargrid_putuchar(cons->frontbuf, ch, true);
}
pointer_draw();
fibril_mutex_unlock(&cons->mtx);
if (updated > 1)
cons_update(cons);
}
static void cons_set_cursor_vis(console_t *cons, bool visible)
{
fibril_mutex_lock(&cons->mtx);
pointer_undraw();
chargrid_set_cursor_visibility(cons->frontbuf, visible);
pointer_draw();
fibril_mutex_unlock(&cons->mtx);
cons_update_cursor(cons);
}
static errno_t cons_open(con_srvs_t *srvs, con_srv_t *srv)
{
return EOK;
}
static errno_t cons_close(con_srv_t *srv)
{
return EOK;
}
static errno_t cons_read(con_srv_t *srv, void *buf, size_t size, size_t *nread)
{
uint8_t *bbuf = buf;
console_t *cons = srv_to_console(srv);
size_t pos = 0;
while (pos < size) {
while ((pos < size) && (cons->char_remains_len > 0)) {
bbuf[pos] = cons->char_remains[0];
pos++;
for (size_t i = 1; i < cons->char_remains_len; i++)
cons->char_remains[i - 1] = cons->char_remains[i];
cons->char_remains_len--;
}
if (pos < size) {
link_t *link = prodcons_consume(&cons->input_pc);
cons_qevent_t *qevent = list_get_instance(link,
cons_qevent_t, link);
cons_event_t *event = &qevent->ev;
if (event->type == CEV_KEY && event->ev.key.type == KEY_PRESS &&
(event->ev.key.c != 0)) {
char32_t tmp[2] = { event->ev.key.c, 0 };
wstr_to_str(cons->char_remains, UTF8_CHAR_BUFFER_SIZE, tmp);
cons->char_remains_len = str_size(cons->char_remains);
}
free(qevent);
}
}
*nread = size;
return EOK;
}
static errno_t cons_write(con_srv_t *srv, void *data, size_t size, size_t *nwritten)
{
console_t *cons = srv_to_console(srv);
size_t off = 0;
while (off < size)
cons_write_char(cons, str_decode(data, &off, size));
*nwritten = size;
return EOK;
}
static void cons_sync(con_srv_t *srv)
{
console_t *cons = srv_to_console(srv);
cons_update(cons);
}
static void cons_clear(con_srv_t *srv)
{
console_t *cons = srv_to_console(srv);
fibril_mutex_lock(&cons->mtx);
pointer_undraw();
chargrid_clear(cons->frontbuf);
pointer_draw();
fibril_mutex_unlock(&cons->mtx);
cons_update(cons);
}
static void cons_set_pos(con_srv_t *srv, sysarg_t col, sysarg_t row)
{
console_t *cons = srv_to_console(srv);
fibril_mutex_lock(&cons->mtx);
pointer_undraw();
chargrid_set_cursor(cons->frontbuf, col, row);
pointer_draw();
fibril_mutex_unlock(&cons->mtx);
cons_update_cursor(cons);
}
static errno_t cons_get_pos(con_srv_t *srv, sysarg_t *col, sysarg_t *row)
{
console_t *cons = srv_to_console(srv);
fibril_mutex_lock(&cons->mtx);
chargrid_get_cursor(cons->frontbuf, col, row);
fibril_mutex_unlock(&cons->mtx);
return EOK;
}
static errno_t cons_get_size(con_srv_t *srv, sysarg_t *cols, sysarg_t *rows)
{
console_t *cons = srv_to_console(srv);
fibril_mutex_lock(&cons->mtx);
*cols = cons->cols;
*rows = cons->rows;
fibril_mutex_unlock(&cons->mtx);
return EOK;
}
static errno_t cons_get_color_cap(con_srv_t *srv, console_caps_t *ccaps)
{
console_t *cons = srv_to_console(srv);
fibril_mutex_lock(&cons->mtx);
*ccaps = cons->ccaps;
fibril_mutex_unlock(&cons->mtx);
return EOK;
}
static void cons_set_style(con_srv_t *srv, console_style_t style)
{
console_t *cons = srv_to_console(srv);
fibril_mutex_lock(&cons->mtx);
chargrid_set_style(cons->frontbuf, style);
fibril_mutex_unlock(&cons->mtx);
}
static void cons_set_color(con_srv_t *srv, console_color_t bgcolor,
console_color_t fgcolor, console_color_attr_t attr)
{
console_t *cons = srv_to_console(srv);
fibril_mutex_lock(&cons->mtx);
chargrid_set_color(cons->frontbuf, bgcolor, fgcolor, attr);
fibril_mutex_unlock(&cons->mtx);
}
static void cons_set_rgb_color(con_srv_t *srv, pixel_t bgcolor,
pixel_t fgcolor)
{
console_t *cons = srv_to_console(srv);
fibril_mutex_lock(&cons->mtx);
chargrid_set_rgb_color(cons->frontbuf, bgcolor, fgcolor);
fibril_mutex_unlock(&cons->mtx);
}
static void cons_set_cursor_visibility(con_srv_t *srv, bool visible)
{
console_t *cons = srv_to_console(srv);
cons_set_cursor_vis(cons, visible);
}
static errno_t cons_set_caption(con_srv_t *srv, const char *caption)
{
console_t *cons = srv_to_console(srv);
(void) cons;
(void) caption;
return EOK;
}
static errno_t cons_get_event(con_srv_t *srv, cons_event_t *event)
{
console_t *cons = srv_to_console(srv);
link_t *link = prodcons_consume(&cons->input_pc);
cons_qevent_t *qevent = list_get_instance(link, cons_qevent_t, link);
*event = qevent->ev;
free(qevent);
return EOK;
}
static errno_t cons_map(con_srv_t *srv, sysarg_t cols, sysarg_t rows,
charfield_t **rbuf)
{
console_t *cons = srv_to_console(srv);
void *buf;
fibril_mutex_lock(&cons->mtx);
if (cons->ubuf != NULL) {
fibril_mutex_unlock(&cons->mtx);
return EBUSY;
}
buf = as_area_create(AS_AREA_ANY, cols * rows * sizeof(charfield_t),
AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE, AS_AREA_UNPAGED);
if (buf == AS_MAP_FAILED) {
fibril_mutex_unlock(&cons->mtx);
return ENOMEM;
}
cons->ucols = cols;
cons->urows = rows;
cons->ubuf = buf;
fibril_mutex_unlock(&cons->mtx);
*rbuf = buf;
return EOK;
}
static void cons_unmap(con_srv_t *srv)
{
console_t *cons = srv_to_console(srv);
void *buf;
fibril_mutex_lock(&cons->mtx);
buf = cons->ubuf;
cons->ubuf = NULL;
if (buf != NULL)
as_area_destroy(buf);
fibril_mutex_unlock(&cons->mtx);
}
static void cons_buf_update(con_srv_t *srv, sysarg_t c0, sysarg_t r0,
sysarg_t c1, sysarg_t r1)
{
console_t *cons = srv_to_console(srv);
charfield_t *ch;
sysarg_t col, row;
fibril_mutex_lock(&cons->mtx);
if (cons->ubuf == NULL) {
fibril_mutex_unlock(&cons->mtx);
return;
}
if (c1 > cons->ucols)
c1 = cons->ucols;
if (c1 > cons->cols)
c1 = cons->cols;
if (c0 >= c1) {
fibril_mutex_unlock(&cons->mtx);
return;
}
if (r1 > cons->urows)
r1 = cons->urows;
if (r1 > cons->rows)
r1 = cons->rows;
if (r0 >= r1) {
fibril_mutex_unlock(&cons->mtx);
return;
}
pointer_undraw();
for (row = r0; row < r1; row++) {
for (col = c0; col < c1; col++) {
ch = chargrid_charfield_at(cons->frontbuf, col, row);
*ch = cons->ubuf[row * cons->ucols + col];
}
}
pointer_draw();
fibril_mutex_unlock(&cons->mtx);
cons_update(cons);
}
static void client_connection(ipc_call_t *icall, void *arg)
{
console_t *cons = NULL;
for (size_t i = 0; i < CONSOLE_COUNT; i++) {
if (consoles[i].dsid == (service_id_t) ipc_get_arg2(icall)) {
cons = &consoles[i];
break;
}
}
if (cons == NULL) {
async_answer_0(icall, ENOENT);
return;
}
if (!atomic_flag_test_and_set(&cons->refcnt))
cons_set_cursor_vis(cons, true);
con_conn(icall, &cons->srvs);
}
static errno_t input_connect(const char *svc)
{
async_sess_t *sess;
service_id_t dsid;
errno_t rc = loc_service_get_id(svc, &dsid, 0);
if (rc != EOK) {
printf("%s: Input service %s not found\n", NAME, svc);
return rc;
}
sess = loc_service_connect(dsid, INTERFACE_INPUT, 0);
if (sess == NULL) {
printf("%s: Unable to connect to input service %s\n", NAME,
svc);
return EIO;
}
rc = input_open(sess, &input_ev_ops, NULL, &input);
if (rc != EOK) {
async_hangup(sess);
printf("%s: Unable to communicate with service %s (%s)\n",
NAME, svc, str_error(rc));
return rc;
}
return EOK;
}
static async_sess_t *output_connect(const char *svc)
{
async_sess_t *sess;
service_id_t dsid;
errno_t rc = loc_service_get_id(svc, &dsid, 0);
if (rc == EOK) {
sess = loc_service_connect(dsid, INTERFACE_OUTPUT, 0);
if (sess == NULL) {
printf("%s: Unable to connect to output service %s\n",
NAME, svc);
return NULL;
}
} else
return NULL;
return sess;
}
static bool console_srv_init(char *input_svc, char *output_svc)
{
errno_t rc = input_connect(input_svc);
if (rc != EOK)
return false;
output_sess = output_connect(output_svc);
if (output_sess == NULL)
return false;
async_set_fallback_port_handler(client_connection, NULL);
rc = loc_server_register(NAME, &console_srv);
if (rc != EOK) {
printf("%s: Unable to register server (%s)\n", NAME,
str_error(rc));
return false;
}
output_get_dimensions(output_sess, &cols, &rows);
output_set_style(output_sess, STYLE_NORMAL);
console_caps_t ccaps;
output_get_caps(output_sess, &ccaps);
if (ccaps != 0) {
for (size_t i = 0; i < CONSOLE_COUNT; i++) {
consoles[i].index = i;
atomic_flag_clear(&consoles[i].refcnt);
fibril_mutex_initialize(&consoles[i].mtx);
prodcons_initialize(&consoles[i].input_pc);
consoles[i].char_remains_len = 0;
consoles[i].cols = cols;
consoles[i].rows = rows;
consoles[i].ccaps = ccaps;
consoles[i].frontbuf =
chargrid_create(cols, rows, CHARGRID_FLAG_SHARED);
if (consoles[i].frontbuf == NULL) {
printf("%s: Unable to allocate frontbuffer %zu\n", NAME, i);
return false;
}
consoles[i].fbid = output_frontbuf_create(output_sess,
consoles[i].frontbuf);
if (consoles[i].fbid == 0) {
printf("%s: Unable to create frontbuffer %zu\n", NAME, i);
return false;
}
con_srvs_init(&consoles[i].srvs);
consoles[i].srvs.ops = &con_ops;
consoles[i].srvs.sarg = &consoles[i];
char vc[LOC_NAME_MAXLEN + 1];
snprintf(vc, LOC_NAME_MAXLEN, "%s/vc%zu", NAMESPACE, i);
if (loc_service_register(console_srv, vc,
&consoles[i].dsid) != EOK) {
printf("%s: Unable to register device %s\n", NAME, vc);
return false;
}
}
input_activate(input);
active = true;
cons_damage(active_console);
}
return true;
}
static void usage(char *name)
{
printf("Usage: %s <input_dev> <output_dev>\n", name);
}
int main(int argc, char *argv[])
{
if (argc < 3) {
usage(argv[0]);
return -1;
}
printf("%s: HelenOS Console service\n", NAME);
if (!console_srv_init(argv[1], argv[2]))
return -1;
printf("%s: Accepting connections\n", NAME);
task_retval(0);
async_manager();
return 0;
}
HelenOS homepage, sources at GitHub