Index: uspace/lib/ui/include/ui/ui.h
===================================================================
--- uspace/lib/ui/include/ui/ui.h	(revision b2261af05e13475c29f923e0da8ad806ae269700)
+++ uspace/lib/ui/include/ui/ui.h	(revision f1ce7ffb96f25129b80c6daa8fb6c10bef656490)
@@ -57,4 +57,5 @@
 extern errno_t ui_suspend(ui_t *);
 extern errno_t ui_resume(ui_t *);
+extern bool ui_is_suspended(ui_t *);
 extern void ui_lock(ui_t *);
 extern void ui_unlock(ui_t *);
Index: uspace/lib/ui/private/ui.h
===================================================================
--- uspace/lib/ui/private/ui.h	(revision b2261af05e13475c29f923e0da8ad806ae269700)
+++ uspace/lib/ui/private/ui.h	(revision f1ce7ffb96f25129b80c6daa8fb6c10bef656490)
@@ -61,4 +61,6 @@
 	/** Output owned by UI, clean up when destroying UI */
 	bool myoutput;
+	/** @c true iff UI is suspended */
+	bool suspended;
 	/** @c true if terminating */
 	bool quit;
Index: uspace/lib/ui/src/ui.c
===================================================================
--- uspace/lib/ui/src/ui.c	(revision b2261af05e13475c29f923e0da8ad806ae269700)
+++ uspace/lib/ui/src/ui.c	(revision f1ce7ffb96f25129b80c6daa8fb6c10bef656490)
@@ -338,5 +338,7 @@
 	switch (event->type) {
 	case CEV_KEY:
+		ui_lock(ui);
 		ui_window_send_kbd(awnd, &event->ev.key);
+		ui_unlock(ui);
 		break;
 	case CEV_POS:
@@ -348,6 +350,9 @@
 		claim = ui_wdecor_pos_event(awnd->wdecor, &pos);
 		/* Note: If event is claimed, awnd might not be valid anymore */
-		if (claim == ui_unclaimed)
+		if (claim == ui_unclaimed) {
+			ui_lock(ui);
 			ui_window_send_pos(awnd, &pos);
+			ui_unlock(ui);
+		}
 
 		break;
@@ -454,9 +459,20 @@
 errno_t ui_suspend(ui_t *ui)
 {
-	if (ui->cgc == NULL)
+	errno_t rc;
+
+	assert(!ui->suspended);
+
+	if (ui->cgc == NULL) {
+		ui->suspended = true;
 		return EOK;
+	}
 
 	(void) console_set_caption(ui->console, "");
-	return console_gc_suspend(ui->cgc);
+	rc = console_gc_suspend(ui->cgc);
+	if (rc != EOK)
+		return rc;
+
+	ui->suspended = true;
+	return EOK;
 }
 
@@ -477,6 +493,10 @@
 	cons_event_t ev;
 
-	if (ui->cgc == NULL)
+	assert(ui->suspended);
+
+	if (ui->cgc == NULL) {
+		ui->suspended = false;
 		return EOK;
+	}
 
 	rc = console_get_pos(ui->console, &col, &row);
@@ -510,9 +530,25 @@
 		return rc;
 
+	ui->suspended = false;
+
 	awnd = ui_window_get_active(ui);
 	if (awnd != NULL)
 		(void) console_set_caption(ui->console, awnd->wdecor->caption);
 
-	return gfx_cursor_set_visible(console_gc_get_ctx(ui->cgc), false);
+	rc = gfx_cursor_set_visible(console_gc_get_ctx(ui->cgc), false);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+/** Determine if UI is suspended.
+ *
+ * @param ui UI
+ * @return @c true iff UI is suspended
+ */
+bool ui_is_suspended(ui_t *ui)
+{
+	return ui->suspended;
 }
 
Index: uspace/lib/ui/test/ui.c
===================================================================
--- uspace/lib/ui/test/ui.c	(revision b2261af05e13475c29f923e0da8ad806ae269700)
+++ uspace/lib/ui/test/ui.c	(revision f1ce7ffb96f25129b80c6daa8fb6c10bef656490)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2022 Jiri Svoboda
+ * Copyright (c) 2023 Jiri Svoboda
  * All rights reserved.
  *
@@ -69,5 +69,7 @@
 }
 
-/** ui_suspend() / ui_resume() do nothing if we don't have a console */
+/** ui_suspend() / ui_resume() do nothing if we don't have a console,
+ * ui_is_suspended() returns suspend status
+ */
 PCUT_TEST(suspend_resume)
 {
@@ -79,8 +81,15 @@
 	PCUT_ASSERT_NOT_NULL(ui);
 
+	PCUT_ASSERT_FALSE(ui_is_suspended(ui));
+
 	rc = ui_suspend(ui);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_TRUE(ui_is_suspended(ui));
+
 	rc = ui_resume(ui);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_FALSE(ui_is_suspended(ui));
 
 	ui_destroy(ui);
