Index: uspace/lib/c/Makefile
===================================================================
--- uspace/lib/c/Makefile	(revision ad780544d441f9ddecd38ab48b2e79bcea7123cb)
+++ uspace/lib/c/Makefile	(revision 111d2d6731b1fbc5a2181c92094ca7b8dbaf9d2f)
@@ -95,4 +95,5 @@
 	generic/inetping.c \
 	generic/io/asprintf.c \
+	generic/io/input.c \
 	generic/io/io.c \
 	generic/io/chargrid.c \
Index: uspace/lib/c/generic/io/input.c
===================================================================
--- uspace/lib/c/generic/io/input.c	(revision 111d2d6731b1fbc5a2181c92094ca7b8dbaf9d2f)
+++ uspace/lib/c/generic/io/input.c	(revision 111d2d6731b1fbc5a2181c92094ca7b8dbaf9d2f)
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2012 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/**
+ * @file
+ * @brief Input protocol client stub
+ */
+
+#include <async.h>
+#include <assert.h>
+#include <errno.h>
+#include <io/kbd_event.h>
+#include <io/input.h>
+#include <ipc/input.h>
+#include <stdlib.h>
+
+static void input_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg);
+
+int input_open(async_sess_t *sess, input_ev_ops_t *ev_ops,
+    void *arg, input_t **rinput)
+{
+	input_t *input = calloc(1, sizeof(input_t));
+	if (input == NULL)
+		return ENOMEM;
+
+	input->sess = sess;
+	input->ev_ops = ev_ops;
+	input->user = arg;
+
+	async_exch_t *exch = async_exchange_begin(sess);
+
+	int rc = async_connect_to_me(exch, 0, 0, 0, input_cb_conn, input);
+	async_exchange_end(exch);
+
+	if (rc != EOK)
+		goto error;
+
+	*rinput = input;
+	return EOK;
+
+error:
+	if (input != NULL)
+		free(input);
+
+	return rc;
+}
+
+void input_close(input_t *input)
+{
+	/* XXX Synchronize with input_cb_conn */
+	free(input);
+}
+
+int input_yield(input_t *input)
+{
+	async_exch_t *exch = async_exchange_begin(input->sess);
+
+	int rc = async_req_0_0(exch, INPUT_YIELD);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+int input_reclaim(input_t *input)
+{
+	async_exch_t *exch = async_exchange_begin(input->sess);
+
+	int rc = async_req_0_0(exch, INPUT_RECLAIM);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+static void input_ev_key(input_t *input, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	kbd_event_type_t type;
+	keycode_t key;
+	keymod_t mods;
+	wchar_t c;
+	int rc;
+
+	type = IPC_GET_ARG1(*call);
+	key = IPC_GET_ARG2(*call);
+	mods = IPC_GET_ARG3(*call);
+	c = IPC_GET_ARG4(*call);
+
+	rc = input->ev_ops->key(input, type, key, mods, c);
+	async_answer_0(callid, rc);
+}
+
+static void input_ev_move(input_t *input, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	int dx;
+	int dy;
+	int rc;
+
+	dx = IPC_GET_ARG1(*call);
+	dy = IPC_GET_ARG2(*call);
+
+	rc = input->ev_ops->move(input, dx, dy);
+	async_answer_0(callid, rc);
+}
+
+static void input_ev_abs_move(input_t *input, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	unsigned x;
+	unsigned y;
+	unsigned max_x;
+	unsigned max_y;
+	int rc;
+
+	x = IPC_GET_ARG1(*call);
+	y = IPC_GET_ARG2(*call);
+	max_x = IPC_GET_ARG2(*call);
+	max_y = IPC_GET_ARG3(*call);
+
+	rc = input->ev_ops->abs_move(input, x, y, max_x, max_y);
+	async_answer_0(callid, rc);
+}
+
+static void input_ev_button(input_t *input, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	int bnum;
+	int press;
+	int rc;
+
+	bnum = IPC_GET_ARG1(*call);
+	press = IPC_GET_ARG2(*call);
+
+	rc = input->ev_ops->button(input, bnum, press);
+	async_answer_0(callid, rc);
+}
+
+static void input_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	input_t *input = (input_t *)arg;
+
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+
+		if (!IPC_GET_IMETHOD(call)) {
+			/* TODO: Handle hangup */
+			return;
+		}
+
+		switch (IPC_GET_IMETHOD(call)) {
+		case INPUT_EVENT_KEY:
+			input_ev_key(input, callid, &call);
+			break;
+		case INPUT_EVENT_MOVE:
+			input_ev_move(input, callid, &call);
+			break;
+		case INPUT_EVENT_ABS_MOVE:
+			input_ev_abs_move(input, callid, &call);
+			break;
+		case INPUT_EVENT_BUTTON:
+			input_ev_button(input, callid, &call);
+			break;
+		default:
+			async_answer_0(callid, ENOTSUP);
+		}
+	}
+}
+
+/** @}
+ */
Index: uspace/lib/c/include/io/console.h
===================================================================
--- uspace/lib/c/include/io/console.h	(revision ad780544d441f9ddecd38ab48b2e79bcea7123cb)
+++ uspace/lib/c/include/io/console.h	(revision 111d2d6731b1fbc5a2181c92094ca7b8dbaf9d2f)
@@ -37,4 +37,5 @@
 
 #include <sys/time.h>
+#include <io/kbd_event.h>
 #include <io/keycode.h>
 #include <async.h>
@@ -70,27 +71,4 @@
 } console_ctrl_t;
 
-typedef enum {
-	KEY_PRESS,
-	KEY_RELEASE
-} kbd_event_type_t;
-
-/** Console event structure. */
-typedef struct {
-	/** List handle */
-	link_t link;
-	
-	/** Press or release event. */
-	kbd_event_type_t type;
-	
-	/** Keycode of the key that was pressed or released. */
-	keycode_t key;
-	
-	/** Bitmask of modifiers held. */
-	keymod_t mods;
-	
-	/** The character that was generated or '\0' for none. */
-	wchar_t c;
-} kbd_event_t;
-
 extern console_ctrl_t *console_init(FILE *, FILE *);
 extern void console_done(console_ctrl_t *);
Index: uspace/lib/c/include/io/input.h
===================================================================
--- uspace/lib/c/include/io/input.h	(revision 111d2d6731b1fbc5a2181c92094ca7b8dbaf9d2f)
+++ uspace/lib/c/include/io/input.h	(revision 111d2d6731b1fbc5a2181c92094ca7b8dbaf9d2f)
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2012 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_IO_INPUT_H_
+#define LIBC_IO_INPUT_H_
+
+#include <async.h>
+#include <io/kbd_event.h>
+#include <sys/types.h>
+
+struct input_ev_ops;
+
+typedef struct {
+	async_sess_t *sess;
+	struct input_ev_ops *ev_ops;
+	void *user;
+} input_t;
+
+typedef struct input_ev_ops {
+	int (*key)(input_t *, kbd_event_type_t, keycode_t, keymod_t, wchar_t);
+	int (*move)(input_t *, int, int);
+	int (*abs_move)(input_t *, unsigned, unsigned, unsigned, unsigned);
+	int (*button)(input_t *, int, int);
+} input_ev_ops_t;
+
+extern int input_open(async_sess_t *, input_ev_ops_t *, void *, input_t **);
+extern void input_close(input_t *);
+extern int input_yield(input_t *);
+extern int input_reclaim(input_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/io/kbd_event.h
===================================================================
--- uspace/lib/c/include/io/kbd_event.h	(revision 111d2d6731b1fbc5a2181c92094ca7b8dbaf9d2f)
+++ uspace/lib/c/include/io/kbd_event.h	(revision 111d2d6731b1fbc5a2181c92094ca7b8dbaf9d2f)
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_IO_KBD_EVENT_H_
+#define LIBC_IO_KBD_EVENT_H_
+
+#include <adt/list.h>
+#include <io/keycode.h>
+
+typedef enum {
+	KEY_PRESS,
+	KEY_RELEASE
+} kbd_event_type_t;
+
+/** Console event structure. */
+typedef struct {
+	/** List handle */
+	link_t link;
+	
+	/** Press or release event. */
+	kbd_event_type_t type;
+	
+	/** Keycode of the key that was pressed or released. */
+	keycode_t key;
+	
+	/** Bitmask of modifiers held. */
+	keymod_t mods;
+	
+	/** The character that was generated or '\0' for none. */
+	wchar_t c;
+} kbd_event_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/hid/compositor/compositor.c
===================================================================
--- uspace/srv/hid/compositor/compositor.c	(revision ad780544d441f9ddecd38ab48b2e79bcea7123cb)
+++ uspace/srv/hid/compositor/compositor.c	(revision 111d2d6731b1fbc5a2181c92094ca7b8dbaf9d2f)
@@ -49,6 +49,6 @@
 #include <adt/prodcons.h>
 #include <adt/list.h>
+#include <io/input.h>
 #include <ipc/graph.h>
-#include <ipc/input.h>
 #include <ipc/window.h>
 
@@ -143,5 +143,26 @@
 static LIST_INITIALIZE(viewport_list);
 
-static async_sess_t *input_sess;
+/** Input server proxy */
+static input_t *input;
+
+static int comp_key_press(input_t *, kbd_event_type_t, keycode_t, keymod_t, wchar_t);
+static int comp_mouse_move(input_t *, int, int);
+static int comp_abs_move(input_t *, unsigned, unsigned, unsigned, unsigned);
+static int comp_mouse_button(input_t *, int, int);
+
+static input_ev_ops_t input_ev_ops = {
+	.key = comp_key_press,
+	.move = comp_mouse_move,
+	.abs_move = comp_abs_move,
+	.button = comp_mouse_button
+};
+
+static void input_disconnect(void);
+
+
+static pointer_t *input_pointer(input_t *input)
+{
+	return input->user;
+}
 
 static pointer_t *pointer_create()
@@ -759,5 +780,5 @@
 		fibril_mutex_unlock(&viewport_list_mtx);
 		loc_service_unregister(winreg_id);
-		async_hangup(input_sess);
+		input_disconnect();
 
 		/* Close all clients and their windows. */
@@ -1055,8 +1076,14 @@
 }
 
-static void comp_mouse_move(pointer_t *pointer, ipc_callid_t iid, ipc_call_t *icall)
-{
-	int dx = (int) IPC_GET_ARG1(*icall);
-	int dy = (int) IPC_GET_ARG2(*icall);
+static int comp_abs_move(input_t *input, unsigned x , unsigned y,
+    unsigned max_x, unsigned max_y)
+{
+	/* XXX TODO */
+	return EOK;
+}
+
+static int comp_mouse_move(input_t *input, int dx, int dy)
+{
+	pointer_t *pointer = input_pointer(input);
 
 	/* Update pointer position. */
@@ -1118,16 +1145,15 @@
 	}
 
-	async_answer_0(iid, EOK);
-}
-
-static void comp_mouse_button(pointer_t *pointer, ipc_callid_t iid, ipc_call_t *icall)
-{
-	sysarg_t btn_num = IPC_GET_ARG1(*icall);
-	bool pressed = (bool) IPC_GET_ARG2(*icall);
-
-	if (pressed) {
+	return EOK;
+}
+
+static int comp_mouse_button(input_t *input, int bnum, int bpress)
+{
+	pointer_t *pointer = input_pointer(input);
+
+	if (bpress) {
 		pointer->btn_hpos = pointer->hpos;
 		pointer->btn_vpos = pointer->vpos;
-		pointer->btn_num = btn_num;
+		pointer->btn_num = bnum;
 		pointer->pressed = true;
 
@@ -1137,6 +1163,5 @@
 		if (!win || !win->surface) {
 			fibril_mutex_unlock(&window_list_mtx);
-			async_answer_0(iid, EOK);
-			return;
+			return EOK;
 		}
 		sysarg_t x, y, width, height;
@@ -1154,14 +1179,13 @@
 				event->data.pos.pos_id = pointer->id;
 				event->data.pos.type = POS_PRESS;
-				event->data.pos.btn_num = btn_num;
+				event->data.pos.btn_num = bnum;
 				event->data.pos.hpos = x;
 				event->data.pos.vpos = y;
 				comp_post_event(event);
 			} else {
-				async_answer_0(iid, ENOMEM);
-				return;
-			}
-		}
-	} else if (pointer->pressed && pointer->btn_num == btn_num) {
+				return ENOMEM;
+			}
+		}
+	} else if (pointer->pressed && pointer->btn_num == (unsigned)bnum) {
 		pointer->pressed = false;
 
@@ -1191,6 +1215,5 @@
 			pointer->grab_flags = GF_EMPTY;
 			fibril_mutex_unlock(&window_list_mtx);
-			async_answer_0(iid, EOK);
-			return;
+			return EOK;
 		}
 
@@ -1257,5 +1280,5 @@
 				event->data.pos.pos_id = pointer->id;
 				event->data.pos.type = POS_RELEASE;
-				event->data.pos.btn_num = btn_num;
+				event->data.pos.btn_num = bnum;
 				event->data.pos.hpos = point_x;
 				event->data.pos.vpos = point_y;
@@ -1263,5 +1286,5 @@
 			pointer->grab_flags = GF_EMPTY;
 			
-		} else if (within_client && (pointer->grab_flags == GF_EMPTY) && (btn_num == 1)) {
+		} else if (within_client && (pointer->grab_flags == GF_EMPTY) && (bnum == 1)) {
 
 			/* Bring the window to the foreground. */
@@ -1286,14 +1309,10 @@
 	}
 
-	async_answer_0(iid, EOK);
-}
-
-static void comp_key_press(ipc_callid_t iid, ipc_call_t *icall)
-{
-	kbd_event_type_t type = IPC_GET_ARG1(*icall);
-	keycode_t key = IPC_GET_ARG2(*icall);
-	keymod_t mods = IPC_GET_ARG3(*icall);
-	wchar_t c = IPC_GET_ARG4(*icall);
-
+	return EOK;
+}
+
+static int comp_key_press(input_t *input, kbd_event_type_t type, keycode_t key,
+    keymod_t mods, wchar_t c)
+{
 	bool win_transform = (mods & KM_ALT) && (
 	    key == KC_W || key == KC_S || key == KC_A || key == KC_D ||
@@ -1378,6 +1397,5 @@
 			if (event == NULL) {
 				fibril_mutex_unlock(&window_list_mtx);
-				async_answer_0(iid, ENOMEM);
-				return;
+				return ENOMEM;
 			}
 
@@ -1448,8 +1466,6 @@
 	} else if (win_close) {
 		window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
-		if (event == NULL) {
-			async_answer_0(iid, ENOMEM);
-			return;
-		}
+		if (event == NULL)
+			return ENOMEM;
 
 		link_initialize(&event->link);
@@ -1594,8 +1610,6 @@
 	} else {
 		window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
-		if (event == NULL) {
-			async_answer_0(iid, ENOMEM);
-			return;
-		}
+		if (event == NULL)
+			return ENOMEM;
 
 		link_initialize(&event->link);
@@ -1609,9 +1623,25 @@
 	}
 
-	async_answer_0(iid, EOK);
-}
-
-static void input_events(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
+	return EOK;
+}
+
+static int input_connect(const char *svc)
+{
+	async_sess_t *sess;
+	service_id_t dsid;
+
+	int 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(EXCHANGE_ATOMIC, dsid, 0);
+	if (sess == NULL) {
+		printf("%s: Unable to connect to input service %s\n", NAME,
+		    svc);
+		return EIO;
+	}
+
 	fibril_mutex_lock(&pointer_list_mtx);
 	pointer_t *pointer = pointer_create();
@@ -1622,63 +1652,26 @@
 	fibril_mutex_unlock(&pointer_list_mtx);
 
-	/* Ignore parameters, the connection is already opened. */
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-
-		if (!IPC_GET_IMETHOD(call)) {
-			fibril_mutex_lock(&pointer_list_mtx);
-			if (pointer != NULL) {
-				list_remove(&pointer->link);
-				pointer_destroy(pointer);
-			}
-			fibril_mutex_unlock(&pointer_list_mtx);
-			async_hangup(input_sess);
-			return;
-		}
-
-		switch (IPC_GET_IMETHOD(call)) {
-		case INPUT_EVENT_KEY:
-			comp_key_press(callid, &call);
-			break;
-		case INPUT_EVENT_MOVE:
-			comp_mouse_move(pointer, callid, &call);
-			break;
-		case INPUT_EVENT_BUTTON:
-			comp_mouse_button(pointer, callid, &call);
-			break;
-		default:
-			async_answer_0(callid, EINVAL);
-		}
-	}
-}
-
-static async_sess_t *input_connect(const char *svc)
-{
-	async_sess_t *sess;
-	service_id_t dsid;
-
-	int rc = loc_service_get_id(svc, &dsid, 0);
-	if (rc == EOK) {
-		sess = loc_service_connect(EXCHANGE_ATOMIC, dsid, 0);
-		if (sess == NULL) {
-			printf("%s: Unable to connect to input service %s\n", NAME, svc);
-			return NULL;
-		}
-	} else
-		return NULL;
-
-	async_exch_t *exch = async_exchange_begin(sess);
-	rc = async_connect_to_me(exch, 0, 0, 0, input_events, NULL);
-	async_exchange_end(exch);
-
+	if (pointer == NULL) {
+		printf("%s: Cannot create pointer.\n", NAME);
+		async_hangup(sess);
+		return ENOMEM;
+	}
+
+	rc = input_open(sess, &input_ev_ops, pointer, &input);
 	if (rc != EOK) {
 		async_hangup(sess);
-		printf("%s: Unable to create callback connection to service %s (%s)\n",
+		printf("%s: Unable to communicate with service %s (%s)\n",
 		    NAME, svc, str_error(rc));
-		return NULL;
-	}
-
-	return sess;
+		return rc;
+	}
+
+	return EOK;
+}
+
+static void input_disconnect(void)
+{
+    	pointer_t *pointer = input->user;
+	input_close(input);
+	pointer_destroy(pointer);
 }
 
@@ -1733,8 +1726,7 @@
 
 	/* Establish input bidirectional connection. */
-	input_sess = input_connect(input_svc);
-	if (input_sess == NULL) {
-		return -1;
-	}
+	rc = input_connect(input_svc);
+	if (rc != EOK)
+		return rc;
 
 	/* Create viewports and connect them to visualizers. */
@@ -1742,5 +1734,5 @@
 	rc = loc_category_get_id("visualizer", &cat_id, IPC_FLAG_BLOCKING);
 	if (rc != EOK) {
-		async_hangup(input_sess);
+		input_disconnect();
 		return -1;
 	}
@@ -1750,5 +1742,5 @@
 	rc = loc_category_get_svcs(cat_id, &svcs, &svcs_cnt);
 	if (rc != EOK || svcs_cnt == 0) {
-		async_hangup(input_sess);
+		input_disconnect();
 		return -1;
 	}
@@ -1766,5 +1758,5 @@
 	
 	if (list_empty(&viewport_list)) {
-		async_hangup(input_sess);
+		input_disconnect();
 		return -1;
 	}
Index: uspace/srv/hid/console/console.c
===================================================================
--- uspace/srv/hid/console/console.c	(revision ad780544d441f9ddecd38ab48b2e79bcea7123cb)
+++ uspace/srv/hid/console/console.c	(revision 111d2d6731b1fbc5a2181c92094ca7b8dbaf9d2f)
@@ -36,5 +36,5 @@
 #include <stdio.h>
 #include <adt/prodcons.h>
-#include <ipc/input.h>
+#include <io/input.h>
 #include <ipc/console.h>
 #include <ipc/vfs.h>
@@ -43,4 +43,5 @@
 #include <loc.h>
 #include <event.h>
+#include <io/kbd_event.h>
 #include <io/keycode.h>
 #include <io/chargrid.h>
@@ -81,6 +82,6 @@
 } console_t;
 
-/** Session to the input server */
-static async_sess_t *input_sess;
+/** Input server proxy */
+static input_t *input;
 
 /** Session to the output server */
@@ -100,4 +101,16 @@
 static console_t *active_console = &consoles[0];
 static console_t *kernel_console = &consoles[KERNEL_CONSOLE];
+
+static int input_ev_key(input_t *, kbd_event_type_t, keycode_t, keymod_t, wchar_t);
+static int input_ev_move(input_t *, int, int);
+static int input_ev_abs_move(input_t *, unsigned, unsigned, unsigned, unsigned);
+static int input_ev_button(input_t *, int, int);
+
+static input_ev_ops_t input_ev_ops = {
+	.key = input_ev_key,
+	.move = input_ev_move,
+	.abs_move = input_ev_abs_move,
+	.button = input_ev_button
+};
 
 static void cons_update(console_t *cons)
@@ -195,71 +208,53 @@
 }
 
-static void input_events(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	/* Ignore parameters, the connection is already opened */
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call)) {
-			/* TODO: Handle hangup */
-			async_hangup(input_sess);
-			return;
-		}
-		
-		kbd_event_type_t type;
-		keycode_t key;
-		keymod_t mods;
-		wchar_t c;
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case INPUT_EVENT_KEY:
-			type = IPC_GET_ARG1(call);
-			key = IPC_GET_ARG2(call);
-			mods = IPC_GET_ARG3(call);
-			c = IPC_GET_ARG4(call);
-			
-			if ((key >= KC_F1) && (key < KC_F1 + CONSOLE_COUNT) &&
-			    ((mods & KM_CTRL) == 0))
-				cons_switch(&consoles[key - KC_F1]);
-			else {
-				/* Got key press/release event */
-				kbd_event_t *event =
-				    (kbd_event_t *) malloc(sizeof(kbd_event_t));
-				if (event == NULL) {
-					async_answer_0(callid, ENOMEM);
-					break;
-				}
-				
-				link_initialize(&event->link);
-				event->type = type;
-				event->key = key;
-				event->mods = mods;
-				event->c = c;
-				
-				/*
-				 * Kernel console does not read events
-				 * from us, so we will redirect them
-				 * to the (last) active userspace console
-				 * if necessary.
-				 */
-				console_t *target_console = cons_get_active_uspace();
-				
-				prodcons_produce(&target_console->input_pc,
-				    &event->link);
-			}
-			
-			async_answer_0(callid, EOK);
-			break;
-		case INPUT_EVENT_MOVE:
-			async_answer_0(callid, EOK);
-			break;
-		case INPUT_EVENT_BUTTON:
-			async_answer_0(callid, EOK);
-			break;
-		default:
-			async_answer_0(callid, EINVAL);
-		}
-	}
+static int input_ev_key(input_t *input, kbd_event_type_t type, keycode_t key,
+    keymod_t mods, wchar_t c)
+{
+	if ((key >= KC_F1) && (key < KC_F1 + CONSOLE_COUNT) &&
+	    ((mods & KM_CTRL) == 0)) {
+		cons_switch(&consoles[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;
+		
+		/*
+		 * Kernel console does not read events
+		 * from us, so we will redirect them
+		 * to the (last) active userspace console
+		 * if necessary.
+		 */
+		console_t *target_console = cons_get_active_uspace();
+		
+		prodcons_produce(&target_console->input_pc,
+		    &event->link);
+	}
+	
+	return EOK;
+}
+
+static int input_ev_move(input_t *input, int dx, int dy)
+{
+	return EOK;
+}
+
+static int input_ev_abs_move(input_t *input, unsigned x , unsigned y,
+    unsigned max_x, unsigned max_y)
+{
+	return EOK;
+}
+
+static int input_ev_button(input_t *input, int bnum, int bpress)
+{
+	return EOK;
 }
 
@@ -518,5 +513,5 @@
 }
 
-static async_sess_t *input_connect(const char *svc)
+static int input_connect(const char *svc)
 {
 	async_sess_t *sess;
@@ -524,26 +519,25 @@
 	
 	int rc = loc_service_get_id(svc, &dsid, 0);
-	if (rc == EOK) {
-		sess = loc_service_connect(EXCHANGE_ATOMIC, dsid, 0);
-		if (sess == NULL) {
-			printf("%s: Unable to connect to input service %s\n", NAME,
-			    svc);
-			return NULL;
-		}
-	} else
-		return NULL;
-	
-	async_exch_t *exch = async_exchange_begin(sess);
-	rc = async_connect_to_me(exch, 0, 0, 0, input_events, NULL);
-	async_exchange_end(exch);
-	
+	if (rc != EOK) {
+		printf("%s: Input service %s not found\n", NAME, svc);
+		return rc;
+	}
+
+	sess = loc_service_connect(EXCHANGE_ATOMIC, dsid, 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 create callback connection to service %s (%s)\n",
+		printf("%s: Unable to communicate with service %s (%s)\n",
 		    NAME, svc, str_error(rc));
-		return NULL;
-	}
-	
-	return sess;
+		return rc;
+	}
+	
+	return EOK;
 }
 
@@ -574,7 +568,9 @@
 static bool console_srv_init(char *input_svc, char *output_svc)
 {
+	int rc;
+	
 	/* Connect to input service */
-	input_sess = input_connect(input_svc);
-	if (input_sess == NULL)
+	rc = input_connect(input_svc);
+	if (rc != EOK)
 		return false;
 	
@@ -586,5 +582,5 @@
 	/* Register server */
 	async_set_client_connection(client_connection);
-	int rc = loc_server_register(NAME);
+	rc = loc_server_register(NAME);
 	if (rc != EOK) {
 		printf("%s: Unable to register server (%s)\n", NAME,
