Index: uspace/app/disp/disp.c
===================================================================
--- uspace/app/disp/disp.c	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
+++ uspace/app/disp/disp.c	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2023 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 disp
+ * @{
+ */
+/** @file Display configuration utility.
+ *
+ * Configures the display service.
+ */
+
+#include <errno.h>
+#include <dispcfg.h>
+#include <io/table.h>
+#include <loc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <str.h>
+#include <str_error.h>
+
+#define NAME  "disp"
+
+static void print_syntax(void)
+{
+	printf("%s: Display configuration utility.\n", NAME);
+	printf("Syntax:\n");
+	printf("  %s list-seat\n", NAME);
+	printf("  %s create-seat <name>\n", NAME);
+	printf("  %s delete-seat <name>\n", NAME);
+}
+
+/** Find seat by name.
+ *
+ * @param dispcfg Display configuration
+ * @param name Seat name
+ * @param rseat_id Place to store seat ID
+ * @return EOK on success or an error code
+ */
+static errno_t seat_find_by_name(dispcfg_t *dispcfg, const char *name,
+    sysarg_t *rseat_id)
+{
+	dispcfg_seat_list_t *seat_list;
+	dispcfg_seat_info_t *sinfo;
+
+	size_t i;
+	errno_t rc;
+
+	rc = dispcfg_get_seat_list(dispcfg, &seat_list);
+	if (rc != EOK) {
+		printf(NAME ": Failed getting seat list.\n");
+		return rc;
+	}
+
+	for (i = 0; i < seat_list->nseats; i++) {
+		rc = dispcfg_get_seat_info(dispcfg, seat_list->seats[i],
+		    &sinfo);
+		if (rc != EOK)
+			continue;
+
+		if (str_cmp(sinfo->name, name) == 0) {
+			*rseat_id = seat_list->seats[i];
+			dispcfg_free_seat_info(sinfo);
+			return EOK;
+		}
+
+		dispcfg_free_seat_info(sinfo);
+	}
+
+	return ENOENT;
+}
+
+/** Create seat subcommand.
+ *
+ * @param dcfg_svc Display configuration service name
+ * @param argc Number of arguments
+ * @param argv Arguments
+ * @return EOK on success or an erro code
+ */
+static errno_t create_seat(const char *dcfg_svc, int argc, char *argv[])
+{
+	dispcfg_t *dispcfg;
+	char *seat_name;
+	sysarg_t seat_id;
+	errno_t rc;
+
+	if (argc < 1) {
+		printf(NAME ": Missing arguments.\n");
+		print_syntax();
+		return EINVAL;
+	}
+
+	if (argc > 1) {
+		printf(NAME ": Too many arguments.\n");
+		print_syntax();
+		return EINVAL;
+	}
+
+	seat_name = argv[0];
+
+	rc = dispcfg_open(dcfg_svc, NULL, NULL, &dispcfg);
+	if (rc != EOK) {
+		printf(NAME ": Failed connecting to display configuration "
+		    "service: %s.\n", str_error(rc));
+		return rc;
+	}
+
+	rc = dispcfg_seat_create(dispcfg, seat_name, &seat_id);
+	if (rc != EOK) {
+		printf(NAME ": Failed creating seat '%s' (%s)\n",
+		    seat_name, str_error(rc));
+		dispcfg_close(dispcfg);
+		return EIO;
+	}
+
+	(void)seat_id;
+	dispcfg_close(dispcfg);
+	return EOK;
+}
+
+/** Delete seat subcommand.
+ *
+ * @param dcfg_svc Display configuration service name
+ * @param argc Number of arguments
+ * @param argv Arguments
+ * @return EOK on success or an erro code
+ */
+static errno_t delete_seat(const char *dcfg_svc, int argc, char *argv[])
+{
+	dispcfg_t *dispcfg;
+	char *seat_name;
+	sysarg_t seat_id;
+	errno_t rc;
+
+	if (argc < 1) {
+		printf(NAME ": Missing arguments.\n");
+		print_syntax();
+		return EINVAL;
+	}
+
+	if (argc > 1) {
+		printf(NAME ": Too many arguments.\n");
+		print_syntax();
+		return EINVAL;
+	}
+
+	seat_name = argv[0];
+
+	rc = dispcfg_open(dcfg_svc, NULL, NULL, &dispcfg);
+	if (rc != EOK) {
+		printf(NAME ": Failed connecting to display configuration "
+		    "service: %s.\n", str_error(rc));
+		return rc;
+	}
+
+	rc = seat_find_by_name(dispcfg, seat_name, &seat_id);
+	if (rc != EOK) {
+		printf(NAME ": Seat '%s' not found.\n", seat_name);
+		dispcfg_close(dispcfg);
+		return ENOENT;
+	}
+
+	rc = dispcfg_seat_delete(dispcfg, seat_id);
+	if (rc != EOK) {
+		printf(NAME ": Failed deleting seat '%s': %s\n", seat_name,
+		    str_error(rc));
+		dispcfg_close(dispcfg);
+		return EIO;
+	}
+
+	dispcfg_close(dispcfg);
+	return EOK;
+}
+
+/** List seats subcommand.
+ *
+ * @param dcfg_svc Display configuration service name
+ * @param argc Number of arguments
+ * @param argv Arguments
+ * @return EOK on success or an erro code
+ */
+static errno_t list_seat(const char *dcfg_svc, int argc, char *argv[])
+{
+	dispcfg_t *dispcfg;
+	dispcfg_seat_list_t *seat_list;
+	dispcfg_seat_info_t *sinfo;
+	table_t *table = NULL;
+	size_t i;
+	errno_t rc;
+
+	if (argc > 0) {
+		printf(NAME ": Too many arguments.\n");
+		print_syntax();
+		return EINVAL;
+	}
+
+	rc = dispcfg_open(dcfg_svc, NULL, NULL, &dispcfg);
+	if (rc != EOK) {
+		printf(NAME ": Failed connecting to display configuration "
+		    "service: %s.\n", str_error(rc));
+		return rc;
+	}
+
+	rc = dispcfg_get_seat_list(dispcfg, &seat_list);
+	if (rc != EOK) {
+		printf(NAME ": Failed getting seat list.\n");
+		dispcfg_close(dispcfg);
+		return rc;
+	}
+
+	rc = table_create(&table);
+	if (rc != EOK) {
+		printf("Memory allocation failed.\n");
+		dispcfg_close(dispcfg);
+		goto out;
+	}
+
+	table_header_row(table);
+	table_printf(table, "Seat Name\n");
+
+	for (i = 0; i < seat_list->nseats; i++) {
+		rc = dispcfg_get_seat_info(dispcfg, seat_list->seats[i],
+		    &sinfo);
+		if (rc != EOK) {
+			printf("Failed getting properties of seat %zu.\n",
+			    (size_t)seat_list->seats[i]);
+			continue;
+		}
+
+		table_printf(table, "%s\n", sinfo->name);
+
+		dispcfg_free_seat_info(sinfo);
+	}
+
+	if (seat_list->nseats != 0) {
+		rc = table_print_out(table, stdout);
+		if (rc != EOK) {
+			printf("Error printing table.\n");
+			goto out;
+		}
+	}
+
+	rc = EOK;
+out:
+	table_destroy(table);
+	dispcfg_close(dispcfg);
+	return rc;
+}
+
+int main(int argc, char *argv[])
+{
+	const char *dispcfg_svc = DISPCFG_DEFAULT;
+	errno_t rc;
+
+	if (argc < 2 || str_cmp(argv[1], "-h") == 0) {
+		print_syntax();
+		return 0;
+	}
+
+	if (str_cmp(argv[1], "list-seat") == 0) {
+		rc = list_seat(dispcfg_svc, argc - 2, argv + 2);
+		if (rc != EOK)
+			return 1;
+	} else if (str_cmp(argv[1], "create-seat") == 0) {
+		rc = create_seat(dispcfg_svc, argc - 2, argv + 2);
+		if (rc != EOK)
+			return 1;
+	} else if (str_cmp(argv[1], "delete-seat") == 0) {
+		rc = delete_seat(dispcfg_svc, argc - 2, argv + 2);
+		if (rc != EOK)
+			return 1;
+	} else {
+		printf(NAME ": Unknown command '%s'.\n", argv[1]);
+		print_syntax();
+		return 1;
+	}
+
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/app/disp/doc/doxygroups.h
===================================================================
--- uspace/app/disp/doc/doxygroups.h	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
+++ uspace/app/disp/doc/doxygroups.h	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -0,0 +1,4 @@
+/** @addtogroup disp disp
+ * @brief Display configuration utility
+ * @ingroup apps
+ */
Index: uspace/app/disp/meson.build
===================================================================
--- uspace/app/disp/meson.build	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
+++ uspace/app/disp/meson.build	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -0,0 +1,30 @@
+#
+# Copyright (c) 2023 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.
+#
+
+deps = [ 'dispcfg' ]
+src = files('disp.c')
Index: uspace/app/meson.build
===================================================================
--- uspace/app/meson.build	(revision e04b72d69126c4074644c19c920bf771d9c72a1e)
+++ uspace/app/meson.build	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -39,4 +39,5 @@
 	'devctl',
 	'df',
+	'disp',
 	'dnscfg',
 	'dnsres',
Index: uspace/srv/hid/display/cfgclient.c
===================================================================
--- uspace/srv/hid/display/cfgclient.c	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
+++ uspace/srv/hid/display/cfgclient.c	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2023 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 CFG client
+ */
+
+#include <adt/list.h>
+#include <errno.h>
+#include <io/log.h>
+#include <stdlib.h>
+#include "cfgclient.h"
+#include "display.h"
+#include "window.h"
+
+/** Create CFG client.
+ *
+ * @param display Parent display
+ * @param cb CFG client callbacks
+ * @param cb_arg Callback argument
+ * @param rcfgclient Place to store pointer to new CFG client.
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t ds_cfgclient_create(ds_display_t *display, ds_cfgclient_cb_t *cb,
+    void *cb_arg, ds_cfgclient_t **rcfgclient)
+{
+	ds_cfgclient_t *cfgclient;
+
+	cfgclient = calloc(1, sizeof(ds_cfgclient_t));
+	if (cfgclient == NULL)
+		return ENOMEM;
+
+	list_initialize(&cfgclient->events);
+	cfgclient->cb = cb;
+	cfgclient->cb_arg = cb_arg;
+
+	ds_display_add_cfgclient(display, cfgclient);
+
+	*rcfgclient = cfgclient;
+	return EOK;
+}
+
+/** Destroy CFG client.
+ *
+ * @param cfgclient CFG client
+ */
+void ds_cfgclient_destroy(ds_cfgclient_t *cfgclient)
+{
+	ds_cfgclient_purge_events(cfgclient);
+	ds_display_remove_cfgclient(cfgclient);
+	free(cfgclient);
+}
+
+/** Get next event from CFG client event queue.
+ *
+ * @param cfgclient CFG client
+ * @param event Place to store event
+ * @return EOK on success, ENOENT if event queue is empty
+ */
+errno_t ds_cfgclient_get_event(ds_cfgclient_t *cfgclient, dispcfg_ev_t *event)
+{
+	link_t *link;
+	ds_cfgclient_ev_t *wevent;
+
+	link = list_first(&cfgclient->events);
+	if (link == NULL)
+		return ENOENT;
+
+	wevent = list_get_instance(link, ds_cfgclient_ev_t, levents);
+	list_remove(link);
+
+	*event = wevent->event;
+	free(wevent);
+	return EOK;
+}
+
+/** Purge events from CFG client event queue.
+ *
+ * @param client Client
+ */
+void ds_cfgclient_purge_events(ds_cfgclient_t *cfgclient)
+{
+	link_t *cur;
+	link_t *next;
+	ds_cfgclient_ev_t *wevent;
+
+	cur = list_first(&cfgclient->events);
+	while (cur != NULL) {
+		next = list_next(cur, &cfgclient->events);
+		wevent = list_get_instance(cur, ds_cfgclient_ev_t, levents);
+
+		list_remove(cur);
+		free(wevent);
+		cur = next;
+	}
+}
+
+/** Post seat added event to the CFG client's message queue.
+ *
+ * @param cfgclient CFG client
+ * @param seat_id Seat ID of the added seat
+ *
+ * @return EOK on success or an error code
+ */
+errno_t ds_cfgclient_post_seat_added_event(ds_cfgclient_t *cfgclient,
+    sysarg_t seat_id)
+{
+	ds_cfgclient_ev_t *sevent;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "cfgclient_pos_seat_added_event "
+	    "cfgclient=%p seat_id=%zu\n", (void *)cfgclient, seat_id);
+
+	sevent = calloc(1, sizeof(ds_cfgclient_ev_t));
+	if (sevent == NULL)
+		return ENOMEM;
+
+	sevent->cfgclient = cfgclient;
+	sevent->event.etype = dcev_seat_added;
+	sevent->event.seat_id = seat_id;
+	list_append(&sevent->levents, &cfgclient->events);
+
+	/* Notify the client */
+	// TODO Do not send more than once until client drains the queue
+	if (cfgclient->cb != NULL && cfgclient->cb->ev_pending != NULL)
+		cfgclient->cb->ev_pending(cfgclient->cb_arg);
+
+	return EOK;
+}
+
+/** Post seat removed event to the CFG client's message queue.
+ *
+ * @param cfgclient CFG client
+ * @param seat_id Seat ID of the added seat
+ *
+ * @return EOK on success or an error code
+ */
+errno_t ds_cfgclient_post_seat_removed_event(ds_cfgclient_t *cfgclient,
+    sysarg_t seat_id)
+{
+	ds_cfgclient_ev_t *sevent;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "cfgclient_pos_seat_removed_event "
+	    "cfgclient=%p seat_id=%zu\n", (void *)cfgclient, seat_id);
+
+	sevent = calloc(1, sizeof(ds_cfgclient_ev_t));
+	if (sevent == NULL)
+		return ENOMEM;
+
+	sevent->cfgclient = cfgclient;
+	sevent->event.etype = dcev_seat_removed;
+	sevent->event.seat_id = seat_id;
+	list_append(&sevent->levents, &cfgclient->events);
+
+	/* Notify the client */
+	// TODO Do not send more than once until client drains the queue
+	if (cfgclient->cb != NULL && cfgclient->cb->ev_pending != NULL)
+		cfgclient->cb->ev_pending(cfgclient->cb_arg);
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/srv/hid/display/cfgclient.h
===================================================================
--- uspace/srv/hid/display/cfgclient.h	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
+++ uspace/srv/hid/display/cfgclient.h	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2023 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 CFG client
+ */
+
+#ifndef CFGCLIENT_H
+#define CFGCLIENT_H
+
+#include <errno.h>
+#include <dispcfg.h>
+#include "types/display/display.h"
+#include "types/display/cfgclient.h"
+
+extern errno_t ds_cfgclient_create(ds_display_t *, ds_cfgclient_cb_t *, void *,
+    ds_cfgclient_t **);
+extern void ds_cfgclient_destroy(ds_cfgclient_t *);
+extern errno_t ds_cfgclient_get_event(ds_cfgclient_t *, dispcfg_ev_t *);
+extern errno_t ds_cfgclient_post_seat_added_event(ds_cfgclient_t *, sysarg_t);
+extern errno_t ds_cfgclient_post_seat_removed_event(ds_cfgclient_t *, sysarg_t);
+extern void ds_cfgclient_purge_events(ds_cfgclient_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/hid/display/cfgops.c
===================================================================
--- uspace/srv/hid/display/cfgops.c	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
+++ uspace/srv/hid/display/cfgops.c	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2023 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 configuration ops implementation
+ */
+
+#include <errno.h>
+#include <io/log.h>
+#include <stdlib.h>
+#include <str.h>
+#include <dispcfg_srv.h>
+#include "display.h"
+#include "seat.h"
+#include "cfgclient.h"
+
+static errno_t dispc_get_seat_list(void *, dispcfg_seat_list_t **);
+static errno_t dispc_get_seat_info(void *, sysarg_t, dispcfg_seat_info_t **);
+static errno_t dispc_seat_create(void *, const char *, sysarg_t *);
+static errno_t dispc_seat_delete(void *, sysarg_t);
+static errno_t dispc_get_event(void *, dispcfg_ev_t *);
+
+dispcfg_ops_t dispcfg_srv_ops = {
+	.get_seat_list = dispc_get_seat_list,
+	.get_seat_info = dispc_get_seat_info,
+	.seat_create = dispc_seat_create,
+	.seat_delete = dispc_seat_delete,
+	.get_event = dispc_get_event,
+};
+
+/** Get seat list.
+ *
+ * @param arg Argument (CFG client)
+ * @param rlist Place to store pointer to new list
+ * @return EOK on success or an error code
+ */
+static errno_t dispc_get_seat_list(void *arg, dispcfg_seat_list_t **rlist)
+{
+	ds_cfgclient_t *cfgclient = (ds_cfgclient_t *)arg;
+	dispcfg_seat_list_t *list;
+	ds_seat_t *seat;
+	unsigned i;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dispcfg_get_seat_list()");
+
+	list = calloc(1, sizeof(dispcfg_seat_list_t));
+	if (list == NULL)
+		return ENOMEM;
+
+	ds_display_lock(cfgclient->display);
+
+	/* Count the number of seats */
+	list->nseats = 0;
+	seat = ds_display_first_seat(cfgclient->display);
+	while (seat != NULL) {
+		++list->nseats;
+		seat = ds_display_next_seat(seat);
+	}
+
+	/* Allocate array for seat IDs */
+	list->seats = calloc(list->nseats, sizeof(sysarg_t));
+	if (list->seats == NULL) {
+		ds_display_unlock(cfgclient->display);
+		free(list);
+		return ENOMEM;
+	}
+
+	/* Fill in seat IDs */
+	i = 0;
+	seat = ds_display_first_seat(cfgclient->display);
+	while (seat != NULL) {
+		list->seats[i++] = seat->id;
+		seat = ds_display_next_seat(seat);
+	}
+
+	ds_display_unlock(cfgclient->display);
+	*rlist = list;
+	return EOK;
+}
+
+/** Get seat information.
+ *
+ * @param arg Argument (CFG client)
+ * @param seat_id Seat ID
+ * @param rinfo Place to store pointer to new seat information structure
+ * @return EOK on success or an error code
+ */
+static errno_t dispc_get_seat_info(void *arg, sysarg_t seat_id,
+    dispcfg_seat_info_t **rinfo)
+{
+	ds_cfgclient_t *cfgclient = (ds_cfgclient_t *)arg;
+	ds_seat_t *seat;
+	dispcfg_seat_info_t *info;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dispcfg_get_seat_info()");
+
+	ds_display_lock(cfgclient->display);
+	seat = ds_display_find_seat(cfgclient->display, seat_id);
+	if (seat == NULL) {
+		ds_display_unlock(cfgclient->display);
+		return ENOENT;
+	}
+
+	info = calloc(1, sizeof(dispcfg_seat_info_t));
+	if (info == NULL) {
+		ds_display_unlock(cfgclient->display);
+		return ENOMEM;
+	}
+
+	(void)seat;
+	info->name = str_dup(seat->name);
+	if (info->name == NULL) {
+		ds_display_unlock(cfgclient->display);
+		free(info);
+		return ENOMEM;
+	}
+
+	ds_display_unlock(cfgclient->display);
+	*rinfo = info;
+	return EOK;
+}
+
+/** Create seat.
+ *
+ * @param arg Argument (CFG client)
+ * @param name Seat name
+ * @param rseat_id Place to store ID of the new seat
+ * @return EOK on success or an error code
+ */
+static errno_t dispc_seat_create(void *arg, const char *name,
+    sysarg_t *rseat_id)
+{
+	ds_cfgclient_t *cfgclient = (ds_cfgclient_t *)arg;
+	ds_seat_t *seat;
+	errno_t rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dispcfg_seat_create()");
+
+	ds_display_lock(cfgclient->display);
+
+	rc = ds_seat_create(cfgclient->display, name, &seat);
+	if (rc != EOK) {
+		ds_display_unlock(cfgclient->display);
+		return rc;
+	}
+
+	(void) ds_display_paint(cfgclient->display, NULL);
+	ds_display_unlock(cfgclient->display);
+
+	*rseat_id = seat->id;
+	return EOK;
+}
+
+/** Delete seat.
+ *
+ * @param arg Argument (CFG client)
+ * @param seat_id Seat ID
+ * @return EOK on success or an error code
+ */
+static errno_t dispc_seat_delete(void *arg, sysarg_t seat_id)
+{
+	ds_cfgclient_t *cfgclient = (ds_cfgclient_t *)arg;
+	ds_seat_t *seat;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dispcfg_seat_delete()");
+
+	ds_display_lock(cfgclient->display);
+	seat = ds_display_find_seat(cfgclient->display, seat_id);
+	if (seat == NULL) {
+		ds_display_unlock(cfgclient->display);
+		return ENOENT;
+	}
+
+	ds_seat_destroy(seat);
+
+	(void) ds_display_paint(cfgclient->display, NULL);
+	ds_display_unlock(cfgclient->display);
+	return EOK;
+}
+
+/** Get display configuration event.
+ *
+ * @param arg Argument (CFG client)
+ * @param ev Place to store event
+ * @return EOK on success, ENOENT if there are no events
+ */
+static errno_t dispc_get_event(void *arg, dispcfg_ev_t *ev)
+{
+	ds_cfgclient_t *cfgclient = (ds_cfgclient_t *)arg;
+	errno_t rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dispcfg_get_event()");
+
+	ds_display_lock(cfgclient->display);
+	rc = ds_cfgclient_get_event(cfgclient, ev);
+	ds_display_unlock(cfgclient->display);
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/srv/hid/display/cfgops.h
===================================================================
--- uspace/srv/hid/display/cfgops.h	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
+++ uspace/srv/hid/display/cfgops.h	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2023 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 configuration ops implementation
+ */
+
+#ifndef CFGOPS_H
+#define CFGOPS_H
+
+#include <dispcfg_srv.h>
+
+extern dispcfg_ops_t dispcfg_srv_ops;
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/hid/display/display.c
===================================================================
--- uspace/srv/hid/display/display.c	(revision e04b72d69126c4074644c19c920bf771d9c72a1e)
+++ uspace/srv/hid/display/display.c	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2022 Jiri Svoboda
+ * Copyright (c) 2023 Jiri Svoboda
  * All rights reserved.
  *
@@ -98,4 +98,5 @@
 	list_initialize(&disp->clients);
 	list_initialize(&disp->wmclients);
+	list_initialize(&disp->cfgclients);
 	disp->next_wnd_id = 1;
 	list_initialize(&disp->ddevs);
@@ -120,4 +121,5 @@
 	assert(list_empty(&disp->clients));
 	assert(list_empty(&disp->wmclients));
+	assert(list_empty(&disp->cfgclients));
 	assert(list_empty(&disp->seats));
 	assert(list_empty(&disp->ddevs));
@@ -241,4 +243,28 @@
 	list_remove(&wmclient->lwmclients);
 	wmclient->display = NULL;
+}
+
+/** Add CFG client to display.
+ *
+ * @param disp Display
+ * @param cfgclient CFG client
+ */
+void ds_display_add_cfgclient(ds_display_t *disp, ds_cfgclient_t *cfgclient)
+{
+	assert(cfgclient->display == NULL);
+	assert(!link_used(&cfgclient->lcfgclients));
+
+	cfgclient->display = disp;
+	list_append(&cfgclient->lcfgclients, &disp->cfgclients);
+}
+
+/** Remove CFG client from display.
+ *
+ * @param cfgclient CFG client
+ */
+void ds_display_remove_cfgclient(ds_cfgclient_t *cfgclient)
+{
+	list_remove(&cfgclient->lcfgclients);
+	cfgclient->display = NULL;
 }
 
@@ -525,4 +551,5 @@
 
 	seat->display = disp;
+	seat->id = disp->next_seat_id++;
 	list_append(&seat->lseats, &disp->seats);
 }
@@ -566,4 +593,24 @@
 
 	return list_get_instance(link, ds_seat_t, lseats);
+}
+
+/** Find seat by ID.
+ *
+ * @param display Display
+ * @param id Seat ID
+ */
+ds_seat_t *ds_display_find_seat(ds_display_t *display, ds_seat_id_t id)
+{
+	ds_seat_t *seat;
+
+	seat = ds_display_first_seat(display);
+	while (seat != NULL) {
+		if (seat->id == id)
+			return seat;
+
+		seat = ds_display_next_seat(seat);
+	}
+
+	return NULL;
 }
 
Index: uspace/srv/hid/display/display.h
===================================================================
--- uspace/srv/hid/display/display.h	(revision e04b72d69126c4074644c19c920bf771d9c72a1e)
+++ uspace/srv/hid/display/display.h	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2022 Jiri Svoboda
+ * Copyright (c) 2023 Jiri Svoboda
  * All rights reserved.
  *
@@ -42,4 +42,5 @@
 #include <gfx/coord.h>
 #include <io/kbd_event.h>
+#include "types/display/cfgclient.h"
 #include "types/display/client.h"
 #include "types/display/cursor.h"
@@ -63,4 +64,6 @@
 extern void ds_display_add_wmclient(ds_display_t *, ds_wmclient_t *);
 extern void ds_display_remove_wmclient(ds_wmclient_t *);
+extern void ds_display_add_cfgclient(ds_display_t *, ds_cfgclient_t *);
+extern void ds_display_remove_cfgclient(ds_cfgclient_t *);
 extern ds_wmclient_t *ds_display_first_wmclient(ds_display_t *);
 extern ds_wmclient_t *ds_display_next_wmclient(ds_wmclient_t *);
@@ -81,4 +84,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_find_seat(ds_display_t *, ds_seat_id_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 *);
Index: uspace/srv/hid/display/main.c
===================================================================
--- uspace/srv/hid/display/main.c	(revision e04b72d69126c4074644c19c920bf771d9c72a1e)
+++ uspace/srv/hid/display/main.c	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2022 Jiri Svoboda
+ * Copyright (c) 2023 Jiri Svoboda
  * All rights reserved.
  *
@@ -36,4 +36,5 @@
 #include <async.h>
 #include <disp_srv.h>
+#include <dispcfg_srv.h>
 #include <errno.h>
 #include <gfx/context.h>
@@ -48,4 +49,6 @@
 #include <task.h>
 #include <wndmgt_srv.h>
+#include "cfgclient.h"
+#include "cfgops.h"
 #include "client.h"
 #include "display.h"
@@ -62,6 +65,8 @@
 static void display_client_ev_pending(void *);
 static void display_wmclient_ev_pending(void *);
+static void display_cfgclient_ev_pending(void *);
 static void display_gc_conn(ipc_call_t *, void *);
 static void display_wndmgt_conn(ipc_call_t *, void *);
+static void display_dispcfg_conn(ipc_call_t *, void *);
 
 #ifdef CONFIG_DISP_DOUBLE_BUF
@@ -86,4 +91,8 @@
 };
 
+static ds_cfgclient_cb_t display_cfgclient_cb = {
+	.ev_pending = display_cfgclient_ev_pending
+};
+
 static void display_client_ev_pending(void *arg)
 {
@@ -98,4 +107,11 @@
 
 	wndmgt_srv_ev_pending(srv);
+}
+
+static void display_cfgclient_ev_pending(void *arg)
+{
+	dispcfg_srv_t *srv = (dispcfg_srv_t *) arg;
+
+	dispcfg_srv_ev_pending(srv);
 }
 
@@ -110,4 +126,5 @@
 	port_id_t gc_port;
 	port_id_t wm_port;
+	port_id_t dc_port;
 	errno_t rc;
 
@@ -118,5 +135,5 @@
 		goto error;
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	if (rc != EOK)
 		goto error;
@@ -146,4 +163,9 @@
 	rc = async_create_port(INTERFACE_WNDMGT, display_wndmgt_conn, disp,
 	    &wm_port);
+	if (rc != EOK)
+		goto error;
+
+	rc = async_create_port(INTERFACE_DISPCFG, display_dispcfg_conn, disp,
+	    &dc_port);
 	if (rc != EOK)
 		goto error;
@@ -170,4 +192,5 @@
 	// XXX destroy gc_port
 	// XXX destroy wm_port
+	// XXX destroy dc_port
 #if 0
 	if (disp->input != NULL)
@@ -284,4 +307,34 @@
 }
 
+/** Handle configuration connection to display server */
+static void display_dispcfg_conn(ipc_call_t *icall, void *arg)
+{
+	ds_display_t *disp = (ds_display_t *) arg;
+	errno_t rc;
+	dispcfg_srv_t srv;
+	ds_cfgclient_t *cfgclient = NULL;
+
+	/* Create CFG client object */
+	ds_display_lock(disp);
+	rc = ds_cfgclient_create(disp, &display_cfgclient_cb, &srv, &cfgclient);
+	ds_display_unlock(disp);
+	if (rc != EOK) {
+		async_answer_0(icall, ENOMEM);
+		return;
+	}
+
+	/* Set up protocol structure */
+	dispcfg_srv_initialize(&srv);
+	srv.ops = &dispcfg_srv_ops;
+	srv.arg = cfgclient;
+
+	/* Handle connection */
+	dispcfg_conn(icall, &srv);
+
+	ds_display_lock(disp);
+	ds_cfgclient_destroy(cfgclient);
+	ds_display_unlock(disp);
+}
+
 int main(int argc, char *argv[])
 {
Index: uspace/srv/hid/display/meson.build
===================================================================
--- uspace/srv/hid/display/meson.build	(revision e04b72d69126c4074644c19c920bf771d9c72a1e)
+++ uspace/srv/hid/display/meson.build	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2022 Jiri Svoboda
+# Copyright (c) 2023 Jiri Svoboda
 # All rights reserved.
 #
@@ -27,7 +27,9 @@
 #
 
-deps = [ 'ipcgfx', 'memgfx', 'display', 'ddev', 'wndmgt' ]
+deps = [ 'ipcgfx', 'memgfx', 'display', 'ddev', 'dispcfg', 'wndmgt' ]
 
 src = files(
+	'cfgclient.c',
+	'cfgops.c',
 	'client.c',
 	'clonegc.c',
@@ -47,4 +49,5 @@
 
 test_src = files(
+	'cfgclient.c',
 	'client.c',
 	'clonegc.c',
@@ -56,4 +59,5 @@
 	'window.c',
 	'wmclient.c',
+	'test/cfgclient.c',
 	'test/client.c',
 	'test/clonegc.c',
Index: uspace/srv/hid/display/seat.c
===================================================================
--- uspace/srv/hid/display/seat.c	(revision e04b72d69126c4074644c19c920bf771d9c72a1e)
+++ uspace/srv/hid/display/seat.c	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -39,4 +39,5 @@
 #include <gfx/render.h>
 #include <stdlib.h>
+#include <str.h>
 #include "client.h"
 #include "cursor.h"
@@ -51,10 +52,20 @@
  *
  * @param display Parent display
+ * @param name Seat name
  * @param rseat Place to store pointer to new seat.
  * @return EOK on success, ENOMEM if out of memory
  */
-errno_t ds_seat_create(ds_display_t *display, ds_seat_t **rseat)
+errno_t ds_seat_create(ds_display_t *display, const char *name,
+    ds_seat_t **rseat)
 {
 	ds_seat_t *seat;
+	ds_seat_t *s0;
+
+	s0 = ds_display_first_seat(display);
+	while (s0 != NULL) {
+		if (str_cmp(s0->name, name) == 0)
+			return EEXIST;
+		s0 = ds_display_next_seat(s0);
+	}
 
 	seat = calloc(1, sizeof(ds_seat_t));
@@ -62,4 +73,10 @@
 		return ENOMEM;
 
+	seat->name = str_dup(name);
+	if (seat->name == NULL) {
+		free(seat);
+		return ENOMEM;
+	}
+
 	ds_display_add_seat(display, seat);
 	seat->pntpos.x = 0;
@@ -80,4 +97,5 @@
 {
 	ds_display_remove_seat(seat);
+	free(seat->name);
 	free(seat);
 }
Index: uspace/srv/hid/display/seat.h
===================================================================
--- uspace/srv/hid/display/seat.h	(revision e04b72d69126c4074644c19c920bf771d9c72a1e)
+++ uspace/srv/hid/display/seat.h	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -46,5 +46,5 @@
 #include "types/display/window.h"
 
-extern errno_t ds_seat_create(ds_display_t *, ds_seat_t **);
+extern errno_t ds_seat_create(ds_display_t *, const char *, ds_seat_t **);
 extern void ds_seat_destroy(ds_seat_t *);
 extern void ds_seat_set_focus(ds_seat_t *, ds_window_t *);
Index: uspace/srv/hid/display/test/client.c
===================================================================
--- uspace/srv/hid/display/test/client.c	(revision e04b72d69126c4074644c19c920bf771d9c72a1e)
+++ uspace/srv/hid/display/test/client.c	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2023 Jiri Svoboda
  * All rights reserved.
  *
@@ -93,5 +93,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -144,5 +144,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -192,5 +192,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -250,5 +250,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -309,5 +309,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -377,5 +377,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -445,5 +445,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -514,5 +514,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -572,5 +572,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -618,5 +618,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
Index: uspace/srv/hid/display/test/display.c
===================================================================
--- uspace/srv/hid/display/test/display.c	(revision e04b72d69126c4074644c19c920bf771d9c72a1e)
+++ uspace/srv/hid/display/test/display.c	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2022 Jiri Svoboda
+ * Copyright (c) 2023 Jiri Svoboda
  * All rights reserved.
  *
@@ -32,4 +32,5 @@
 #include <str.h>
 
+#include "../cfgclient.h"
 #include "../client.h"
 #include "../display.h"
@@ -54,4 +55,10 @@
 };
 
+static void test_ds_cfgev_pending(void *);
+
+static ds_cfgclient_cb_t test_ds_cfgclient_cb = {
+	.ev_pending = test_ds_cfgev_pending
+};
+
 static void test_ds_ev_pending(void *arg)
 {
@@ -66,4 +73,10 @@
 }
 
+static void test_ds_cfgev_pending(void *arg)
+{
+	bool *called_cb = (bool *) arg;
+	*called_cb = true;
+}
+
 /** Display creation and destruction. */
 PCUT_TEST(display_create_destroy)
@@ -123,4 +136,21 @@
 
 	ds_wmclient_destroy(wmclient);
+	ds_display_destroy(disp);
+}
+
+/** Basic CFG client operation. */
+PCUT_TEST(display_cfgclient)
+{
+	ds_display_t *disp;
+	ds_cfgclient_t *cfgclient;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, df_none, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_cfgclient_create(disp, &test_ds_cfgclient_cb, NULL, &cfgclient);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ds_cfgclient_destroy(cfgclient);
 	ds_display_destroy(disp);
 }
@@ -145,5 +175,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -216,5 +246,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -287,5 +317,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -331,5 +361,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -372,11 +402,11 @@
 	ds_display_t *disp;
 	ds_seat_t *seat;
-	ds_seat_t *s0, *s1;
-	errno_t rc;
-
-	rc = ds_display_create(NULL, df_none, &disp);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ds_seat_create(disp, &seat);
+	ds_seat_t *s0, *s1, *s2;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, df_none, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -386,4 +416,7 @@
 	s1 = ds_display_next_seat(s0);
 	PCUT_ASSERT_NULL(s1);
+
+	s2 = ds_display_find_seat(disp, seat->id);
+	PCUT_ASSERT_EQUALS(s2, seat);
 
 	ds_seat_destroy(seat);
@@ -413,5 +446,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -461,5 +494,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -529,5 +562,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -614,5 +647,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
Index: uspace/srv/hid/display/test/main.c
===================================================================
--- uspace/srv/hid/display/test/main.c	(revision e04b72d69126c4074644c19c920bf771d9c72a1e)
+++ uspace/srv/hid/display/test/main.c	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2023 Jiri Svoboda
  * All rights reserved.
  *
@@ -31,4 +31,5 @@
 PCUT_INIT;
 
+PCUT_IMPORT(cfgclient);
 PCUT_IMPORT(client);
 PCUT_IMPORT(clonegc);
Index: uspace/srv/hid/display/test/seat.c
===================================================================
--- uspace/srv/hid/display/test/seat.c	(revision e04b72d69126c4074644c19c920bf771d9c72a1e)
+++ uspace/srv/hid/display/test/seat.c	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -70,5 +70,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -110,5 +110,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -155,5 +155,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -196,5 +196,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -245,5 +245,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -295,5 +295,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -341,5 +341,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -382,5 +382,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -431,5 +431,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -475,5 +475,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -528,5 +528,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -592,5 +592,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -688,5 +688,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
Index: uspace/srv/hid/display/test/window.c
===================================================================
--- uspace/srv/hid/display/test/window.c	(revision e04b72d69126c4074644c19c920bf771d9c72a1e)
+++ uspace/srv/hid/display/test/window.c	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -67,5 +67,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -100,5 +100,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -147,5 +147,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -194,5 +194,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -235,5 +235,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -277,5 +277,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -314,5 +314,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -352,5 +352,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -392,5 +392,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -436,5 +436,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -471,5 +471,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -516,5 +516,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -571,5 +571,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -643,5 +643,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -682,5 +682,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -722,5 +722,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -769,5 +769,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -816,5 +816,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -1086,5 +1086,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -1140,5 +1140,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -1185,5 +1185,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -1241,5 +1241,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ds_seat_create(disp, &seat);
+	rc = ds_seat_create(disp, "Alice", &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
Index: uspace/srv/hid/display/types/display/cfgclient.h
===================================================================
--- uspace/srv/hid/display/types/display/cfgclient.h	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
+++ uspace/srv/hid/display/types/display/cfgclient.h	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2023 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 CFG client type
+ */
+
+#ifndef TYPES_DISPLAY_CFGCLIENT_H
+#define TYPES_DISPLAY_CFGCLIENT_H
+
+#include <adt/list.h>
+#include <dispcfg.h>
+
+/** Display server CFG client callbacks */
+typedef struct {
+	void (*ev_pending)(void *);
+} ds_cfgclient_cb_t;
+
+/** Display server WM client */
+typedef struct ds_cfgclient {
+	/** Parent display */
+	struct ds_display *display;
+	/** Callbacks */
+	ds_cfgclient_cb_t *cb;
+	/** Callback argument */
+	void *cb_arg;
+	/** Link to @c display->cfgclients */
+	link_t lcfgclients;
+	/** Event queue (of ds_window_ev_t) */
+	list_t events;
+} ds_cfgclient_t;
+
+/** WM client event queue entry */
+typedef struct {
+	/** Link to event queue */
+	link_t levents;
+	/** WM client to which the event is delivered */
+	ds_cfgclient_t *cfgclient;
+	/** Event */
+	dispcfg_ev_t event;
+} ds_cfgclient_ev_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/hid/display/types/display/display.h
===================================================================
--- uspace/srv/hid/display/types/display/display.h	(revision e04b72d69126c4074644c19c920bf771d9c72a1e)
+++ uspace/srv/hid/display/types/display/display.h	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2022 Jiri Svoboda
+ * Copyright (c) 2023 Jiri Svoboda
  * All rights reserved.
  *
@@ -46,4 +46,5 @@
 #include "cursor.h"
 #include "clonegc.h"
+#include "seat.h"
 #include "window.h"
 
@@ -64,4 +65,6 @@
 	/** WM clients (of ds_wmclient_t) */
 	list_t wmclients;
+	/** CFG clients (of ds_cfgclient_t) */
+	list_t cfgclients;
 
 	/** Next ID to assign to a window.
@@ -73,4 +76,6 @@
 	 */
 	ds_wnd_id_t next_wnd_id;
+	/** Next ID to assign to a seat */
+	ds_seat_id_t next_seat_id;
 	/** Input service */
 	input_t *input;
Index: uspace/srv/hid/display/types/display/seat.h
===================================================================
--- uspace/srv/hid/display/types/display/seat.h	(revision e04b72d69126c4074644c19c920bf771d9c72a1e)
+++ uspace/srv/hid/display/types/display/seat.h	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2023 Jiri Svoboda
  * All rights reserved.
  *
@@ -40,4 +40,6 @@
 #include <gfx/coord.h>
 
+typedef sysarg_t ds_seat_id_t;
+
 /** Display server seat */
 typedef struct ds_seat {
@@ -46,4 +48,8 @@
 	/** Link to display->seats */
 	link_t lseats;
+	/** Seat ID */
+	ds_seat_id_t id;
+	/** Seat name */
+	char *name;
 	/** Window this seat is focused on */
 	struct ds_window *focus;
Index: uspace/srv/hid/display/wmclient.c
===================================================================
--- uspace/srv/hid/display/wmclient.c	(revision e04b72d69126c4074644c19c920bf771d9c72a1e)
+++ uspace/srv/hid/display/wmclient.c	(revision d8503fd3802d7d38f5bc88d114fa216baaefc079)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2022 Jiri Svoboda
+ * Copyright (c) 2023 Jiri Svoboda
  * All rights reserved.
  *
@@ -36,4 +36,5 @@
 #include <adt/list.h>
 #include <errno.h>
+#include <io/log.h>
 #include <stdlib.h>
 #include "display.h"
@@ -122,5 +123,5 @@
 	}
 }
-#include <io/log.h>
+
 /** Post window added event to the WM client's message queue.
  *
