Index: uspace/srv/hid/console/console.c
===================================================================
--- uspace/srv/hid/console/console.c	(revision 68a552f0cc0e1665ed70383cada86c9355991a73)
+++ uspace/srv/hid/console/console.c	(revision 49aaa0e35a10f53802af5c8cb22cd8f46f1230bb)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2024 Jiri Svoboda
  * Copyright (c) 2011 Martin Decky
  * All rights reserved.
@@ -37,4 +37,5 @@
 #include <stdio.h>
 #include <adt/prodcons.h>
+#include <io/console.h>
 #include <io/input.h>
 #include <ipc/vfs.h>
@@ -63,5 +64,5 @@
 typedef struct {
 	atomic_flag refcnt;      /**< Connection reference count */
-	prodcons_t input_pc;  /**< Incoming keyboard events */
+	prodcons_t input_pc;  /**< Incoming console events */
 
 	/**
@@ -89,4 +90,6 @@
 } console_t;
 
+static loc_srv_t *console_srv;
+
 /** Input server proxy */
 static input_t *input;
@@ -100,4 +103,14 @@
 static sysarg_t rows;
 
+/** Mouse pointer X coordinate */
+static int pointer_x;
+/** Mouse pointer Y coordinate */
+static int pointer_y;
+/** Character under mouse cursor */
+static charfield_t pointer_bg;
+
+static int mouse_scale_x = 4;
+static int mouse_scale_y = 8;
+
 /** Array of data for virtual consoles */
 static console_t consoles[CONSOLE_COUNT];
@@ -110,8 +123,11 @@
 static errno_t input_ev_active(input_t *);
 static errno_t input_ev_deactive(input_t *);
-static errno_t input_ev_key(input_t *, kbd_event_type_t, keycode_t, keymod_t, char32_t);
-static errno_t input_ev_move(input_t *, int, int);
-static errno_t input_ev_abs_move(input_t *, unsigned, unsigned, unsigned, unsigned);
-static errno_t input_ev_button(input_t *, int, int);
+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 = {
@@ -121,5 +137,6 @@
 	.move = input_ev_move,
 	.abs_move = input_ev_abs_move,
-	.button = input_ev_button
+	.button = input_ev_button,
+	.dclick = input_ev_dclick
 };
 
@@ -139,4 +156,5 @@
 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 **);
@@ -160,4 +178,5 @@
 	.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,
@@ -165,4 +184,7 @@
 	.update = cons_buf_update
 };
+
+static void pointer_draw(void);
+static void pointer_undraw(void);
 
 static console_t *srv_to_console(con_srv_t *srv)
@@ -231,4 +253,5 @@
 
 	fibril_mutex_lock(&switch_mtx);
+	pointer_undraw();
 
 	if (cons == active_console) {
@@ -239,7 +262,85 @@
 	active_console = cons;
 
+	pointer_draw();
 	fibril_mutex_unlock(&switch_mtx);
 
 	cons_damage(cons);
+}
+
+/** Draw mouse pointer. */
+static void pointer_draw(void)
+{
+	charfield_t *ch;
+	int col, row;
+
+	/* Downscale coordinates to text resolution */
+	col = pointer_x / mouse_scale_x;
+	row = pointer_y / mouse_scale_y;
+
+	/* Make sure they are in range */
+	if (col < 0 || row < 0 || col >= (int)cols || row >= (int)rows)
+		return;
+
+	ch = chargrid_charfield_at(active_console->frontbuf, col, row);
+
+	/*
+	 * Store background attributes for undrawing the pointer.
+	 * This is necessary as styles cannot be inverted with
+	 * round trip (unlike RGB or INDEX)
+	 */
+	pointer_bg = *ch;
+
+	/* In general the color should be a one's complement of the background */
+	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) {
+		/* Don't have a proper inverse for each style */
+		if (ch->attrs.val.style == STYLE_INVERTED)
+			ch->attrs.val.style = STYLE_NORMAL;
+		else
+			ch->attrs.val.style = STYLE_INVERTED;
+	}
+
+	/* Make sure the cell gets updated */
+	ch->flags |= CHAR_FLAG_DIRTY;
+}
+
+/** Undraw mouse pointer. */
+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;
+}
+
+/** Queue console event.
+ *
+ * @param cons Console
+ * @param ev Console event
+ */
+static void console_queue_cons_event(console_t *cons, cons_event_t *ev)
+{
+	/* Got key press/release event */
+	cons_event_t *event =
+	    (cons_event_t *) malloc(sizeof(cons_event_t));
+	if (event == NULL)
+		return;
+
+	*event = *ev;
+	link_initialize(&event->link);
+
+	prodcons_produce(&cons->input_pc, &event->link);
 }
 
@@ -261,44 +362,116 @@
 }
 
-static errno_t input_ev_key(input_t *input, kbd_event_type_t type, keycode_t key,
-    keymod_t mods, char32_t c)
-{
+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;
+
+	/* Switch console on Alt+Fn or Shift+Fn */
 	if ((key >= KC_F1) && (key <= KC_F1 + CONSOLE_COUNT) &&
-	    ((mods & KM_CTRL) == 0)) {
+	    (alt || shift)) {
 		cons_switch(key - KC_F1);
 	} else {
 		/* Got key press/release event */
-		kbd_event_t *event =
-		    (kbd_event_t *) malloc(sizeof(kbd_event_t));
-		if (event == NULL) {
-			return ENOMEM;
-		}
-
-		link_initialize(&event->link);
-		event->type = type;
-		event->key = key;
-		event->mods = mods;
-		event->c = c;
-
-		prodcons_produce(&active_console->input_pc,
-		    &event->link);
-	}
-
-	return EOK;
-}
-
-static errno_t input_ev_move(input_t *input, int dx, int dy)
-{
-	return EOK;
-}
-
-static errno_t input_ev_abs_move(input_t *input, unsigned x, unsigned y,
-    unsigned max_x, unsigned max_y)
-{
-	return EOK;
-}
-
-static errno_t input_ev_button(input_t *input, int bnum, int bpress)
-{
+		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;
+}
+
+/** Update pointer position.
+ *
+ * @param new_x New X coordinate (in pixels)
+ * @param new_y New Y coordinate (in pixels)
+ */
+static void pointer_update(int new_x, int new_y)
+{
+	bool upd_pointer;
+
+	/* Make sure coordinates are in range */
+
+	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;
+
+	/* Determine if pointer moved to a different character cell */
+	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();
+
+	/* Store new pointer position */
+	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;
 }
@@ -310,4 +483,5 @@
 
 	fibril_mutex_lock(&cons->mtx);
+	pointer_undraw();
 
 	switch (ch) {
@@ -327,4 +501,5 @@
 	}
 
+	pointer_draw();
 	fibril_mutex_unlock(&cons->mtx);
 
@@ -336,5 +511,7 @@
 {
 	fibril_mutex_lock(&cons->mtx);
+	pointer_undraw();
 	chargrid_set_cursor_visibility(cons->frontbuf, visible);
+	pointer_draw();
 	fibril_mutex_unlock(&cons->mtx);
 
@@ -379,9 +556,11 @@
 		if (pos < size) {
 			link_t *link = prodcons_consume(&cons->input_pc);
-			kbd_event_t *event = list_get_instance(link, kbd_event_t, link);
+			cons_event_t *event = list_get_instance(link,
+			    cons_event_t, link);
 
 			/* Accept key presses of printable chars only. */
-			if ((event->type == KEY_PRESS) && (event->c != 0)) {
-				char32_t tmp[2] = { event->c, 0 };
+			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);
@@ -420,5 +599,7 @@
 
 	fibril_mutex_lock(&cons->mtx);
+	pointer_undraw();
 	chargrid_clear(cons->frontbuf);
+	pointer_draw();
 	fibril_mutex_unlock(&cons->mtx);
 
@@ -431,5 +612,7 @@
 
 	fibril_mutex_lock(&cons->mtx);
+	pointer_undraw();
 	chargrid_set_cursor(cons->frontbuf, col, row);
+	pointer_draw();
 	fibril_mutex_unlock(&cons->mtx);
 
@@ -507,14 +690,21 @@
 }
 
+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);
-	kbd_event_t *kevent = list_get_instance(link, kbd_event_t, link);
-
-	event->type = CEV_KEY;
-	event->ev.key = *kevent;
-
-	free(kevent);
+	cons_event_t *cevent = list_get_instance(link, cons_event_t, link);
+
+	*event = *cevent;
+	free(cevent);
 	return EOK;
 }
@@ -621,4 +811,6 @@
 	/* Update front buffer from user buffer */
 
+	pointer_undraw();
+
 	for (row = r0; row < r1; row++) {
 		for (col = c0; col < c1; col++) {
@@ -628,4 +820,5 @@
 	}
 
+	pointer_draw();
 	fibril_mutex_unlock(&cons->mtx);
 
@@ -718,5 +911,5 @@
 	/* Register server */
 	async_set_fallback_port_handler(client_connection, NULL);
-	rc = loc_server_register(NAME);
+	rc = loc_server_register(NAME, &console_srv);
 	if (rc != EOK) {
 		printf("%s: Unable to register server (%s)\n", NAME,
@@ -768,5 +961,6 @@
 			snprintf(vc, LOC_NAME_MAXLEN, "%s/vc%zu", NAMESPACE, i);
 
-			if (loc_service_register(vc, &consoles[i].dsid) != EOK) {
+			if (loc_service_register(console_srv, vc,
+			    &consoles[i].dsid) != EOK) {
 				printf("%s: Unable to register device %s\n", NAME, vc);
 				return false;
