Index: uspace/app/taskbar/main.c
===================================================================
--- uspace/app/taskbar/main.c	(revision 0761448df8f6e5a4d681a24b634ffc5ddbf890ad)
+++ uspace/app/taskbar/main.c	(revision 176632602be72a79118566eeda781805d4ce61ab)
@@ -46,4 +46,5 @@
 {
 	const char *display_spec = UI_ANY_DEFAULT;
+	const char *wndmgt_svc = WNDMGT_DEFAULT;
 	taskbar_t *taskbar;
 	errno_t rc;
@@ -61,4 +62,13 @@
 
 			display_spec = argv[i++];
+		} else if (str_cmp(argv[i], "-w") == 0) {
+			++i;
+			if (i >= argc) {
+				printf("Argument missing.\n");
+				print_syntax();
+				return 1;
+			}
+
+			wndmgt_svc = argv[i++];
 		} else {
 			printf("Invalid option '%s'.\n", argv[i]);
@@ -73,5 +83,5 @@
 	}
 
-	rc = taskbar_create(display_spec, &taskbar);
+	rc = taskbar_create(display_spec, wndmgt_svc, &taskbar);
 	if (rc != EOK)
 		return 1;
Index: uspace/app/taskbar/meson.build
===================================================================
--- uspace/app/taskbar/meson.build	(revision 0761448df8f6e5a4d681a24b634ffc5ddbf890ad)
+++ uspace/app/taskbar/meson.build	(revision 176632602be72a79118566eeda781805d4ce61ab)
@@ -27,5 +27,5 @@
 #
 
-deps = [ 'ui' ]
+deps = [ 'ui', 'wndmgt' ]
 src = files(
 	'clock.c',
Index: uspace/app/taskbar/taskbar.c
===================================================================
--- uspace/app/taskbar/taskbar.c	(revision 0761448df8f6e5a4d681a24b634ffc5ddbf890ad)
+++ uspace/app/taskbar/taskbar.c	(revision 176632602be72a79118566eeda781805d4ce61ab)
@@ -42,4 +42,5 @@
 #include <ui/ui.h>
 #include <ui/window.h>
+#include <wndmgt.h>
 #include "clock.h"
 #include "taskbar.h"
@@ -67,8 +68,10 @@
  *
  * @param display_spec Display specification
+ * @param wndmgt_svc Window management service (or WNDMGT_DEFAULT)
  * @param rtaskbar Place to store pointer to new task bar
  * @return @c EOK on success or an error coe
  */
-errno_t taskbar_create(const char *display_spec, taskbar_t **rtaskbar)
+errno_t taskbar_create(const char *display_spec, const char *wndmgt_svc,
+    taskbar_t **rtaskbar)
 {
 	ui_wnd_params_t params;
@@ -83,4 +86,10 @@
 		rc = ENOMEM;
 		goto error;
+	}
+
+	if (wndmgt_svc != NULL) {
+		rc = wndmgt_open(wndmgt_svc, NULL, NULL, &taskbar->wndmgt);
+		if (rc != EOK)
+			goto error;
 	}
 
@@ -215,4 +224,6 @@
 	if (taskbar->ui != NULL)
 		ui_destroy(taskbar->ui);
+	if (taskbar->wndmgt != NULL)
+		wndmgt_close(taskbar->wndmgt);
 	return rc;
 
@@ -226,4 +237,6 @@
 	ui_window_destroy(taskbar->window);
 	ui_destroy(taskbar->ui);
+	if (taskbar->wndmgt != NULL)
+		wndmgt_close(taskbar->wndmgt);
 }
 
Index: uspace/app/taskbar/taskbar.h
===================================================================
--- uspace/app/taskbar/taskbar.h	(revision 0761448df8f6e5a4d681a24b634ffc5ddbf890ad)
+++ uspace/app/taskbar/taskbar.h	(revision 176632602be72a79118566eeda781805d4ce61ab)
@@ -38,7 +38,8 @@
 
 #include <errno.h>
+#include <wndmgt.h>
 #include "types/taskbar.h"
 
-extern errno_t taskbar_create(const char *display_spec, taskbar_t **);
+extern errno_t taskbar_create(const char *, const char *, taskbar_t **);
 extern void taskbar_destroy(taskbar_t *);
 
Index: uspace/app/taskbar/test/taskbar.c
===================================================================
--- uspace/app/taskbar/test/taskbar.c	(revision 0761448df8f6e5a4d681a24b634ffc5ddbf890ad)
+++ uspace/app/taskbar/test/taskbar.c	(revision 176632602be72a79118566eeda781805d4ce61ab)
@@ -41,5 +41,5 @@
 	taskbar_t *taskbar;
 
-	rc = taskbar_create(UI_DISPLAY_NULL, &taskbar);
+	rc = taskbar_create(UI_DISPLAY_NULL, NULL, &taskbar);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
Index: uspace/app/taskbar/types/taskbar.h
===================================================================
--- uspace/app/taskbar/types/taskbar.h	(revision 0761448df8f6e5a4d681a24b634ffc5ddbf890ad)
+++ uspace/app/taskbar/types/taskbar.h	(revision 176632602be72a79118566eeda781805d4ce61ab)
@@ -57,4 +57,6 @@
 	/** Clock */
 	taskbar_clock_t *clock;
+	/** Window management service */
+	wndmgt_t *wndmgt;
 } taskbar_t;
 
Index: uspace/lib/wndmgt/src/wndmgt.c
===================================================================
--- uspace/lib/wndmgt/src/wndmgt.c	(revision 0761448df8f6e5a4d681a24b634ffc5ddbf890ad)
+++ uspace/lib/wndmgt/src/wndmgt.c	(revision 176632602be72a79118566eeda781805d4ce61ab)
@@ -169,5 +169,5 @@
 	if (rc != EOK) {
 		async_exchange_end(exch);
-		async_forget(req);
+		async_wait_for(req, &rc);
 		return rc;
 	}
@@ -238,5 +238,5 @@
 	if (rc != EOK) {
 		async_exchange_end(exch);
-		async_forget(req);
+		async_wait_for(req, &rc);
 		return rc;
 	}
Index: uspace/lib/wndmgt/test/wndmgt.c
===================================================================
--- uspace/lib/wndmgt/test/wndmgt.c	(revision 0761448df8f6e5a4d681a24b634ffc5ddbf890ad)
+++ uspace/lib/wndmgt/test/wndmgt.c	(revision 176632602be72a79118566eeda781805d4ce61ab)
@@ -34,4 +34,5 @@
 #include <loc.h>
 #include <pcut/pcut.h>
+#include <str.h>
 #include "../private/wndmgt.h"
 
@@ -81,4 +82,5 @@
 
 	bool get_window_info_called;
+	sysarg_t get_window_info_wnd_id;
 	wndmgt_window_info_t *get_window_info_rinfo;
 
@@ -127,4 +129,166 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 }
+
+/** wndmgt_get_window_list() with server returning error response works */
+PCUT_TEST(get_window_list_failure)
+{
+	errno_t rc;
+	service_id_t sid;
+	wndmgt_t *wndmgt = NULL;
+	wndmgt_window_list_t *list;
+	test_response_t resp;
+
+	async_set_fallback_port_handler(test_wndmgt_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_wndmgt_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_wndmgt_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = wndmgt_open(test_wndmgt_svc, NULL, NULL, &wndmgt);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(wndmgt);
+
+	resp.rc = ENOMEM;
+	resp.get_window_list_called = false;
+
+	rc = wndmgt_get_window_list(wndmgt, &list);
+	PCUT_ASSERT_TRUE(resp.get_window_list_called);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+
+	wndmgt_close(wndmgt);
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** wndmgt_get_window_list() with server returning success response works */
+PCUT_TEST(get_window_list_success)
+{
+	errno_t rc;
+	service_id_t sid;
+	wndmgt_t *wndmgt = NULL;
+	wndmgt_window_list_t *list;
+	test_response_t resp;
+
+	async_set_fallback_port_handler(test_wndmgt_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_wndmgt_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_wndmgt_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = wndmgt_open(test_wndmgt_svc, NULL, NULL, &wndmgt);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(wndmgt);
+
+	resp.rc = EOK;
+	resp.get_window_list_called = false;
+	resp.get_window_list_rlist = calloc(1, sizeof(wndmgt_window_list_t));
+	PCUT_ASSERT_NOT_NULL(resp.get_window_list_rlist);
+	resp.get_window_list_rlist->nwindows = 2;
+	resp.get_window_list_rlist->windows = calloc(2, sizeof(sysarg_t));
+	PCUT_ASSERT_NOT_NULL(resp.get_window_list_rlist->windows);
+	resp.get_window_list_rlist->windows[0] = 42;
+	resp.get_window_list_rlist->windows[1] = 43;
+
+	rc = wndmgt_get_window_list(wndmgt, &list);
+	PCUT_ASSERT_TRUE(resp.get_window_list_called);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+
+	PCUT_ASSERT_INT_EQUALS(2, list->nwindows);
+	PCUT_ASSERT_INT_EQUALS(42, list->windows[0]);
+	PCUT_ASSERT_INT_EQUALS(43, list->windows[1]);
+
+	wndmgt_free_window_list(list);
+	wndmgt_close(wndmgt);
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** wndmgt_get_window_infp() with server returning error response works */
+PCUT_TEST(get_window_info_failure)
+{
+	errno_t rc;
+	service_id_t sid;
+	wndmgt_t *wndmgt = NULL;
+	sysarg_t wnd_id;
+	wndmgt_window_info_t *info;
+	test_response_t resp;
+
+	async_set_fallback_port_handler(test_wndmgt_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_wndmgt_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_wndmgt_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = wndmgt_open(test_wndmgt_svc, NULL, NULL, &wndmgt);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(wndmgt);
+
+	resp.rc = ENOMEM;
+	resp.get_window_info_called = false;
+	wnd_id = 1;
+
+	rc = wndmgt_get_window_info(wndmgt, wnd_id, &info);
+	PCUT_ASSERT_TRUE(resp.get_window_info_called);
+	PCUT_ASSERT_INT_EQUALS(wnd_id, resp.get_window_info_wnd_id);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+
+	wndmgt_close(wndmgt);
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** wndmgt_get_window_info() with server returning success response works */
+PCUT_TEST(get_window_info_success)
+{
+	errno_t rc;
+	service_id_t sid;
+	wndmgt_t *wndmgt = NULL;
+	sysarg_t wnd_id;
+	wndmgt_window_info_t *info;
+	test_response_t resp;
+
+	async_set_fallback_port_handler(test_wndmgt_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_wndmgt_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_wndmgt_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = wndmgt_open(test_wndmgt_svc, NULL, NULL, &wndmgt);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(wndmgt);
+
+	resp.rc = EOK;
+	resp.get_window_info_called = false;
+	resp.get_window_info_rinfo = calloc(1, sizeof(wndmgt_window_info_t));
+	PCUT_ASSERT_NOT_NULL(resp.get_window_info_rinfo);
+	resp.get_window_info_rinfo->caption = str_dup("Hello");
+	PCUT_ASSERT_NOT_NULL(resp.get_window_info_rinfo->caption);
+	wnd_id = 1;
+
+	rc = wndmgt_get_window_info(wndmgt, wnd_id, &info);
+	PCUT_ASSERT_TRUE(resp.get_window_info_called);
+	PCUT_ASSERT_INT_EQUALS(wnd_id, resp.get_window_info_wnd_id);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+
+	PCUT_ASSERT_STR_EQUALS("Hello", info->caption);
+
+	wndmgt_free_window_info(info);
+	wndmgt_close(wndmgt);
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
 /** wndmgt_activate_window() with server returning error response works */
 PCUT_TEST(activate_window_failure)
@@ -199,6 +363,78 @@
 }
 
+/** wndmgt_close_window() with server returning error response works */
+PCUT_TEST(close_window_failure)
+{
+	errno_t rc;
+	service_id_t sid;
+	wndmgt_t *wndmgt = NULL;
+	sysarg_t wnd_id;
+	test_response_t resp;
+
+	async_set_fallback_port_handler(test_wndmgt_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_wndmgt_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_wndmgt_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = wndmgt_open(test_wndmgt_svc, NULL, NULL, &wndmgt);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(wndmgt);
+
+	wnd_id = 42;
+	resp.rc = ENOMEM;
+	resp.close_window_called = false;
+
+	rc = wndmgt_close_window(wndmgt, wnd_id);
+	PCUT_ASSERT_TRUE(resp.close_window_called);
+	PCUT_ASSERT_INT_EQUALS(wnd_id, resp.close_window_wnd_id);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+
+	wndmgt_close(wndmgt);
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** wndmgt_close_window() with server returning success response works */
+PCUT_TEST(close_window_success)
+{
+	errno_t rc;
+	service_id_t sid;
+	wndmgt_t *wndmgt = NULL;
+	sysarg_t wnd_id;
+	test_response_t resp;
+
+	async_set_fallback_port_handler(test_wndmgt_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_wndmgt_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_wndmgt_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = wndmgt_open(test_wndmgt_svc, NULL, NULL, &wndmgt);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(wndmgt);
+
+	wnd_id = 42;
+	resp.rc = EOK;
+	resp.close_window_called = false;
+
+	rc = wndmgt_close_window(wndmgt, wnd_id);
+	PCUT_ASSERT_TRUE(resp.close_window_called);
+	PCUT_ASSERT_INT_EQUALS(wnd_id, resp.close_window_wnd_id);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+
+	wndmgt_close(wndmgt);
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
 /** Window added event can be delivered from server to client callback function */
-PCUT_TEST(window_added_event_deliver)
+PCUT_TEST(window_added_deliver)
 {
 	errno_t rc;
@@ -247,5 +483,5 @@
 
 /** Window removed event can be delivered from server to client callback function */
-PCUT_TEST(window_removed_event_deliver)
+PCUT_TEST(window_removed_deliver)
 {
 	errno_t rc;
@@ -341,8 +577,9 @@
 	test_response_t *resp = (test_response_t *) arg;
 
+	resp->get_window_list_called = true;
+
 	if (resp->rc != EOK)
 		return resp->rc;
 
-	resp->get_window_list_called = true;
 	*rlist = resp->get_window_list_rlist;
 	return EOK;
@@ -354,8 +591,10 @@
 	test_response_t *resp = (test_response_t *) arg;
 
+	resp->get_window_info_called = true;
+	resp->get_window_info_wnd_id = wnd_id;
+
 	if (resp->rc != EOK)
 		return resp->rc;
 
-	resp->get_window_info_called = true;
 	*rinfo = resp->get_window_info_rinfo;
 	return EOK;
Index: uspace/srv/hid/display/main.c
===================================================================
--- uspace/srv/hid/display/main.c	(revision 0761448df8f6e5a4d681a24b634ffc5ddbf890ad)
+++ uspace/srv/hid/display/main.c	(revision 176632602be72a79118566eeda781805d4ce61ab)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -47,4 +47,5 @@
 #include <stdio.h>
 #include <task.h>
+#include <wndmgt_srv.h>
 #include "client.h"
 #include "display.h"
@@ -55,7 +56,10 @@
 #include "seat.h"
 #include "window.h"
+#include "wmops.h"
 
 static void display_client_conn(ipc_call_t *, void *);
 static void display_client_ev_pending(void *);
+static void display_gc_conn(ipc_call_t *, void *);
+static void display_wndmgt_conn(ipc_call_t *, void *);
 
 #ifdef CONFIG_DISP_DOUBLE_BUF
@@ -90,4 +94,7 @@
 	ds_output_t *output = NULL;
 	gfx_context_t *gc = NULL;
+	port_id_t disp_port;
+	port_id_t gc_port;
+	port_id_t wm_port;
 	errno_t rc;
 
@@ -115,5 +122,17 @@
 		goto error;
 
-	async_set_fallback_port_handler(display_client_conn, disp);
+	rc = async_create_port(INTERFACE_DISPLAY, display_client_conn, disp,
+	    &disp_port);
+	if (rc != EOK)
+		goto error;
+
+	rc = async_create_port(INTERFACE_GC, display_gc_conn, disp, &gc_port);
+	if (rc != EOK)
+		goto error;
+
+	rc = async_create_port(INTERFACE_WNDMGT, display_wndmgt_conn, disp,
+	    &wm_port);
+	if (rc != EOK)
+		goto error;
 
 	rc = loc_server_register(NAME);
@@ -135,4 +154,7 @@
 	return EOK;
 error:
+	// XXX destroy disp_port
+	// XXX destroy gc_port
+	// XXX destroy wm_port
 #if 0
 	if (disp->input != NULL)
@@ -154,10 +176,7 @@
 {
 	display_srv_t srv;
-	sysarg_t wnd_id;
 	sysarg_t svc_id;
 	ds_client_t *client = NULL;
-	ds_window_t *wnd;
 	ds_display_t *disp = (ds_display_t *) arg;
-	gfx_context_t *gc;
 	errno_t rc;
 
@@ -166,9 +185,5 @@
 	    ipc_get_arg4(icall));
 
-	(void) icall;
-	(void) arg;
-
 	svc_id = ipc_get_arg2(icall);
-	wnd_id = ipc_get_arg3(icall);
 
 	if (svc_id != 0) {
@@ -191,20 +206,49 @@
 		ds_client_destroy(client);
 		ds_display_unlock(disp);
-	} else {
-		/* Window GC connection */
-
-		wnd = ds_display_find_window(disp, wnd_id);
-		if (wnd == NULL) {
-			async_answer_0(icall, ENOENT);
-			return;
-		}
-
-		/*
-		 * XXX We need a way to make sure that the connection does
-		 * not stay active after the window had been destroyed
-		 */
-		gc = ds_window_get_ctx(wnd);
-		gc_conn(icall, gc);
-	}
+	}
+}
+
+/** Handle GC connection to display server */
+static void display_gc_conn(ipc_call_t *icall, void *arg)
+{
+	sysarg_t wnd_id;
+	ds_window_t *wnd;
+	ds_display_t *disp = (ds_display_t *) arg;
+	gfx_context_t *gc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "display_gc_conn arg1=%zu arg2=%zu arg3=%zu arg4=%zu.",
+	    ipc_get_arg1(icall), ipc_get_arg2(icall), ipc_get_arg3(icall),
+	    ipc_get_arg4(icall));
+
+	wnd_id = ipc_get_arg3(icall);
+
+	/* Window GC connection */
+
+	wnd = ds_display_find_window(disp, wnd_id);
+	if (wnd == NULL) {
+		async_answer_0(icall, ENOENT);
+		return;
+	}
+
+	/*
+	 * XXX We need a way to make sure that the connection does
+	 * not stay active after the window had been destroyed
+	 */
+	gc = ds_window_get_ctx(wnd);
+	gc_conn(icall, gc);
+}
+
+/** Handle window management connection to display server */
+static void display_wndmgt_conn(ipc_call_t *icall, void *arg)
+{
+	wndmgt_srv_t srv;
+
+	/* Set up protocol structure */
+	wndmgt_srv_initialize(&srv);
+	srv.ops = &wndmgt_srv_ops;
+	srv.arg = NULL; // XXX
+
+	/* Handle connection */
+	wndmgt_conn(icall, &srv);
 }
 
Index: uspace/srv/hid/display/meson.build
===================================================================
--- uspace/srv/hid/display/meson.build	(revision 0761448df8f6e5a4d681a24b634ffc5ddbf890ad)
+++ uspace/srv/hid/display/meson.build	(revision 176632602be72a79118566eeda781805d4ce61ab)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2019 Jiri Svoboda
+# Copyright (c) 2022 Jiri Svoboda
 # All rights reserved.
 #
@@ -27,5 +27,5 @@
 #
 
-deps = [ 'ipcgfx', 'memgfx', 'display', 'ddev' ]
+deps = [ 'ipcgfx', 'memgfx', 'display', 'ddev', 'wndmgt' ]
 
 src = files(
@@ -42,4 +42,5 @@
 	'seat.c',
 	'window.c',
+	'wmops.c',
 )
 
Index: uspace/srv/hid/display/wmops.c
===================================================================
--- uspace/srv/hid/display/wmops.c	(revision 176632602be72a79118566eeda781805d4ce61ab)
+++ uspace/srv/hid/display/wmops.c	(revision 176632602be72a79118566eeda781805d4ce61ab)
@@ -0,0 +1,137 @@
+/*
+ * 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 Window management ops implementation
+ */
+
+#include <errno.h>
+//#include <io/log.h>
+#include <stdlib.h>
+#include <str.h>
+#include <wndmgt_srv.h>
+//#include "client.h"
+//#include "display.h"
+//#include "dsops.h"
+//#include "seat.h"
+//#include "window.h"
+
+static errno_t dispwm_get_window_list(void *, wndmgt_window_list_t **);
+static errno_t dispwm_get_window_info(void *, sysarg_t, wndmgt_window_info_t **);
+static errno_t dispwm_activate_window(void *, sysarg_t);
+static errno_t dispwm_close_window(void *, sysarg_t);
+static errno_t dispwm_get_event(void *, wndmgt_ev_t *);
+
+wndmgt_ops_t wndmgt_srv_ops = {
+	.get_window_list = dispwm_get_window_list,
+	.get_window_info = dispwm_get_window_info,
+	.activate_window = dispwm_activate_window,
+	.close_window = dispwm_close_window,
+	.get_event = dispwm_get_event,
+};
+
+static errno_t dispwm_get_window_list(void *arg, wndmgt_window_list_t **rlist)
+{
+	wndmgt_window_list_t *list;
+
+	list = calloc(1, sizeof(wndmgt_window_list_t));
+	if (list == NULL)
+		return ENOMEM;
+
+	list->nwindows = 2;
+	list->windows = calloc(2, sizeof(sysarg_t));
+	if (list->windows == NULL) {
+		free(list);
+		return ENOMEM;
+	}
+
+	list->windows[0] = 1;
+	list->windows[1] = 2;
+
+	*rlist = list;
+	return EOK;
+}
+
+static errno_t dispwm_get_window_info(void *arg, sysarg_t wnd_id,
+    wndmgt_window_info_t **rinfo)
+{
+/*	ds_client_t *client = (ds_client_t *) arg;
+	ds_window_t *wnd;
+
+	ds_display_lock(client->display);
+
+	wnd = ds_client_find_window(client, wnd_id);
+	if (wnd == NULL) {
+		ds_display_unlock(client->display);
+		return ENOENT;
+	}
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "disp_window_get_pos()");
+	ds_window_get_pos(wnd, pos);
+	ds_display_unlock(client->display);
+	return EOK;*/
+	wndmgt_window_info_t *info;
+
+	info = calloc(1, sizeof(wndmgt_window_info_t));
+	if (info == NULL)
+		return ENOMEM;
+
+	info->caption = str_dup("Hello");
+	if (info->caption == NULL) {
+		free(info);
+		return ENOMEM;
+	}
+
+	*rinfo = info;
+	return EOK;
+}
+
+static errno_t dispwm_activate_window(void *arg, sysarg_t wnd_id)
+{
+	(void)arg;
+	(void)wnd_id;
+	return EOK;
+}
+
+static errno_t dispwm_close_window(void *arg, sysarg_t wnd_id)
+{
+	(void)arg;
+	(void)wnd_id;
+	return EOK;
+}
+
+static errno_t dispwm_get_event(void *arg, wndmgt_ev_t *ev)
+{
+	return ENOTSUP;
+}
+
+/** @}
+ */
Index: uspace/srv/hid/display/wmops.h
===================================================================
--- uspace/srv/hid/display/wmops.h	(revision 176632602be72a79118566eeda781805d4ce61ab)
+++ uspace/srv/hid/display/wmops.h	(revision 176632602be72a79118566eeda781805d4ce61ab)
@@ -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 Window management ops implementation
+ */
+
+#ifndef WMOPS_H
+#define WMOPS_H
+
+#include <wndmgt_srv.h>
+
+extern wndmgt_ops_t wndmgt_srv_ops;
+
+#endif
+
+/** @}
+ */
