Index: uspace/lib/ui/src/ui.c
===================================================================
--- uspace/lib/ui/src/ui.c	(revision 5d380b63867e347a3212398238dbe95d3b227d35)
+++ uspace/lib/ui/src/ui.c	(revision 30b56d9814196a29138d75a9555cb10e287d85dd)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2022 Jiri Svoboda
+ * Copyright (c) 2023 Jiri Svoboda
  * All rights reserved.
  *
@@ -48,4 +48,5 @@
 #include <str.h>
 #include <task.h>
+#include <types/common.h>
 #include <ui/clickmatic.h>
 #include <ui/ui.h>
@@ -65,9 +66,18 @@
  * @param ws Place to store window system type (protocol)
  * @param osvc Place to store pointer to output service name
- */
-static void ui_ospec_parse(const char *ospec, ui_winsys_t *ws,
-    const char **osvc)
+ * @param ridev_id Place to store input device ID
+ * @return EOK on success, EINVAL if syntax is invalid, ENOMEM if out of
+ *         memory
+ */
+static errno_t ui_ospec_parse(const char *ospec, ui_winsys_t *ws,
+    char **osvc, sysarg_t *ridev_id)
 {
 	const char *cp;
+	const char *qm;
+	const char *endptr;
+	uint64_t idev_id;
+	errno_t rc;
+
+	*ridev_id = 0;
 
 	cp = ospec;
@@ -75,4 +85,5 @@
 		++cp;
 
+	/* Window system / protocol */
 	if (*cp == '@') {
 		if (str_lcmp(ospec, "disp@", str_length("disp@")) == 0) {
@@ -88,12 +99,48 @@
 		}
 
-		if (cp[1] != '\0')
-			*osvc = cp + 1;
-		else
-			*osvc = NULL;
+		++cp;
 	} else {
 		*ws = ui_ws_display;
-		*osvc = ospec;
-	}
+	}
+
+	/* Output service is the part before question mark */
+	qm = str_chr(cp, '?');
+	if (qm != NULL) {
+		*osvc = str_ndup(cp, qm - cp);
+	} else {
+		/* No question mark */
+		*osvc = str_dup(cp);
+	}
+
+	if (*osvc == NULL)
+		return ENOMEM;
+
+	if (qm != NULL) {
+		/* The part after the question mark */
+		cp = qm + 1;
+
+		/* Input device ID parameter */
+		if (str_lcmp(cp, "idev=", str_length("idev=")) == 0) {
+			cp += str_length("idev=");
+
+			rc = str_uint64_t(cp, &endptr, 10, false, &idev_id);
+			if (rc != EOK)
+				goto error;
+
+			*ridev_id = idev_id;
+			cp = endptr;
+		}
+	}
+
+	if (*cp != '\0') {
+		rc = EINVAL;
+		goto error;
+	}
+
+	return EOK;
+error:
+	free(*osvc);
+	*osvc = NULL;
+	return rc;
 }
 
@@ -114,14 +161,17 @@
 	console_gc_t *cgc;
 	ui_winsys_t ws;
-	const char *osvc;
+	char *osvc;
 	sysarg_t cols;
 	sysarg_t rows;
+	sysarg_t idev_id;
 	ui_t *ui;
 
-	ui_ospec_parse(ospec, &ws, &osvc);
+	rc = ui_ospec_parse(ospec, &ws, &osvc, &idev_id);
+	if (rc != EOK)
+		return rc;
 
 	if (ws == ui_ws_display || ws == ui_ws_any) {
-		rc = display_open(osvc != NULL ? osvc : DISPLAY_DEFAULT,
-		    &display);
+		rc = display_open((str_cmp(osvc, "") != 0) ? osvc :
+		    DISPLAY_DEFAULT, &display);
 		if (rc != EOK)
 			goto disp_fail;
@@ -133,5 +183,7 @@
 		}
 
+		free(osvc);
 		ui->myoutput = true;
+		ui->idev_id = idev_id;
 		*rui = ui;
 		return EOK;
@@ -166,4 +218,6 @@
 		}
 
+		free(osvc);
+
 		ui->cgc = cgc;
 		ui->rect.p0.x = 0;
@@ -180,4 +234,5 @@
 cons_fail:
 	if (ws == ui_ws_null) {
+		free(osvc);
 		rc = ui_create_disp(NULL, &ui);
 		if (rc != EOK)
@@ -189,4 +244,5 @@
 	}
 
+	free(osvc);
 	return EINVAL;
 }
Index: uspace/lib/ui/src/window.c
===================================================================
--- uspace/lib/ui/src/window.c	(revision 5d380b63867e347a3212398238dbe95d3b227d35)
+++ uspace/lib/ui/src/window.c	(revision 30b56d9814196a29138d75a9555cb10e287d85dd)
@@ -227,5 +227,14 @@
 	/* Only allow making the window larger */
 	gfx_rect_dims(&params->rect, &dparams.min_size);
-	dparams.idev_id = params->idev_id;
+
+	/*
+	 * If idev_id is not specified, use the UI default (probably
+	 * obtained from display specification. This creates the
+	 * main window in the seat specified on the command line.
+	 */
+	if (params->idev_id != 0)
+		dparams.idev_id = params->idev_id;
+	else
+		dparams.idev_id = ui->idev_id;
 
 	if ((params->flags & ui_wndf_popup) != 0)
