Index: uspace/lib/gfx/include/types/gfx/ops/context.h
===================================================================
--- uspace/lib/gfx/include/types/gfx/ops/context.h	(revision 8e9781f3bb5a151b942fed581c13470de1a2ec2e)
+++ uspace/lib/gfx/include/types/gfx/ops/context.h	(revision a40ae0d786e6716313eec18bc48f28cfe11babcf)
@@ -47,5 +47,5 @@
 /** Graphics context ops */
 typedef struct {
-	/** Set drawing collor */
+	/** Set drawing color */
 	errno_t (*set_color)(void *, gfx_color_t *);
 	/** Fill rectangle using the current drawing color */
Index: uspace/srv/hid/display/seat.c
===================================================================
--- uspace/srv/hid/display/seat.c	(revision 8e9781f3bb5a151b942fed581c13470de1a2ec2e)
+++ uspace/srv/hid/display/seat.c	(revision a40ae0d786e6716313eec18bc48f28cfe11babcf)
@@ -215,4 +215,6 @@
 /** Post pointing device event to the seat
  *
+ * Update pointer position and generate position event.
+ *
  * @param seat Seat
  * @param event Event
@@ -223,21 +225,37 @@
 errno_t ds_seat_post_ptd_event(ds_seat_t *seat, ptd_event_t *event)
 {
+	gfx_coord2_t npos;
 	ds_window_t *wnd;
-	gfx_coord2_t npos;
+	pos_event_t pevent;
+	errno_t rc;
 
 	printf("ds_seat_post_ptd_event\n");
+	wnd = ds_display_window_by_pos(seat->display, &seat->pntpos);
+
 	/* 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 (event->type == PTD_PRESS && event->btn_num == 1) {
+		printf("PTD_PRESS (button = %d)\n", event->btn_num);
 		if (wnd != NULL) {
 			printf("set focus\n");
 			ds_seat_set_focus(seat, wnd);
-			return EOK;
 		}
+	}
+
+	if (event->type == PTD_PRESS || event->type == PTD_RELEASE) {
+		pevent.pos_id = 0;
+		pevent.type = (event->type == PTD_PRESS) ?
+		    POS_PRESS : POS_RELEASE;
+		pevent.btn_num = event->btn_num;
+		pevent.hpos = seat->pntpos.x;
+		pevent.vpos = seat->pntpos.y;
+
+		rc = ds_seat_post_pos_event(seat, &pevent);
+		if (rc != EOK)
+			return rc;
 	}
 
 	if (event->type == PTD_MOVE) {
 		printf("PTD_MOVE\n");
+
 		gfx_coord2_add(&seat->pntpos, &event->dmove, &npos);
 		if (npos.x < 0)
@@ -253,4 +271,15 @@
 		(void) ds_seat_draw_pointer(seat, false);
 		seat->pntpos = npos;
+
+		pevent.pos_id = 0;
+		pevent.type = POS_UPDATE;
+		pevent.btn_num = 0;
+		pevent.hpos = seat->pntpos.x;
+		pevent.vpos = seat->pntpos.y;
+
+		rc = ds_seat_post_pos_event(seat, &pevent);
+		if (rc != EOK)
+			return rc;
+
 		printf("draw pointer\n");
 		(void) ds_seat_draw_pointer(seat, true);
@@ -260,4 +289,37 @@
 }
 
+/** Post position event to seat.
+ *
+ * Deliver event to relevant windows.
+ *
+ * @param seat Seat
+ * @param event Position event
+ */
+errno_t ds_seat_post_pos_event(ds_seat_t *seat, pos_event_t *event)
+{
+	ds_window_t *wnd;
+	errno_t rc;
+
+	printf("ds_seat_post_pos_event\n");
+
+	wnd = ds_display_window_by_pos(seat->display, &seat->pntpos);
+	if (wnd != NULL) {
+		printf("send event to window at pointer position\n");
+		rc = ds_window_post_pos_event(wnd, event);
+		if (rc != EOK)
+			return rc;
+	}
+
+	if (seat->focus != wnd) {
+		printf("send event to focused window\n");
+
+		rc = ds_window_post_pos_event(seat->focus, event);
+		if (rc != EOK)
+			return rc;
+	}
+
+	return EOK;
+}
+
 /** @}
  */
Index: uspace/srv/hid/display/seat.h
===================================================================
--- uspace/srv/hid/display/seat.h	(revision 8e9781f3bb5a151b942fed581c13470de1a2ec2e)
+++ uspace/srv/hid/display/seat.h	(revision a40ae0d786e6716313eec18bc48f28cfe11babcf)
@@ -39,4 +39,5 @@
 #include <errno.h>
 #include <io/kbd_event.h>
+#include <io/pos_event.h>
 #include "types/display/display.h"
 #include "types/display/seat.h"
@@ -50,4 +51,5 @@
 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 *);
+extern errno_t ds_seat_post_pos_event(ds_seat_t *, pos_event_t *);
 
 #endif
Index: uspace/srv/hid/display/test/client.c
===================================================================
--- uspace/srv/hid/display/test/client.c	(revision 8e9781f3bb5a151b942fed581c13470de1a2ec2e)
+++ uspace/srv/hid/display/test/client.c	(revision a40ae0d786e6716313eec18bc48f28cfe11babcf)
@@ -164,5 +164,5 @@
 
 /** Test ds_client_get_event(), ds_client_post_kbd_event(). */
-PCUT_TEST(display_get_post_kbd_event)
+PCUT_TEST(client_get_post_kbd_event)
 {
 	ds_display_t *disp;
Index: uspace/srv/hid/display/test/display.c
===================================================================
--- uspace/srv/hid/display/test/display.c	(revision 8e9781f3bb5a151b942fed581c13470de1a2ec2e)
+++ uspace/srv/hid/display/test/display.c	(revision a40ae0d786e6716313eec18bc48f28cfe11babcf)
@@ -388,6 +388,6 @@
 
 	event.type = PTD_MOVE;
-	event.dmove.x = -400;
-	event.dmove.y = -400;
+	event.dmove.x = -400 + 10;
+	event.dmove.y = -400 + 10;
 	rc = ds_display_post_ptd_event(disp, &event);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
Index: uspace/srv/hid/display/test/seat.c
===================================================================
--- uspace/srv/hid/display/test/seat.c	(revision 8e9781f3bb5a151b942fed581c13470de1a2ec2e)
+++ uspace/srv/hid/display/test/seat.c	(revision a40ae0d786e6716313eec18bc48f28cfe11babcf)
@@ -134,3 +134,199 @@
 }
 
+/** Test ds_seat_post_kbd_event() with Alt-Tab switches focus */
+PCUT_TEST(post_kbd_event_alt_tab)
+{
+	ds_display_t *disp;
+	ds_client_t *client;
+	ds_seat_t *seat;
+	ds_window_t *w0;
+	ds_window_t *w1;
+	display_wnd_params_t params;
+	kbd_event_t event;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, NULL, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, &seat);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_wnd_params_init(&params);
+	params.rect.p0.x = params.rect.p0.y = 0;
+	params.rect.p1.x = params.rect.p1.y = 1;
+
+	rc = ds_window_create(client, &params, &w1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_window_create(client, &params, &w0);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ds_seat_set_focus(seat, w1);
+	PCUT_ASSERT_EQUALS(w1, seat->focus);
+
+	event.type = KEY_PRESS;
+	event.mods = KM_ALT;
+	event.key = KC_TAB;
+	rc = ds_seat_post_kbd_event(seat, &event);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(w0, seat->focus);
+
+	ds_window_destroy(w0);
+	ds_window_destroy(w1);
+	ds_seat_destroy(seat);
+	ds_client_destroy(client);
+	ds_display_destroy(disp);
+}
+
+/** Test ds_seat_post_kbd_event() with regular key press delivers to client queue */
+PCUT_TEST(post_kbd_event_regular)
+{
+	ds_display_t *disp;
+	ds_client_t *client;
+	ds_seat_t *seat;
+	ds_window_t *wnd;
+	display_wnd_params_t params;
+	kbd_event_t event;
+	ds_window_t *rwindow;
+	display_wnd_ev_t revent;
+	bool called_cb = NULL;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, &called_cb, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, &seat);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_wnd_params_init(&params);
+	params.rect.p0.x = params.rect.p0.y = 0;
+	params.rect.p1.x = params.rect.p1.y = 1;
+
+	rc = ds_window_create(client, &params, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ds_seat_set_focus(seat, wnd);
+	PCUT_ASSERT_EQUALS(wnd, seat->focus);
+
+	event.type = KEY_PRESS;
+	event.key = KC_ENTER;
+	event.mods = 0;
+	event.c = L'\0';
+
+	PCUT_ASSERT_FALSE(called_cb);
+
+	rc = ds_client_get_event(client, &rwindow, &revent);
+	PCUT_ASSERT_ERRNO_VAL(ENOENT, rc);
+
+	rc = ds_seat_post_kbd_event(seat, &event);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(called_cb);
+
+	rc = ds_client_get_event(client, &rwindow, &revent);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(wnd, rwindow);
+	PCUT_ASSERT_EQUALS(event.type, revent.kbd_event.type);
+	PCUT_ASSERT_EQUALS(event.key, revent.kbd_event.key);
+	PCUT_ASSERT_EQUALS(event.mods, revent.kbd_event.mods);
+	PCUT_ASSERT_EQUALS(event.c, revent.kbd_event.c);
+
+	rc = ds_client_get_event(client, &rwindow, &revent);
+	PCUT_ASSERT_ERRNO_VAL(ENOENT, rc);
+
+	ds_window_destroy(wnd);
+	ds_client_destroy(client);
+	ds_display_destroy(disp);
+}
+
+/** Test ds_seat_post_ptd_event() with click on window switches focus
+ */
+PCUT_TEST(post_ptd_event_wnd_switch)
+{
+	ds_display_t *disp;
+	ds_seat_t *seat;
+	ds_client_t *client;
+	ds_window_t *w0, *w1;
+	display_wnd_params_t params;
+	ptd_event_t event;
+	bool called_cb = false;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, &seat);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, &called_cb, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_wnd_params_init(&params);
+	params.rect.p0.x = params.rect.p0.y = 0;
+	params.rect.p1.x = params.rect.p1.y = 1;
+
+	rc = ds_window_create(client, &params, &w0);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_window_create(client, &params, &w1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	w0->dpos.x = 10;
+	w0->dpos.y = 10;
+
+	w1->dpos.x = 400;
+	w1->dpos.y = 400;
+
+	PCUT_ASSERT_FALSE(called_cb);
+
+	ds_seat_set_focus(seat, w0);
+
+	event.type = PTD_MOVE;
+	event.dmove.x = 400;
+	event.dmove.y = 400;
+	rc = ds_seat_post_ptd_event(seat, &event);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_FALSE(called_cb);
+
+	event.type = PTD_PRESS;
+	event.btn_num = 1;
+	rc = ds_seat_post_ptd_event(seat, &event);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_FALSE(called_cb);
+
+	PCUT_ASSERT_EQUALS(w1, seat->focus);
+
+	event.type = PTD_MOVE;
+	event.dmove.x = -400 + 10;
+	event.dmove.y = -400 + 10;
+	rc = ds_seat_post_ptd_event(seat, &event);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_FALSE(called_cb);
+
+	event.type = PTD_PRESS;
+	event.btn_num = 1;
+	rc = ds_seat_post_ptd_event(seat, &event);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_FALSE(called_cb);
+
+	PCUT_ASSERT_EQUALS(w0, seat->focus);
+
+	ds_window_destroy(w0);
+	ds_window_destroy(w1);
+	ds_client_destroy(client);
+	ds_seat_destroy(seat);
+	ds_display_destroy(disp);
+}
+
+/** Test ds_seat_post_pos_event() */
+PCUT_TEST(post_pos_event)
+{
+	// XXX
+}
+
 PCUT_EXPORT(seat);
Index: uspace/srv/hid/display/test/window.c
===================================================================
--- uspace/srv/hid/display/test/window.c	(revision 8e9781f3bb5a151b942fed581c13470de1a2ec2e)
+++ uspace/srv/hid/display/test/window.c	(revision a40ae0d786e6716313eec18bc48f28cfe11babcf)
@@ -29,4 +29,5 @@
 #include <disp_srv.h>
 #include <errno.h>
+#include <gfx/context.h>
 #include <pcut/pcut.h>
 #include <stdio.h>
@@ -40,4 +41,12 @@
 
 PCUT_TEST_SUITE(window);
+
+static errno_t dummy_set_color(void *, gfx_color_t *);
+static errno_t dummy_fill_rect(void *, gfx_rect_t *);
+
+static gfx_context_ops_t dummy_ops = {
+	.set_color = dummy_set_color,
+	.fill_rect = dummy_fill_rect
+};
 
 /** Test ds_window_get_ctx(). */
@@ -72,3 +81,76 @@
 }
 
+/** Test ds_window_post_pos_event() */
+PCUT_TEST(window_post_pos_event)
+{
+	gfx_context_t *gc;
+	ds_display_t *disp;
+	ds_client_t *client;
+	ds_window_t *wnd;
+	display_wnd_params_t params;
+	pos_event_t event;
+	errno_t rc;
+
+	rc = gfx_context_new(&dummy_ops, NULL, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_display_create(gc, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, NULL, NULL, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_wnd_params_init(&params);
+	params.rect.p0.x = params.rect.p0.y = 0;
+	params.rect.p1.x = params.rect.p1.y = 1;
+
+	rc = ds_window_create(client, &params, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_INT_EQUALS(dsw_idle, wnd->state);
+
+	event.type = POS_PRESS;
+	event.hpos = 10;
+	event.vpos = 10;
+	rc = ds_window_post_pos_event(wnd, &event);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_INT_EQUALS(dsw_moving, wnd->state);
+
+	event.type = POS_UPDATE;
+	event.hpos = 11;
+	event.vpos = 12;
+	rc = ds_window_post_pos_event(wnd, &event);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_INT_EQUALS(dsw_moving, wnd->state);
+	PCUT_ASSERT_INT_EQUALS(wnd->dpos.x, 0);
+	PCUT_ASSERT_INT_EQUALS(wnd->dpos.y, 0);
+
+	event.type = POS_RELEASE;
+	event.hpos = 13;
+	event.vpos = 14;
+
+	rc = ds_window_post_pos_event(wnd, &event);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_INT_EQUALS(dsw_idle, wnd->state);
+	PCUT_ASSERT_INT_EQUALS(wnd->dpos.x, 3);
+	PCUT_ASSERT_INT_EQUALS(wnd->dpos.y, 4);
+
+	ds_window_destroy(wnd);
+	ds_client_destroy(client);
+	ds_display_destroy(disp);
+}
+
+static errno_t dummy_set_color(void *arg, gfx_color_t *color)
+{
+	return EOK;
+}
+
+static errno_t dummy_fill_rect(void *arg, gfx_rect_t *rect)
+{
+	return EOK;
+}
+
 PCUT_EXPORT(window);
Index: uspace/srv/hid/display/types/display/window.h
===================================================================
--- uspace/srv/hid/display/types/display/window.h	(revision 8e9781f3bb5a151b942fed581c13470de1a2ec2e)
+++ uspace/srv/hid/display/types/display/window.h	(revision a40ae0d786e6716313eec18bc48f28cfe11babcf)
@@ -44,4 +44,12 @@
 typedef sysarg_t ds_wnd_id_t;
 
+/** Window state */
+typedef enum {
+	/** Idle */
+	dsw_idle,
+	/** Moving by mouse drag */
+	dsw_moving
+} ds_window_state_t;
+
 /** Display server window */
 typedef struct ds_window {
@@ -62,4 +70,9 @@
 	/** Graphic context */
 	gfx_context_t *gc;
+
+	/** State */
+	ds_window_state_t state;
+	/** Original position before started to move the window */
+	gfx_coord2_t orig_pos;
 } ds_window_t;
 
Index: uspace/srv/hid/display/window.c
===================================================================
--- uspace/srv/hid/display/window.c	(revision 8e9781f3bb5a151b942fed581c13470de1a2ec2e)
+++ uspace/srv/hid/display/window.c	(revision a40ae0d786e6716313eec18bc48f28cfe11babcf)
@@ -270,4 +270,113 @@
 }
 
+/** Start moving a window by mouse drag.
+ *
+ * @param wnd Window
+ * @param event Button press event
+ */
+static void ds_window_start_move(ds_window_t *wnd, pos_event_t *event)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "ds_window_start_move (%d, %d)",
+	    (int) event->hpos, (int) event->vpos);
+
+	assert(wnd->state == dsw_idle);
+
+	wnd->orig_pos.x = event->hpos;
+	wnd->orig_pos.y = event->vpos;
+	wnd->state = dsw_moving;
+}
+
+/** Finish moving a window by mouse drag.
+ *
+ * @param wnd Window
+ * @param event Button release event
+ */
+static void ds_window_finish_move(ds_window_t *wnd, pos_event_t *event)
+{
+	gfx_coord2_t pos;
+	gfx_coord2_t dmove;
+	gfx_coord2_t nwpos;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "ds_window_finish_move (%d, %d)",
+	    (int) event->hpos, (int) event->vpos);
+
+	assert(wnd->state == dsw_moving);
+	pos.x = event->hpos;
+	pos.y = event->vpos;
+	gfx_coord2_subtract(&pos, &wnd->orig_pos, &dmove);
+
+	gfx_coord2_add(&wnd->dpos, &dmove, &nwpos);
+	wnd->dpos = nwpos;
+	wnd->state = dsw_idle;
+}
+
+/** Update window position when moving by mouse drag.
+ *
+ * @param wnd Window
+ * @param event Position update event
+ */
+static void ds_window_update_move(ds_window_t *wnd, pos_event_t *event)
+{
+	gfx_coord2_t pos;
+	gfx_coord2_t dmove;
+	gfx_coord2_t nwpos;
+	gfx_rect_t drect;
+	gfx_color_t *color;
+	gfx_context_t *gc;
+	errno_t rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "ds_window_update_move (%d, %d)",
+	    (int) event->hpos, (int) event->vpos);
+
+	assert(wnd->state == dsw_moving);
+	pos.x = event->hpos;
+	pos.y = event->vpos;
+	gfx_coord2_subtract(&pos, &wnd->orig_pos, &dmove);
+
+	gfx_coord2_add(&wnd->dpos, &dmove, &nwpos);
+	gfx_rect_translate(&nwpos, &wnd->rect, &drect);
+
+	rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff, &color);
+	if (rc != EOK)
+		return;
+
+	gc = ds_display_get_gc(wnd->display); // XXX
+	if (gc != NULL) {
+		gfx_set_color(ds_display_get_gc(wnd->display), color);
+		gfx_fill_rect(ds_display_get_gc(wnd->display), &drect);
+	}
+
+	gfx_color_delete(color);
+}
+
+/** Post position event to window.
+ *
+ * @param wnd Window
+ * @param event Position event
+ */
+errno_t ds_window_post_pos_event(ds_window_t *wnd, pos_event_t *event)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG,
+	    "ds_window_post_pos_event type=%d pos=%d,%d\n", event->type,
+	    (int) event->hpos, (int) event->vpos);
+
+	if (event->type == POS_PRESS) {
+		if (wnd->state == dsw_idle)
+			ds_window_start_move(wnd, event);
+	}
+
+	if (event->type == POS_RELEASE) {
+		if (wnd->state == dsw_moving)
+			ds_window_finish_move(wnd, event);
+	}
+
+	if (event->type == POS_UPDATE) {
+		if (wnd->state == dsw_moving)
+			ds_window_update_move(wnd, event);
+	}
+
+	return EOK;
+}
+
 /** @}
  */
Index: uspace/srv/hid/display/window.h
===================================================================
--- uspace/srv/hid/display/window.h	(revision 8e9781f3bb5a151b942fed581c13470de1a2ec2e)
+++ uspace/srv/hid/display/window.h	(revision a40ae0d786e6716313eec18bc48f28cfe11babcf)
@@ -38,4 +38,5 @@
 
 #include <errno.h>
+#include <io/pos_event.h>
 #include <types/gfx/context.h>
 #include <types/gfx/ops/context.h>
@@ -50,4 +51,5 @@
 extern void ds_window_destroy(ds_window_t *);
 extern gfx_context_t *ds_window_get_ctx(ds_window_t *);
+extern errno_t ds_window_post_pos_event(ds_window_t *, pos_event_t *);
 
 #endif
