Index: uspace/srv/hid/remcons/remcons.c
===================================================================
--- uspace/srv/hid/remcons/remcons.c	(revision d31c3ea70b393289736cdac0e91fa5c5eba06c4c)
+++ uspace/srv/hid/remcons/remcons.c	(revision 6907f26c4f1d524ca40bdb6622b8dc0254203a1f)
@@ -34,4 +34,5 @@
  */
 
+#include <adt/prodcons.h>
 #include <as.h>
 #include <async.h>
@@ -124,9 +125,11 @@
 static void remcons_vt_cputs(void *, const char *);
 static void remcons_vt_flush(void *);
+static void remcons_vt_key(void *, keymod_t, keycode_t, char);
 
 static vt100_cb_t remcons_vt_cb = {
 	.putuchar = remcons_vt_putchar,
 	.control_puts = remcons_vt_cputs,
-	.flush = remcons_vt_flush
+	.flush = remcons_vt_flush,
+	.key = remcons_vt_key
 };
 
@@ -340,19 +343,48 @@
 }
 
+/** Creates new keyboard event from given char.
+ *
+ * @param type Event type (press / release).
+ * @param c Pressed character.
+ */
+static kbd_event_t *new_kbd_event(kbd_event_type_t type, keymod_t mods,
+    keycode_t key, char c)
+{
+	kbd_event_t *event = malloc(sizeof(kbd_event_t));
+	assert(event);
+
+	link_initialize(&event->link);
+	event->type = type;
+	event->mods = mods;
+	event->key = key;
+	event->c = c;
+
+	return event;
+}
+
 static errno_t remcons_get_event(con_srv_t *srv, cons_event_t *event)
 {
+	remcons_t *remcons = srv_to_remcons(srv);
 	telnet_user_t *user = srv_to_user(srv);
-	kbd_event_t kevent;
-	errno_t rc;
-
-	rc = telnet_user_get_next_keyboard_event(user, &kevent);
-	if (rc != EOK) {
-		/* XXX What? */
-		memset(event, 0, sizeof(*event));
-		return EOK;
-	}
+	size_t nread;
+
+	while (list_empty(&remcons->in_events.list)) {
+		char next_byte = 0;
+
+		errno_t rc = telnet_user_recv(user, &next_byte, 1,
+		    &nread);
+		if (rc != EOK)
+			return rc;
+
+		vt100_rcvd_char(remcons->vt, next_byte);
+	}
+
+	link_t *link = prodcons_consume(&remcons->in_events);
+	kbd_event_t *tmp = list_get_instance(link, kbd_event_t, link);
 
 	event->type = CEV_KEY;
-	event->ev.key = kevent;
+	event->ev.key = *tmp;
+
+	free(tmp);
 
 	return EOK;
@@ -551,4 +583,16 @@
 }
 
+static void remcons_vt_key(void *arg, keymod_t mods, keycode_t key, char c)
+{
+	remcons_t *remcons = (remcons_t *)arg;
+
+	kbd_event_t *down = new_kbd_event(KEY_PRESS, mods, key, c);
+	kbd_event_t *up = new_kbd_event(KEY_RELEASE, mods, key, c);
+	assert(down);
+	assert(up);
+	prodcons_produce(&remcons->in_events, &down->link);
+	prodcons_produce(&remcons->in_events, &up->link);
+}
+
 /** Handle network connection.
  *
@@ -567,4 +611,5 @@
 	remcons->enable_rgb = !no_ctl && !no_rgb;
 	remcons->user = user;
+	prodcons_initialize(&remcons->in_events);
 
 	if (remcons->enable_ctl) {
Index: uspace/srv/hid/remcons/remcons.h
===================================================================
--- uspace/srv/hid/remcons/remcons.h	(revision d31c3ea70b393289736cdac0e91fa5c5eba06c4c)
+++ uspace/srv/hid/remcons/remcons.h	(revision 6907f26c4f1d524ca40bdb6622b8dc0254203a1f)
@@ -37,4 +37,5 @@
 #define REMCONS_H_
 
+#include <adt/prodcons.h>
 #include <stdbool.h>
 #include <vt/vt100.h>
@@ -54,4 +55,7 @@
 	charfield_t *ubuf;	/**< user buffer */
 	bool curs_visible;	/**< cursor is visible */
+
+	/** Producer-consumer of kbd_event_t. */
+	prodcons_t in_events;
 } remcons_t;
 
Index: uspace/srv/hid/remcons/user.c
===================================================================
--- uspace/srv/hid/remcons/user.c	(revision d31c3ea70b393289736cdac0e91fa5c5eba06c4c)
+++ uspace/srv/hid/remcons/user.c	(revision 6907f26c4f1d524ca40bdb6622b8dc0254203a1f)
@@ -83,5 +83,4 @@
 	user->conn = conn;
 	user->service_id = (service_id_t) -1;
-	prodcons_initialize(&user->in_events);
 	link_initialize(&user->link);
 	user->socket_buffer_len = 0;
@@ -211,7 +210,9 @@
 }
 
-/** Receive next byte from a socket (use buffering.
- * We need to return the value via extra argument because the read byte
- * might be negative.
+/** Receive next byte from a socket (use buffering).
+ *
+ * @param user Telnet user
+ * @param byte Place to store the received byte
+ * @return EOK on success or an error code
  */
 static errno_t telnet_user_recv_next_byte_locked(telnet_user_t *user, char *byte)
@@ -230,57 +231,12 @@
 }
 
-errno_t telnet_user_recv(telnet_user_t *user, void *data, size_t size,
-    size_t *nread)
-{
-	errno_t rc;
-	size_t nb;
-
-	/* No more buffered data? */
-	if (user->socket_buffer_len <= user->socket_buffer_pos) {
-		rc = telnet_user_fill_recv_buf(user);
-		if (rc != EOK)
-			return rc;
-	}
-
-	nb = user->socket_buffer_len - user->socket_buffer_pos;
-	memcpy(data, user->socket_buffer + user->socket_buffer_pos, nb);
-	user->socket_buffer_pos += nb;
-	*nread = nb;
-	return EOK;
-}
-
-/** Creates new keyboard event from given char.
- *
- * @param type Event type (press / release).
- * @param c Pressed character.
- */
-static kbd_event_t *new_kbd_event(kbd_event_type_t type, char32_t c)
-{
-	kbd_event_t *event = malloc(sizeof(kbd_event_t));
-	assert(event);
-
-	link_initialize(&event->link);
-	event->type = type;
-	event->c = c;
-	event->mods = 0;
-
-	switch (c) {
-	case '\n':
-		event->key = KC_ENTER;
-		break;
-	case '\t':
-		event->key = KC_TAB;
-		break;
-	case '\b':
-	case 127: /* This is what Linux telnet sends. */
-		event->key = KC_BACKSPACE;
-		event->c = '\b';
-		break;
-	default:
-		event->key = KC_A;
-		break;
-	}
-
-	return event;
+/** Determine if a received byte is available without waiting.
+ *
+ * @param user Telnet user
+ * @return @c true iff a byte is currently available
+ */
+static bool telnet_user_byte_avail(telnet_user_t *user)
+{
+	return user->socket_buffer_len > user->socket_buffer_pos;
 }
 
@@ -303,14 +259,22 @@
 }
 
-/** Get next keyboard event.
+/** Receive data from telnet connection.
  *
  * @param user Telnet user.
- * @param event Where to store the keyboard event.
- * @return Error code.
- */
-errno_t telnet_user_get_next_keyboard_event(telnet_user_t *user, kbd_event_t *event)
-{
-	fibril_mutex_lock(&user->guard);
-	if (list_empty(&user->in_events.list)) {
+ * @param buf Destination buffer
+ * @param size Buffer size
+ * @param nread Place to store number of bytes read (>0 on success)
+ * @return EOK on success or an error code
+ */
+errno_t telnet_user_recv(telnet_user_t *user, void *buf, size_t size,
+    size_t *nread)
+{
+	uint8_t *bp = (uint8_t *)buf;
+	fibril_mutex_lock(&user->guard);
+
+	assert(size > 0);
+	*nread = 0;
+
+	do {
 		char next_byte = 0;
 		bool inside_telnet_command = false;
@@ -319,13 +283,12 @@
 
 		/* Skip zeros, bail-out on error. */
-		while (next_byte == 0) {
-			fibril_mutex_unlock(&user->guard);
-
-			errno_t rc = telnet_user_recv_next_byte_locked(user, &next_byte);
-			if (rc != EOK)
+		do {
+			errno_t rc = telnet_user_recv_next_byte_locked(user,
+			    &next_byte);
+			if (rc != EOK) {
+				fibril_mutex_unlock(&user->guard);
 				return rc;
-
+			}
 			uint8_t byte = (uint8_t) next_byte;
-			fibril_mutex_lock(&user->guard);
 
 			/* Skip telnet commands. */
@@ -345,5 +308,5 @@
 				next_byte = 0;
 			}
-		}
+		} while (next_byte == 0 && telnet_user_byte_avail(user));
 
 		/* CR-LF conversions. */
@@ -352,21 +315,12 @@
 		}
 
-		kbd_event_t *down = new_kbd_event(KEY_PRESS, next_byte);
-		kbd_event_t *up = new_kbd_event(KEY_RELEASE, next_byte);
-		assert(down);
-		assert(up);
-		prodcons_produce(&user->in_events, &down->link);
-		prodcons_produce(&user->in_events, &up->link);
-	}
-
-	link_t *link = prodcons_consume(&user->in_events);
-	kbd_event_t *tmp = list_get_instance(link, kbd_event_t, link);
-
-	fibril_mutex_unlock(&user->guard);
-
-	*event = *tmp;
-
-	free(tmp);
-
+		if (next_byte != 0) {
+			*bp++ = next_byte;
+			++*nread;
+			--size;
+		}
+	} while (size > 0 && (telnet_user_byte_avail(user) || *nread == 0));
+
+	fibril_mutex_unlock(&user->guard);
 	return EOK;
 }
Index: uspace/srv/hid/remcons/user.h
===================================================================
--- uspace/srv/hid/remcons/user.h	(revision d31c3ea70b393289736cdac0e91fa5c5eba06c4c)
+++ uspace/srv/hid/remcons/user.h	(revision 6907f26c4f1d524ca40bdb6622b8dc0254203a1f)
@@ -37,5 +37,4 @@
 #define TELNET_USER_H_
 
-#include <adt/prodcons.h>
 #include <fibril_synch.h>
 #include <inet/tcp.h>
@@ -62,6 +61,4 @@
 	con_srvs_t srvs;
 
-	/** Producer-consumer of kbd_event_t. */
-	prodcons_t in_events;
 	link_t link;
 	char socket_buffer[BUFFER_SIZE];
