Index: uspace/lib/display/src/disp_srv.c
===================================================================
--- uspace/lib/display/src/disp_srv.c	(revision c8cf261a450bf22fa6cc6e9872a1f07f9e7dccda)
+++ uspace/lib/display/src/disp_srv.c	(revision be15256dedca4e5ededf91be5e6ece8445b5a118)
@@ -36,8 +36,25 @@
 
 #include <disp_srv.h>
+#include <display/event.h>
 #include <errno.h>
+#include <io/log.h>
 #include <ipc/display.h>
 #include <stdlib.h>
 #include <stddef.h>
+
+#include <stdio.h>
+static void display_callback_create_srv(display_srv_t *srv, ipc_call_t *call)
+{
+	printf("display_callback_create_srv\n");
+
+	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	if (sess == NULL) {
+		async_answer_0(call, ENOMEM);
+		return;
+	}
+
+	srv->client_sess = sess;
+	async_answer_0(call, EOK);
+}
 
 static void display_window_create_srv(display_srv_t *srv, ipc_call_t *icall)
@@ -45,4 +62,6 @@
 	sysarg_t wnd_id;
 	errno_t rc;
+
+	printf("display_window_create_srv\n");
 
 	if (srv->ops->window_create == NULL) {
@@ -60,4 +79,6 @@
 	errno_t rc;
 
+	printf("display_window_destroy_srv\n");
+
 	wnd_id = ipc_get_arg1(icall);
 
@@ -71,8 +92,50 @@
 }
 
+static void display_get_event_srv(display_srv_t *srv, ipc_call_t *icall)
+{
+	sysarg_t wnd_id;
+	display_wnd_ev_t event;
+	ipc_call_t call;
+	size_t size;
+	errno_t rc;
+
+	printf("display_get_event_srv\n");
+
+	if (srv->ops->get_event == NULL) {
+		async_answer_0(icall, ENOTSUP);
+		return;
+	}
+
+	rc = srv->ops->get_event(srv->arg, &wnd_id, &event);
+	if (rc != EOK)
+		async_answer_0(icall, rc);
+
+	/* Transfer event data */
+	if (!async_data_read_receive(&call, &size)) {
+		async_answer_0(icall, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(event)) {
+		async_answer_0(icall, EREFUSED);
+		async_answer_0(&call, EREFUSED);
+		return;
+	}
+
+	rc = async_data_read_finalize(&call, &event, sizeof(event));
+	if (rc != EOK) {
+		async_answer_0(icall, rc);
+		async_answer_0(&call, rc);
+		return;
+	}
+
+	async_answer_1(icall, EOK, wnd_id);
+}
+
 void display_conn(ipc_call_t *icall, display_srv_t *srv)
 {
 	/* Accept the connection */
 	async_accept_0(icall);
+	printf("display_conn\n");
 
 	while (true) {
@@ -88,5 +151,9 @@
 		}
 
+		printf("display_conn method=%lu\n", method);
 		switch (method) {
+		case DISPLAY_CALLBACK_CREATE:
+			display_callback_create_srv(srv, &call);
+			break;
 		case DISPLAY_WINDOW_CREATE:
 			display_window_create_srv(srv, &call);
@@ -94,4 +161,7 @@
 		case DISPLAY_WINDOW_DESTROY:
 			display_window_destroy_srv(srv, &call);
+			break;
+		case DISPLAY_GET_EVENT:
+			display_get_event_srv(srv, &call);
 			break;
 		default:
@@ -101,4 +171,19 @@
 }
 
+/** Send 'pending' event to client.
+ *
+ * @param srv Display server structure
+ */
+void display_srv_ev_pending(display_srv_t *srv)
+{
+	async_exch_t *exch;
+
+	printf("display_srv_ev_pending()\n");
+
+	exch = async_exchange_begin(srv->client_sess);
+	async_msg_0(exch, DISPLAY_EV_PENDING);
+	async_exchange_end(exch);
+}
+
 /** @}
  */
Index: uspace/lib/display/src/display.c
===================================================================
--- uspace/lib/display/src/display.c	(revision c8cf261a450bf22fa6cc6e9872a1f07f9e7dccda)
+++ uspace/lib/display/src/display.c	(revision be15256dedca4e5ededf91be5e6ece8445b5a118)
@@ -30,4 +30,5 @@
 #include <display.h>
 #include <errno.h>
+#include <fibril_synch.h>
 #include <ipc/display.h>
 #include <ipc/services.h>
@@ -36,4 +37,8 @@
 #include <stdlib.h>
 
+static errno_t display_callback_create(display_t *);
+static void display_cb_conn(ipc_call_t *, void *);
+static errno_t display_get_window(display_t *, sysarg_t, display_window_t **);
+
 /** Open display service.
  *
@@ -51,4 +56,6 @@
 	if (display == NULL)
 		return ENOMEM;
+
+	list_initialize(&display->windows);
 
 	if (dsname == NULL)
@@ -68,8 +75,41 @@
 	}
 
+	rc = display_callback_create(display);
+	if (rc != EOK) {
+		async_hangup(display->sess);
+		free(display);
+		return EIO;
+	}
+
 	*rdisplay = display;
 	return EOK;
 }
 
+/** Create callback connection from display service.
+ *
+ * @param display Display session
+ * @return EOK on success or an error code
+ */
+static errno_t display_callback_create(display_t *display)
+{
+	async_exch_t *exch = async_exchange_begin(display->sess);
+
+	aid_t req = async_send_0(exch, DISPLAY_CALLBACK_CREATE, NULL);
+
+	port_id_t port;
+	errno_t rc = async_create_callback_port(exch, INTERFACE_DISPLAY_CB, 0, 0,
+	    display_cb_conn, display, &port);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK)
+		return rc;
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
 /** Close display service.
  *
@@ -79,4 +119,12 @@
 {
 	async_hangup(display->sess);
+
+	/* Wait for callback handler to terminate */
+
+	fibril_mutex_lock(&display->lock);
+	while (!display->cb_done)
+		fibril_condvar_wait(&display->cv, &display->lock);
+	fibril_mutex_unlock(&display->lock);
+
 	free(display);
 }
@@ -85,8 +133,11 @@
  *
  * @param display Display
+ * @param cb Callback functions
+ * @param cb_arg Argument to callback functions
  * @param rwindow Place to store pointer to new window
  * @return EOK on success or an error code
  */
-errno_t display_window_create(display_t *display, display_window_t **rwindow)
+errno_t display_window_create(display_t *display, display_wnd_cb_t *cb,
+    void *cb_arg, display_window_t **rwindow)
 {
 	display_window_t *window;
@@ -111,4 +162,8 @@
 	window->display = display;
 	window->id = wnd_id;
+	window->cb = cb;
+	window->cb_arg = cb_arg;
+
+	list_append(&window->lwindows, &display->windows);
 	*rwindow = window;
 	return EOK;
@@ -166,4 +221,130 @@
 }
 
+/** Get display event.
+ *
+ * @param display Display
+ * @param rwindow Place to store pointe to window that received event
+ * @param event Place to store event
+ * @return EOK on success or an error code
+ */
+static errno_t display_get_event(display_t *display, display_window_t **rwindow,
+    display_wnd_ev_t *event)
+{
+	async_exch_t *exch;
+	ipc_call_t answer;
+	aid_t req;
+	errno_t rc;
+	sysarg_t wnd_id;
+	display_window_t *window;
+
+	exch = async_exchange_begin(display->sess);
+	req = async_send_0(exch, DISPLAY_GET_EVENT, &answer);
+	rc = async_data_read_start(exch, event, sizeof(*event));
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	async_exchange_end(exch);
+
+	async_wait_for(req, &rc);
+	if (rc != EOK)
+		return rc;
+
+	wnd_id = ipc_get_arg1(&answer);
+	rc = display_get_window(display, wnd_id, &window);
+	if (rc != EOK)
+		return EIO;
+
+	*rwindow = window;
+	return EOK;
+}
+
+/** Display events are pending.
+ *
+ * @param display Display
+ * @param icall Call data
+ */
+static void display_ev_pending(display_t *display, ipc_call_t *icall)
+{
+	errno_t rc;
+	display_window_t *window = NULL;
+	display_wnd_ev_t event;
+
+	while (true) {
+		rc = display_get_event(display, &window, &event);
+		if (rc != EOK)
+			break;
+
+		if (window->cb->kbd_event != NULL)
+			window->cb->kbd_event(window->cb_arg, &event.kbd_event);
+	}
+
+	async_answer_0(icall, EOK);
+}
+
+/** Callback connection handler.
+ *
+ * @param icall Connect call data
+ * @param arg   Argument, display_t *
+ */
+static void display_cb_conn(ipc_call_t *icall, void *arg)
+{
+	display_t *display = (display_t *) arg;
+
+	while (true) {
+		ipc_call_t call;
+		async_get_call(&call);
+
+		if (!ipc_get_imethod(&call)) {
+			/* Hangup */
+			async_answer_0(&call, EOK);
+			goto out;
+		}
+
+		switch (ipc_get_imethod(&call)) {
+		case DISPLAY_EV_PENDING:
+			display_ev_pending(display, &call);
+			break;
+		default:
+			async_answer_0(&call, ENOTSUP);
+			break;
+		}
+	}
+
+out:
+	fibril_mutex_lock(&display->lock);
+	display->cb_done = true;
+	fibril_mutex_unlock(&display->lock);
+	fibril_condvar_broadcast(&display->cv);
+}
+
+/** Find window by ID.
+ *
+ * @param display Display
+ * @param wnd_id Window ID
+ * @param rwindow Place to store pointer to window
+ * @return EOK on success, ENOENT if not found
+ */
+static errno_t display_get_window(display_t *display, sysarg_t wnd_id,
+    display_window_t **rwindow)
+{
+	link_t *link;
+	display_window_t *window;
+
+	link = list_first(&display->windows);
+	while (link != NULL) {
+		window = list_get_instance(link, display_window_t, lwindows);
+		if (window->id == wnd_id) {
+			*rwindow = window;
+			return EOK;
+		}
+
+		link = list_next(link, &display->windows);
+	}
+
+	return ENOENT;
+}
+
 /** @}
  */
