Index: uspace/lib/display/include/disp_srv.h
===================================================================
--- uspace/lib/display/include/disp_srv.h	(revision 35cffeac21acf1766c2acbf62f162a7a62c130f7)
+++ uspace/lib/display/include/disp_srv.h	(revision 7bcd15fa8041cc8c21cbf98b41cba30ec9aa11f3)
@@ -67,4 +67,5 @@
 	errno_t (*window_unmaximize)(void *, sysarg_t);
 	errno_t (*window_set_cursor)(void *, sysarg_t, display_stock_cursor_t);
+	errno_t (*window_set_caption)(void *, sysarg_t, const char *);
 	errno_t (*get_event)(void *, sysarg_t *, display_wnd_ev_t *);
 	errno_t (*get_info)(void *, display_info_t *);
Index: uspace/lib/display/include/display.h
===================================================================
--- uspace/lib/display/include/display.h	(revision 35cffeac21acf1766c2acbf62f162a7a62c130f7)
+++ uspace/lib/display/include/display.h	(revision 7bcd15fa8041cc8c21cbf98b41cba30ec9aa11f3)
@@ -66,4 +66,5 @@
 extern errno_t display_window_set_cursor(display_window_t *,
     display_stock_cursor_t);
+extern errno_t display_window_set_caption(display_window_t *, const char *);
 
 #endif
Index: uspace/lib/display/include/ipc/display.h
===================================================================
--- uspace/lib/display/include/ipc/display.h	(revision 35cffeac21acf1766c2acbf62f162a7a62c130f7)
+++ uspace/lib/display/include/ipc/display.h	(revision 7bcd15fa8041cc8c21cbf98b41cba30ec9aa11f3)
@@ -50,4 +50,5 @@
 	DISPLAY_WINDOW_RESIZE_REQ,
 	DISPLAY_WINDOW_SET_CURSOR,
+	DISPLAY_WINDOW_SET_CAPTION,
 	DISPLAY_WINDOW_UNMAXIMIZE,
 	DISPLAY_GET_EVENT,
Index: uspace/lib/display/include/types/display/wndparams.h
===================================================================
--- uspace/lib/display/include/types/display/wndparams.h	(revision 35cffeac21acf1766c2acbf62f162a7a62c130f7)
+++ uspace/lib/display/include/types/display/wndparams.h	(revision 7bcd15fa8041cc8c21cbf98b41cba30ec9aa11f3)
@@ -58,4 +58,6 @@
 	/** Bounding rectangle */
 	gfx_rect_t rect;
+	/** Window caption */
+	const char *caption;
 	/** Minimum size (when being resized) */
 	gfx_coord2_t min_size;
Index: uspace/lib/display/private/params.h
===================================================================
--- uspace/lib/display/private/params.h	(revision 35cffeac21acf1766c2acbf62f162a7a62c130f7)
+++ uspace/lib/display/private/params.h	(revision 7bcd15fa8041cc8c21cbf98b41cba30ec9aa11f3)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -46,4 +46,21 @@
 } display_wnd_resize_t;
 
+/** Display window parameters encoded for transport
+ *
+ * This omits variable-length fields (window caption).
+ */
+typedef struct {
+	/** Bounding rectangle */
+	gfx_rect_t rect;
+	/** Window caption size in bytes */
+	size_t caption_size;
+	/** Minimum size (when being resized) */
+	gfx_coord2_t min_size;
+	/** Initial position (if flag wndf_setpos is set) */
+	gfx_coord2_t pos;
+	/** Flags */
+	display_wnd_flags_t flags;
+} display_wnd_params_enc_t;
+
 #endif
 
Index: uspace/lib/display/src/disp_srv.c
===================================================================
--- uspace/lib/display/src/disp_srv.c	(revision 35cffeac21acf1766c2acbf62f162a7a62c130f7)
+++ uspace/lib/display/src/disp_srv.c	(revision 7bcd15fa8041cc8c21cbf98b41cba30ec9aa11f3)
@@ -63,5 +63,7 @@
 	sysarg_t wnd_id;
 	ipc_call_t call;
+	display_wnd_params_enc_t eparams;
 	display_wnd_params_t params;
+	char *caption;
 	size_t size;
 	errno_t rc;
@@ -73,12 +75,40 @@
 	}
 
-	if (size != sizeof(display_wnd_params_t)) {
-		async_answer_0(&call, EINVAL);
-		async_answer_0(icall, EINVAL);
-		return;
-	}
-
-	rc = async_data_write_finalize(&call, &params, size);
-	if (rc != EOK) {
+	if (size != sizeof(display_wnd_params_enc_t)) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	rc = async_data_write_finalize(&call, &eparams, size);
+	if (rc != EOK) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	caption = calloc(eparams.caption_size + 1, 1);
+	if (caption == NULL) {
+		async_answer_0(icall, ENOMEM);
+		return;
+	}
+
+	if (!async_data_write_receive(&call, &size)) {
+		free(caption);
+		async_answer_0(&call, EREFUSED);
+		async_answer_0(icall, EREFUSED);
+		return;
+	}
+
+	if (size != eparams.caption_size) {
+		free(caption);
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	rc = async_data_write_finalize(&call, caption, eparams.caption_size);
+	if (rc != EOK) {
+		free(caption);
 		async_answer_0(&call, rc);
 		async_answer_0(icall, rc);
@@ -87,7 +117,15 @@
 
 	if (srv->ops->window_create == NULL) {
-		async_answer_0(icall, ENOTSUP);
-		return;
-	}
+		free(caption);
+		async_answer_0(icall, ENOTSUP);
+		return;
+	}
+
+	/* Decode the parameters from transport */
+	params.rect = eparams.rect;
+	params.caption = caption;
+	params.min_size = eparams.min_size;
+	params.pos = eparams.pos;
+	params.flags = eparams.flags;
 
 	rc = srv->ops->window_create(srv->arg, &params, &wnd_id);
@@ -401,4 +439,47 @@
 	rc = srv->ops->window_set_cursor(srv->arg, wnd_id, cursor);
 	async_answer_0(icall, rc);
+}
+
+static void display_window_set_caption_srv(display_srv_t *srv,
+    ipc_call_t *icall)
+{
+	sysarg_t wnd_id;
+	ipc_call_t call;
+	char *caption;
+	size_t size;
+	errno_t rc;
+
+	wnd_id = ipc_get_arg1(icall);
+
+	if (!async_data_write_receive(&call, &size)) {
+		async_answer_0(&call, EREFUSED);
+		async_answer_0(icall, EREFUSED);
+		return;
+	}
+
+	caption = calloc(size + 1, 1);
+	if (caption == NULL) {
+		async_answer_0(&call, ENOMEM);
+		async_answer_0(icall, ENOMEM);
+		return;
+	}
+
+	rc = async_data_write_finalize(&call, caption, size);
+	if (rc != EOK) {
+		free(caption);
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	if (srv->ops->window_set_caption == NULL) {
+		free(caption);
+		async_answer_0(icall, ENOTSUP);
+		return;
+	}
+
+	rc = srv->ops->window_set_caption(srv->arg, wnd_id, caption);
+	async_answer_0(icall, rc);
+	free(caption);
 }
 
@@ -538,4 +619,7 @@
 		case DISPLAY_WINDOW_SET_CURSOR:
 			display_window_set_cursor_srv(srv, &call);
+			break;
+		case DISPLAY_WINDOW_SET_CAPTION:
+			display_window_set_caption_srv(srv, &call);
 			break;
 		case DISPLAY_GET_EVENT:
Index: uspace/lib/display/src/display.c
===================================================================
--- uspace/lib/display/src/display.c	(revision 35cffeac21acf1766c2acbf62f162a7a62c130f7)
+++ uspace/lib/display/src/display.c	(revision 7bcd15fa8041cc8c21cbf98b41cba30ec9aa11f3)
@@ -38,4 +38,5 @@
 #include <mem.h>
 #include <stdlib.h>
+#include <str.h>
 #include "../private/display.h"
 #include "../private/params.h"
@@ -147,4 +148,5 @@
 {
 	memset(params, 0, sizeof(*params));
+	params->caption = "";
 }
 
@@ -162,8 +164,16 @@
 {
 	display_window_t *window;
-	async_exch_t *exch;
-	aid_t req;
-	ipc_call_t answer;
-	errno_t rc;
+	display_wnd_params_enc_t eparams;
+	async_exch_t *exch;
+	aid_t req;
+	ipc_call_t answer;
+	errno_t rc;
+
+	/* Encode the parameters for transport */
+	eparams.rect = params->rect;
+	eparams.caption_size = str_size(params->caption);
+	eparams.min_size = params->min_size;
+	eparams.pos = params->pos;
+	eparams.flags = params->flags;
 
 	window = calloc(1, sizeof(display_window_t));
@@ -173,5 +183,18 @@
 	exch = async_exchange_begin(display->sess);
 	req = async_send_0(exch, DISPLAY_WINDOW_CREATE, &answer);
-	rc = async_data_write_start(exch, params, sizeof (display_wnd_params_t));
+
+	/* Write fixed fields */
+	rc = async_data_write_start(exch, &eparams,
+	    sizeof (display_wnd_params_enc_t));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		free(window);
+		return rc;
+	}
+
+	/* Write caption */
+	rc = async_data_write_start(exch, params->caption,
+	    eparams.caption_size);
 	async_exchange_end(exch);
 	if (rc != EOK) {
@@ -540,4 +563,37 @@
 	    cursor);
 	async_exchange_end(exch);
+	return rc;
+}
+
+/** Set display window caption.
+ *
+ * @param window Window
+ * @param caption New caption
+ * @return EOK on success or an error code
+ */
+errno_t display_window_set_caption(display_window_t *window,
+    const char *caption)
+{
+	async_exch_t *exch;
+	aid_t req;
+	ipc_call_t answer;
+	size_t cap_size;
+	errno_t rc;
+
+	cap_size = str_size(caption);
+
+	exch = async_exchange_begin(window->display->sess);
+	req = async_send_1(exch, DISPLAY_WINDOW_SET_CAPTION, window->id,
+	    &answer);
+
+	/* Write caption */
+	rc = async_data_write_start(exch, caption, cap_size);
+	async_exchange_end(exch);
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	async_wait_for(req, &rc);
 	return rc;
 }
Index: uspace/lib/display/test/display.c
===================================================================
--- uspace/lib/display/test/display.c	(revision 35cffeac21acf1766c2acbf62f162a7a62c130f7)
+++ uspace/lib/display/test/display.c	(revision 7bcd15fa8041cc8c21cbf98b41cba30ec9aa11f3)
@@ -38,4 +38,5 @@
 #include <loc.h>
 #include <pcut/pcut.h>
+#include <str.h>
 #include "../private/display.h"
 
@@ -68,4 +69,5 @@
 static errno_t test_window_unmaximize(void *, sysarg_t);
 static errno_t test_window_set_cursor(void *, sysarg_t, display_stock_cursor_t);
+static errno_t test_window_set_caption(void *, sysarg_t, const char *);
 static errno_t test_get_event(void *, sysarg_t *, display_wnd_ev_t *);
 static errno_t test_get_info(void *, display_info_t *);
@@ -85,4 +87,5 @@
 	.window_unmaximize = test_window_unmaximize,
 	.window_set_cursor = test_window_set_cursor,
+	.window_set_caption = test_window_set_caption,
 	.get_event = test_get_event,
 	.get_info = test_get_info
@@ -148,4 +151,8 @@
 	sysarg_t set_cursor_wnd_id;
 	display_stock_cursor_t set_cursor_cursor;
+
+	bool window_set_caption_called;
+	sysarg_t set_caption_wnd_id;
+	char *set_caption_caption;
 
 	bool get_event_called;
@@ -1240,4 +1247,112 @@
 	PCUT_ASSERT_INT_EQUALS(dcurs_size_ud, resp.set_cursor_cursor);
 
+	display_window_destroy(wnd);
+	display_close(disp);
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** display_window_set_caption() with server returning error response works. */
+PCUT_TEST(window_set_caption_failure)
+{
+	errno_t rc;
+	service_id_t sid;
+	display_t *disp = NULL;
+	display_wnd_params_t params;
+	display_window_t *wnd;
+	const char *caption;
+	test_response_t resp;
+
+	async_set_fallback_port_handler(test_display_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_display_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_display_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = display_open(test_display_svc, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(disp);
+
+	resp.rc = EOK;
+	display_wnd_params_init(&params);
+	params.rect.p0.x = 0;
+	params.rect.p0.y = 0;
+	params.rect.p0.x = 100;
+	params.rect.p0.y = 100;
+
+	rc = display_window_create(disp, &params, &test_display_wnd_cb,
+	    (void *) &resp, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(wnd);
+
+	caption = "Hello";
+
+	resp.rc = EIO;
+	resp.window_set_caption_called = false;
+
+	rc = display_window_set_caption(wnd, caption);
+	PCUT_ASSERT_INT_EQUALS(wnd->id, resp.set_caption_wnd_id);
+	PCUT_ASSERT_TRUE(resp.window_set_caption_called);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+	PCUT_ASSERT_INT_EQUALS(0, str_cmp(caption, resp.set_caption_caption));
+
+	//free(resp.set_caption_caption);
+	display_window_destroy(wnd);
+	display_close(disp);
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** display_window_set_caption() with server returning success response works. */
+PCUT_TEST(window_set_caption_success)
+{
+	errno_t rc;
+	service_id_t sid;
+	display_t *disp = NULL;
+	display_wnd_params_t params;
+	display_window_t *wnd;
+	const char *caption;
+	test_response_t resp;
+
+	async_set_fallback_port_handler(test_display_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_display_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_display_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = display_open(test_display_svc, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(disp);
+
+	resp.rc = EOK;
+	display_wnd_params_init(&params);
+	params.rect.p0.x = 0;
+	params.rect.p0.y = 0;
+	params.rect.p0.x = 100;
+	params.rect.p0.y = 100;
+
+	rc = display_window_create(disp, &params, &test_display_wnd_cb,
+	    (void *) &resp, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(wnd);
+
+	caption = "Hello";
+
+	resp.rc = EOK;
+	resp.window_set_caption_called = false;
+
+	rc = display_window_set_caption(wnd, caption);
+	PCUT_ASSERT_INT_EQUALS(wnd->id, resp.set_caption_wnd_id);
+	PCUT_ASSERT_TRUE(resp.window_set_caption_called);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+	PCUT_ASSERT_INT_EQUALS(0, str_cmp(caption, resp.set_caption_caption));
+
+	//free(resp.set_caption_caption);
 	display_window_destroy(wnd);
 	display_close(disp);
@@ -2018,4 +2133,16 @@
 }
 
+static errno_t test_window_set_caption(void *arg, sysarg_t wnd_id,
+    const char *caption)
+{
+	test_response_t *resp = (test_response_t *) arg;
+
+	resp->window_set_caption_called = true;
+	resp->set_caption_wnd_id = wnd_id;
+	resp->set_caption_caption = str_dup(caption);
+
+	return resp->rc;
+}
+
 static errno_t test_get_event(void *arg, sysarg_t *wnd_id,
     display_wnd_ev_t *event)
