Index: uspace/lib/c/include/io/kbd_event.h
===================================================================
--- uspace/lib/c/include/io/kbd_event.h	(revision a649246077a4f5ce0d12241914a16f80638897d2)
+++ uspace/lib/c/include/io/kbd_event.h	(revision 88d828ea8145b6317bdb4b2f22c2e10fea2fc086)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -39,4 +39,5 @@
 #include <inttypes.h>
 #include <io/keycode.h>
+#include <types/common.h>
 
 typedef enum {
@@ -49,4 +50,7 @@
 	/** List handle */
 	link_t link;
+
+	/** Keyboard device ID */
+	sysarg_t kbd_id;
 
 	/** Press or release event. */
Index: uspace/srv/hid/display/display.c
===================================================================
--- uspace/srv/hid/display/display.c	(revision a649246077a4f5ce0d12241914a16f80638897d2)
+++ uspace/srv/hid/display/display.c	(revision 88d828ea8145b6317bdb4b2f22c2e10fea2fc086)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -445,6 +445,6 @@
 	ds_seat_t *seat;
 
-	// TODO Determine which seat the event belongs to
-	seat = ds_display_first_seat(display);
+	/* Determine which seat the event belongs to */
+	seat = ds_display_seat_by_idev(display, event->kbd_id);
 	if (seat == NULL)
 		return EOK;
@@ -462,6 +462,6 @@
 	ds_seat_t *seat;
 
-	// TODO Determine which seat the event belongs to
-	seat = ds_display_first_seat(display);
+	/* Determine which seat the event belongs to */
+	seat = ds_display_seat_by_idev(display, event->pos_id);
 	if (seat == NULL)
 		return EOK;
@@ -522,4 +522,18 @@
 
 	return list_get_instance(link, ds_seat_t, lseats);
+}
+
+/** Get seat which owns the specified input device.
+ *
+ * @param disp Display
+ * @param idev_id Input device ID
+ * @return Seat which owns device with ID @a idev_id or @c NULL if not found
+ */
+ds_seat_t *ds_display_seat_by_idev(ds_display_t *disp, ds_idev_id_t idev_id)
+{
+	// TODO Multi-seat
+	(void) idev_id;
+
+	return ds_display_first_seat(disp);
 }
 
Index: uspace/srv/hid/display/display.h
===================================================================
--- uspace/srv/hid/display/display.h	(revision a649246077a4f5ce0d12241914a16f80638897d2)
+++ uspace/srv/hid/display/display.h	(revision 88d828ea8145b6317bdb4b2f22c2e10fea2fc086)
@@ -46,4 +46,5 @@
 #include "types/display/ddev.h"
 #include "types/display/display.h"
+#include "types/display/idev.h"
 #include "types/display/ptd_event.h"
 #include "types/display/seat.h"
@@ -79,4 +80,5 @@
 extern ds_seat_t *ds_display_first_seat(ds_display_t *);
 extern ds_seat_t *ds_display_next_seat(ds_seat_t *);
+extern ds_seat_t *ds_display_seat_by_idev(ds_display_t *, ds_idev_id_t);
 extern errno_t ds_display_add_ddev(ds_display_t *, ds_ddev_t *);
 extern void ds_display_remove_ddev(ds_ddev_t *);
Index: uspace/srv/hid/display/input.c
===================================================================
--- uspace/srv/hid/display/input.c	(revision a649246077a4f5ce0d12241914a16f80638897d2)
+++ uspace/srv/hid/display/input.c	(revision 88d828ea8145b6317bdb4b2f22c2e10fea2fc086)
@@ -80,6 +80,5 @@
 	errno_t rc;
 
-	(void)kbd_id;
-
+	event.kbd_id = kbd_id;
 	event.type = type;
 	event.key = key;
Index: uspace/srv/hid/display/test/display.c
===================================================================
--- uspace/srv/hid/display/test/display.c	(revision a649246077a4f5ce0d12241914a16f80638897d2)
+++ uspace/srv/hid/display/test/display.c	(revision 88d828ea8145b6317bdb4b2f22c2e10fea2fc086)
@@ -317,4 +317,10 @@
 }
 
+/** ds_display_seat_by_idev() returns the correct seat. */
+PCUT_TEST(display_seat_by_idev)
+{
+	// XXX TODO
+}
+
 /** Test ds_display_post_kbd_event() delivers event to client callback.
  */
Index: uspace/srv/hid/display/types/display/idev.h
===================================================================
--- uspace/srv/hid/display/types/display/idev.h	(revision 88d828ea8145b6317bdb4b2f22c2e10fea2fc086)
+++ uspace/srv/hid/display/types/display/idev.h	(revision 88d828ea8145b6317bdb4b2f22c2e10fea2fc086)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2022 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 display
+ * @{
+ */
+/**
+ * @file Display server input device type
+ */
+
+#ifndef TYPES_DISPLAY_IDEV_H
+#define TYPES_DISPLAY_IDEV_H
+
+#include <types/common.h>
+
+typedef sysarg_t ds_idev_id_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/hid/display/window.c
===================================================================
--- uspace/srv/hid/display/window.c	(revision a649246077a4f5ce0d12241914a16f80638897d2)
+++ uspace/srv/hid/display/window.c	(revision 88d828ea8145b6317bdb4b2f22c2e10fea2fc086)
@@ -142,4 +142,5 @@
 	}
 
+	// TODO Multi-seat: which seat should own the new window?
 	seat = ds_display_first_seat(client->display);
 
@@ -505,5 +506,5 @@
 	wnd->preview_rect = wnd->rect;
 
-	// XXX Need client to tell us which seat started the resize!
+	// TODO Multi-seat: need client to tell us which seat started the resize!
 	seat = ds_display_first_seat(wnd->display);
 	ctype = display_cursor_from_wrsz(rsztype);
@@ -536,5 +537,5 @@
 	ds_client_post_resize_event(wnd->client, wnd, &nrect);
 
-	// XXX Need to know which seat started the resize!
+	// TODO Multi-seat: Need to know which seat started the resize!
 	seat = ds_display_first_seat(wnd->display);
 	ds_seat_set_wm_cursor(seat, NULL);
Index: uspace/srv/hid/display/wmops.c
===================================================================
--- uspace/srv/hid/display/wmops.c	(revision a649246077a4f5ce0d12241914a16f80638897d2)
+++ uspace/srv/hid/display/wmops.c	(revision 88d828ea8145b6317bdb4b2f22c2e10fea2fc086)
@@ -171,7 +171,10 @@
 	}
 
-	// TODO Multi-seat
-	(void) dev_id;
-	seat = ds_display_first_seat(wnd->display);
+	/* Determine which seat's focus should be changed */
+	seat = ds_display_seat_by_idev(wnd->display, dev_id);
+	if (seat == NULL) {
+		ds_display_unlock(wmclient->display);
+		return ENOENT;
+	}
 
 	/* Switch focus */
