Index: uspace/srv/hid/display/display.c
===================================================================
--- uspace/srv/hid/display/display.c	(revision 02f45748527ff6b6574d9c78f003e8747112a273)
+++ uspace/srv/hid/display/display.c	(revision 4fbdc3d4f4d1d992db668ffd58dad07f9d4a07db)
@@ -267,28 +267,15 @@
  * @param event Event
  */
-errno_t ds_display_post_pos_event(ds_display_t *display, pos_event_t *event)
-{
-	gfx_coord2_t pos;
-	ds_window_t *wnd;
+errno_t ds_display_post_ptd_event(ds_display_t *display, ptd_event_t *event)
+{
 	ds_seat_t *seat;
 
-	/* Focus window on button press */
-	if (event->type == POS_PRESS) {
-		printf("Button press\n");
-		pos.x = event->hpos;
-		pos.y = event->vpos;
-
-		wnd = ds_display_window_by_pos(display, &pos);
-		if (wnd != NULL) {
-			seat = ds_display_first_seat(display);
-			if (seat == NULL)
-				return EOK;
-
-			ds_seat_set_focus(seat, wnd);
-			return EOK;
-		}
-	}
-
-	return EOK;
+	// TODO Determine which seat the event belongs to
+	seat = ds_display_first_seat(display);
+	printf("ds_display_post_ptd_event: seat=%p\n", seat);
+	if (seat == NULL)
+		return EOK;
+
+	return ds_seat_post_ptd_event(seat, event);
 }
 
@@ -408,5 +395,5 @@
 	ddev = ds_display_first_ddev(display);
 	if (ddev == NULL)
-		abort();
+		return NULL;
 
 	return ddev->gc;
Index: uspace/srv/hid/display/display.h
===================================================================
--- uspace/srv/hid/display/display.h	(revision 02f45748527ff6b6574d9c78f003e8747112a273)
+++ uspace/srv/hid/display/display.h	(revision 4fbdc3d4f4d1d992db668ffd58dad07f9d4a07db)
@@ -40,8 +40,8 @@
 #include <gfx/context.h>
 #include <io/kbd_event.h>
-#include <io/pos_event.h>
 #include "types/display/client.h"
 #include "types/display/ddev.h"
 #include "types/display/display.h"
+#include "types/display/ptd_event.h"
 #include "types/display/seat.h"
 
@@ -59,5 +59,5 @@
 extern ds_window_t *ds_display_next_window(ds_window_t *);
 extern errno_t ds_display_post_kbd_event(ds_display_t *, kbd_event_t *);
-extern errno_t ds_display_post_pos_event(ds_display_t *, pos_event_t *);
+extern errno_t ds_display_post_ptd_event(ds_display_t *, ptd_event_t *);
 extern void ds_display_add_seat(ds_display_t *, ds_seat_t *);
 extern void ds_display_remove_seat(ds_seat_t *);
Index: uspace/srv/hid/display/input.c
===================================================================
--- uspace/srv/hid/display/input.c	(revision 02f45748527ff6b6574d9c78f003e8747112a273)
+++ uspace/srv/hid/display/input.c	(revision 4fbdc3d4f4d1d992db668ffd58dad07f9d4a07db)
@@ -85,5 +85,13 @@
 static errno_t ds_input_ev_move(input_t *input, int dx, int dy)
 {
-	return EOK;
+	ds_display_t *disp = (ds_display_t *) input->user;
+	ptd_event_t event;
+
+	printf("ds_input_ev_move\n");
+	event.type = PTD_MOVE;
+	event.dmove.x = dx;
+	event.dmove.y = dy;
+
+	return ds_display_post_ptd_event(disp, &event);
 }
 
@@ -91,4 +99,6 @@
     unsigned max_x, unsigned max_y)
 {
+	printf("ds_input_ev_abs_move x=%u y=%u mx=%u my=%u\n",
+	    x, y, max_x, max_y);
 	return EOK;
 }
@@ -96,5 +106,14 @@
 static errno_t ds_input_ev_button(input_t *input, int bnum, int bpress)
 {
-	return EOK;
+	ds_display_t *disp = (ds_display_t *) input->user;
+	ptd_event_t event;
+
+	printf("ds_input_ev_abs_button\n");
+	event.type = bpress ? PTD_PRESS : PTD_RELEASE;
+	event.btn_num = bnum;
+	event.dmove.x = 0;
+	event.dmove.y = 0;
+
+	return ds_display_post_ptd_event(disp, &event);
 }
 
Index: uspace/srv/hid/display/seat.c
===================================================================
--- uspace/srv/hid/display/seat.c	(revision 02f45748527ff6b6574d9c78f003e8747112a273)
+++ uspace/srv/hid/display/seat.c	(revision 4fbdc3d4f4d1d992db668ffd58dad07f9d4a07db)
@@ -36,4 +36,6 @@
 #include <adt/list.h>
 #include <errno.h>
+#include <gfx/color.h>
+#include <gfx/render.h>
 #include <stdlib.h>
 #include "client.h"
@@ -57,4 +59,6 @@
 
 	ds_display_add_seat(display, seat);
+	seat->pntpos.x = 10;
+	seat->pntpos.y = 10;
 
 	*rseat = seat;
@@ -72,4 +76,9 @@
 }
 
+/** Set seat focus to a window.
+ *
+ * @param seat Seat
+ * @param wnd Window to focus
+ */
 void ds_seat_set_focus(ds_seat_t *seat, ds_window_t *wnd)
 {
@@ -77,4 +86,11 @@
 }
 
+/** Evacuate focus from window.
+ *
+ * If seat's focus is @a wnd, it will be set to a different window.
+ *
+ * @param seat Seat
+ * @param wnd Window to evacuate focus from
+ */
 void ds_seat_evac_focus(ds_seat_t *seat, ds_window_t *wnd)
 {
@@ -119,4 +135,130 @@
 }
 
+/** Draw cross at seat pointer position.
+ *
+ * @param seat Seat
+ * @param len Cross length
+ * @param w Cross extra width
+ * @param br Brightness (0 to 65535)
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t ds_seat_draw_cross(ds_seat_t *seat, gfx_coord_t len,
+    gfx_coord_t w, uint16_t br)
+{
+	gfx_color_t *color = NULL;
+	gfx_context_t *gc;
+	gfx_rect_t rect, r0;
+	errno_t rc;
+
+	gc = ds_display_get_gc(seat->display);
+	if (gc == NULL)
+		return EOK;
+
+	rc = gfx_color_new_rgb_i16(br, br, br, &color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_set_color(gc, color);
+	if (rc != EOK)
+		goto error;
+
+	r0.p0.x = -len;
+	r0.p0.y = -w;
+	r0.p1.x = +len + 1;
+	r0.p1.y = +w + 1;
+	gfx_rect_translate(&seat->pntpos, &r0, &rect);
+
+	rc = gfx_fill_rect(gc, &rect);
+	if (rc != EOK)
+		goto error;
+
+	r0.p0.x = -w;
+	r0.p0.y = -len;
+	r0.p1.x = +w + 1;
+	r0.p1.y = +len + 1;
+	gfx_rect_translate(&seat->pntpos, &r0, &rect);
+
+	rc = gfx_fill_rect(gc, &rect);
+	if (rc != EOK)
+		goto error;
+
+	gfx_color_delete(color);
+	return EOK;
+error:
+	if (color != NULL)
+		gfx_color_delete(color);
+	return rc;
+}
+
+/** Draw or clear seat pointer
+ *
+ * @param seat Seat
+ * @param shown @c true to display pointer, @c false to clear it
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t ds_seat_draw_pointer(ds_seat_t *seat, bool shown)
+{
+	errno_t rc;
+
+	rc = ds_seat_draw_cross(seat, 8, 1, 0);
+	if (rc != EOK)
+		return rc;
+
+	rc = ds_seat_draw_cross(seat, 8, 0, shown ? 65535 : 0);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+/** Post pointing device event to the seat
+ *
+ * @param seat Seat
+ * @param event Event
+ *
+ * @return EOK on success or an error code
+ */
+#include <stdio.h>
+errno_t ds_seat_post_ptd_event(ds_seat_t *seat, ptd_event_t *event)
+{
+	ds_window_t *wnd;
+	gfx_coord2_t npos;
+
+	printf("ds_seat_post_ptd_event\n");
+	/* Focus window on button press */
+	if (event->type == PTD_PRESS) {
+		printf("PTD_PRESS\n");
+		wnd = ds_display_window_by_pos(seat->display, &seat->pntpos);
+		if (wnd != NULL) {
+			printf("set focus\n");
+			ds_seat_set_focus(seat, wnd);
+			return EOK;
+		}
+	}
+
+	if (event->type == PTD_MOVE) {
+		printf("PTD_MOVE\n");
+		gfx_coord2_add(&seat->pntpos, &event->dmove, &npos);
+		if (npos.x < 10)
+			npos.x = 10;
+		if (npos.y < 10)
+			npos.y = 10;
+		if (npos.x > 1024 - 10)
+			npos.x = 1024 - 10;
+		if (npos.y > 768 - 10)
+			npos.y = 768 - 10;
+
+		printf("clear pointer\n");
+		(void) ds_seat_draw_pointer(seat, false);
+		seat->pntpos = npos;
+		printf("draw pointer\n");
+		(void) ds_seat_draw_pointer(seat, true);
+	}
+
+	return EOK;
+}
+
 /** @}
  */
Index: uspace/srv/hid/display/seat.h
===================================================================
--- uspace/srv/hid/display/seat.h	(revision 02f45748527ff6b6574d9c78f003e8747112a273)
+++ uspace/srv/hid/display/seat.h	(revision 4fbdc3d4f4d1d992db668ffd58dad07f9d4a07db)
@@ -41,4 +41,5 @@
 #include "types/display/display.h"
 #include "types/display/seat.h"
+#include "types/display/ptd_event.h"
 #include "types/display/window.h"
 
@@ -48,4 +49,5 @@
 extern void ds_seat_evac_focus(ds_seat_t *, ds_window_t *);
 extern errno_t ds_seat_post_kbd_event(ds_seat_t *, kbd_event_t *);
+extern errno_t ds_seat_post_ptd_event(ds_seat_t *, ptd_event_t *);
 
 #endif
Index: uspace/srv/hid/display/test/display.c
===================================================================
--- uspace/srv/hid/display/test/display.c	(revision 02f45748527ff6b6574d9c78f003e8747112a273)
+++ uspace/srv/hid/display/test/display.c	(revision 4fbdc3d4f4d1d992db668ffd58dad07f9d4a07db)
@@ -309,7 +309,7 @@
 }
 
-/** Test ds_display_post_pos_event() with click on window switches focus
+/** Test ds_display_post_ptd_event() with click on window switches focus
  */
-PCUT_TEST(display_post_pos_event_wnd_switch)
+PCUT_TEST(display_post_ptd_event_wnd_switch)
 {
 	ds_display_t *disp;
@@ -317,5 +317,5 @@
 	ds_client_t *client;
 	ds_window_t *w0, *w1;
-	pos_event_t event;
+	ptd_event_t event;
 	bool called_cb = false;
 	errno_t rc;
@@ -342,14 +342,18 @@
 	w1->dpos.y = 400;
 
+	PCUT_ASSERT_FALSE(called_cb);
+
 	ds_seat_set_focus(seat, w0);
 
-	event.type = POS_PRESS;
+	event.type = PTD_MOVE;
+	event.dmove.x = 400;
+	event.dmove.y = 400;
+	rc = ds_display_post_ptd_event(disp, &event);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_FALSE(called_cb);
+
+	event.type = PTD_PRESS;
 	event.btn_num = 1;
-
-	PCUT_ASSERT_FALSE(called_cb);
-
-	event.hpos = 400;
-	event.vpos = 400;
-	rc = ds_display_post_pos_event(disp, &event);
+	rc = ds_display_post_ptd_event(disp, &event);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 	PCUT_ASSERT_FALSE(called_cb);
@@ -357,7 +361,14 @@
 	PCUT_ASSERT_EQUALS(w1, seat->focus);
 
-	event.hpos = 10;
-	event.vpos = 10;
-	rc = ds_display_post_pos_event(disp, &event);
+	event.type = PTD_MOVE;
+	event.dmove.x = -400;
+	event.dmove.y = -400;
+	rc = ds_display_post_ptd_event(disp, &event);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_FALSE(called_cb);
+
+	event.type = PTD_PRESS;
+	event.btn_num = 1;
+	rc = ds_display_post_ptd_event(disp, &event);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 	PCUT_ASSERT_FALSE(called_cb);
Index: uspace/srv/hid/display/types/display/ptd_event.h
===================================================================
--- uspace/srv/hid/display/types/display/ptd_event.h	(revision 4fbdc3d4f4d1d992db668ffd58dad07f9d4a07db)
+++ uspace/srv/hid/display/types/display/ptd_event.h	(revision 4fbdc3d4f4d1d992db668ffd58dad07f9d4a07db)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 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 TYPES_DISPLAY_PTD_EVENT_H
+#define TYPES_DISPLAY_PTD_EVENT_H
+
+#include <gfx/coord.h>
+
+typedef enum {
+	PTD_MOVE,
+	PTD_PRESS,
+	PTD_RELEASE
+} ptd_event_type_t;
+
+/** Pointing device event */
+typedef struct {
+	ptd_event_type_t type;
+	/** Button number for PTD_PRESS or PTD_RELEASE */
+	int btn_num;
+	/** Relative move vector for PTD_MOVE */
+	gfx_coord2_t dmove;
+} ptd_event_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/hid/display/types/display/seat.h
===================================================================
--- uspace/srv/hid/display/types/display/seat.h	(revision 02f45748527ff6b6574d9c78f003e8747112a273)
+++ uspace/srv/hid/display/types/display/seat.h	(revision 4fbdc3d4f4d1d992db668ffd58dad07f9d4a07db)
@@ -38,4 +38,5 @@
 
 #include <adt/list.h>
+#include <gfx/coord.h>
 
 /** Display server seat */
@@ -47,4 +48,6 @@
 	/** Window this seat is focused on */
 	struct ds_window *focus;
+	/** Pointer position */
+	gfx_coord2_t pntpos;
 } ds_seat_t;
 
