Index: uspace/lib/ui/src/window.c
===================================================================
--- uspace/lib/ui/src/window.c	(revision 3d10a2fd37dcc38fbf91bdcdfeaa4f2c6938fd76)
+++ uspace/lib/ui/src/window.c	(revision ec50d65ee29a48534ce00b21e79ef5e2e5b04274)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2024 Jiri Svoboda
  * All rights reserved.
  *
@@ -47,4 +47,7 @@
 #include <stdlib.h>
 #include <ui/control.h>
+#include <ui/menubar.h>
+#include <ui/menu.h>
+#include <ui/menuentry.h>
 #include <ui/resource.h>
 #include <ui/ui.h>
@@ -59,9 +62,9 @@
 
 static void dwnd_close_event(void *);
-static void dwnd_focus_event(void *);
+static void dwnd_focus_event(void *, unsigned);
 static void dwnd_kbd_event(void *, kbd_event_t *);
 static void dwnd_pos_event(void *, pos_event_t *);
 static void dwnd_resize_event(void *, gfx_rect_t *);
-static void dwnd_unfocus_event(void *);
+static void dwnd_unfocus_event(void *, unsigned);
 
 static display_wnd_cb_t dwnd_cb = {
@@ -74,11 +77,25 @@
 };
 
+static void wd_sysmenu_open(ui_wdecor_t *, void *, sysarg_t);
+static void wd_sysmenu_left(ui_wdecor_t *, void *, sysarg_t);
+static void wd_sysmenu_right(ui_wdecor_t *, void *, sysarg_t);
+static void wd_sysmenu_accel(ui_wdecor_t *, void *, char32_t, sysarg_t);
+static void wd_minimize(ui_wdecor_t *, void *);
+static void wd_maximize(ui_wdecor_t *, void *);
+static void wd_unmaximize(ui_wdecor_t *, void *);
 static void wd_close(ui_wdecor_t *, void *);
-static void wd_move(ui_wdecor_t *, void *, gfx_coord2_t *);
+static void wd_move(ui_wdecor_t *, void *, gfx_coord2_t *, sysarg_t);
 static void wd_resize(ui_wdecor_t *, void *, ui_wdecor_rsztype_t,
-    gfx_coord2_t *);
+    gfx_coord2_t *, sysarg_t);
 static void wd_set_cursor(ui_wdecor_t *, void *, ui_stock_cursor_t);
 
 static ui_wdecor_cb_t wdecor_cb = {
+	.sysmenu_open = wd_sysmenu_open,
+	.sysmenu_left = wd_sysmenu_left,
+	.sysmenu_right = wd_sysmenu_right,
+	.sysmenu_accel = wd_sysmenu_accel,
+	.minimize = wd_minimize,
+	.maximize = wd_maximize,
+	.unmaximize = wd_unmaximize,
 	.close = wd_close,
 	.move = wd_move,
@@ -86,4 +103,21 @@
 	.set_cursor = wd_set_cursor
 };
+
+static void wnd_sysmenu_left(ui_menu_t *, void *, sysarg_t);
+static void wnd_sysmenu_right(ui_menu_t *, void *, sysarg_t);
+static void wnd_sysmenu_close_req(ui_menu_t *, void *);
+static void wnd_sysmenu_press_accel(ui_menu_t *, void *, char32_t, sysarg_t);
+
+static ui_menu_cb_t wnd_sysmenu_cb = {
+	.left = wnd_sysmenu_left,
+	.right = wnd_sysmenu_right,
+	.close_req = wnd_sysmenu_close_req,
+	.press_accel = wnd_sysmenu_press_accel
+};
+
+static void wnd_sysmenu_erestore(ui_menu_entry_t *, void *);
+static void wnd_sysmenu_eminimize(ui_menu_entry_t *, void *);
+static void wnd_sysmenu_emaximize(ui_menu_entry_t *, void *);
+static void wnd_sysmenu_eclose(ui_menu_entry_t *, void *);
 
 static void ui_window_invalidate(void *, gfx_rect_t *);
@@ -144,4 +178,6 @@
 {
 	gfx_coord2_t dims;
+	gfx_coord2_t below_pos;
+	gfx_rect_t below_rect;
 
 	assert(params->placement != ui_wnd_place_default ||
@@ -177,9 +213,96 @@
 		break;
 	case ui_wnd_place_popup:
-		/* Place popup window below parent rectangle */
-		pos->x = params->prect.p0.x;
-		pos->y = params->prect.p1.y;
+		/* Compute rectangle when placed below */
+		below_pos.x = params->prect.p0.x;
+		below_pos.y = params->prect.p1.y;
+		gfx_rect_translate(&below_pos, &params->rect, &below_rect);
+
+		/* Does below_rect fit within the display? */
+		if (gfx_rect_is_inside(&below_rect, drect)) {
+			/* Place popup window below parent rectangle */
+			pos->x = params->prect.p0.x - params->rect.p0.x;
+			pos->y = params->prect.p1.y - params->rect.p0.y;
+		} else {
+			/* Place popup window above parent rectangle */
+			pos->x = params->prect.p0.x;
+			pos->y = params->prect.p0.y -
+			    (params->rect.p1.y - params->rect.p0.y);
+		}
 		break;
 	}
+}
+
+/** Create window's system menu.
+ *
+ * @param window Window
+ * @return EOK on success or an error code
+ */
+static errno_t ui_window_sysmenu_create(ui_window_t *window)
+{
+	errno_t rc;
+	ui_menu_entry_t *mrestore;
+	ui_menu_entry_t *mmin;
+	ui_menu_entry_t *mmax;
+	ui_menu_entry_t *msep;
+	ui_menu_entry_t *mclose;
+
+	rc = ui_menu_create(window, &window->sysmenu);
+	if (rc != EOK)
+		goto error;
+
+	ui_menu_set_cb(window->sysmenu, &wnd_sysmenu_cb, (void *)window);
+
+	rc = ui_menu_entry_create(window->sysmenu, "~R~estore",
+	    "", &mrestore);
+	if (rc != EOK)
+		goto error;
+
+	if (!window->wdecor->maximized)
+		ui_menu_entry_set_disabled(mrestore, true);
+
+	ui_menu_entry_set_cb(mrestore, wnd_sysmenu_erestore, (void *)window);
+
+	rc = ui_menu_entry_create(window->sysmenu, "Mi~n~imize",
+	    "", &mmin);
+	if (rc != EOK)
+		goto error;
+
+	if ((window->wdecor->style & ui_wds_minimize_btn) == 0)
+		ui_menu_entry_set_disabled(mmin, true);
+
+	ui_menu_entry_set_cb(mmin, wnd_sysmenu_eminimize, (void *)window);
+
+	rc = ui_menu_entry_create(window->sysmenu, "Ma~x~imize",
+	    "", &mmax);
+	if (rc != EOK)
+		goto error;
+
+	if ((window->wdecor->style & ui_wds_maximize_btn) == 0 ||
+	    window->wdecor->maximized)
+		ui_menu_entry_set_disabled(mmax, true);
+
+	ui_menu_entry_set_cb(mmax, wnd_sysmenu_emaximize, (void *)window);
+
+	rc = ui_menu_entry_sep_create(window->sysmenu, &msep);
+	if (rc != EOK)
+		goto error;
+
+	rc = ui_menu_entry_create(window->sysmenu, "~C~lose", "Alt-F4",
+	    &mclose);
+	if (rc != EOK)
+		goto error;
+
+	if ((window->wdecor->style & ui_wds_close_btn) == 0)
+		ui_menu_entry_set_disabled(mclose, true);
+
+	ui_menu_entry_set_cb(mclose, wnd_sysmenu_eclose, (void *)window);
+
+	window->sysmenu_restore = mrestore;
+	window->sysmenu_minimize = mmin;
+	window->sysmenu_maximize = mmax;
+
+	return EOK;
+error:
+	return rc;
 }
 
@@ -195,4 +318,5 @@
 {
 	ui_window_t *window;
+	ui_window_t *pwindow = NULL;
 	display_info_t info;
 	gfx_coord2_t scr_dims;
@@ -218,9 +342,26 @@
 	display_wnd_params_init(&dparams);
 	dparams.rect = params->rect;
+	dparams.caption = params->caption;
 	/* Only allow making the window larger */
 	gfx_rect_dims(&params->rect, &dparams.min_size);
 
+	/*
+	 * 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)
 		dparams.flags |= wndf_popup;
+	if ((params->flags & ui_wndf_topmost) != 0)
+		dparams.flags |= wndf_topmost;
+	if ((params->flags & ui_wndf_system) != 0)
+		dparams.flags |= wndf_system;
+	if ((params->flags & ui_wndf_avoid) != 0)
+		dparams.flags |= wndf_avoid;
 
 	if (ui->display != NULL) {
@@ -262,4 +403,6 @@
 			gfx_coord2_add(&dparams.rect.p0, &scr_dims,
 			    &dparams.rect.p1);
+			(void) console_set_caption(ui->console,
+			    params->caption);
 		}
 	} else {
@@ -355,7 +498,23 @@
 	window->wdecor = wdecor;
 	window->cursor = ui_curs_arrow;
+	window->placement = params->placement;
+
+	rc = ui_window_sysmenu_create(window);
+	if (rc != EOK)
+		goto error;
+
 	*rwindow = window;
 
+	if (ui_is_fullscreen(ui))
+		pwindow = ui_window_get_active(ui);
+
 	list_append(&window->lwindows, &ui->windows);
+
+	if (ui_is_fullscreen(ui)) {
+		/* Send unfocus event to previously active window */
+		if (pwindow != NULL)
+			ui_window_send_unfocus(pwindow, 0);
+	}
+
 	return EOK;
 error:
@@ -383,4 +542,5 @@
 {
 	ui_t *ui;
+	ui_window_t *nwindow;
 
 	if (window == NULL)
@@ -391,9 +551,10 @@
 	list_remove(&window->lwindows);
 	ui_control_destroy(window->control);
+	ui_menu_destroy(window->sysmenu);
 	ui_wdecor_destroy(window->wdecor);
 	ui_resource_destroy(window->res);
-	if (0 && window->app_mgc != NULL)
+	if (window->app_mgc != NULL)
 		mem_gc_delete(window->app_mgc);
-	if (0 && window->app_bmp != NULL)
+	if (window->app_bmp != NULL)
 		gfx_bitmap_destroy(window->app_bmp);
 	if (window->mgc != NULL) {
@@ -406,10 +567,19 @@
 		display_window_destroy(window->dwindow);
 
-	free(window);
-
 	/* Need to repaint if windows are emulated */
 	if (ui_is_fullscreen(ui)) {
 		ui_paint(ui);
-	}
+		/* Send focus event to newly active window */
+		nwindow = ui_window_get_active(ui);
+		if (nwindow != NULL)
+			ui_window_send_focus(nwindow, 0);
+	}
+
+	if (ui->console != NULL &&
+	    window->placement == ui_wnd_place_full_screen) {
+		(void) console_set_caption(ui->console, "");
+	}
+
+	free(window);
 }
 
@@ -421,5 +591,4 @@
  * @param window Window
  * @param control Control
- * @return EOK on success, ENOMEM if out of memory
  */
 void ui_window_add(ui_window_t *window, ui_control_t *control)
@@ -461,15 +630,14 @@
 }
 
-/** Resize/move window.
- *
- * Resize window to the dimensions of @a rect. If @a rect.p0 is not 0,0,
- * the top-left corner of the window will move on the screen accordingly.
+/** Resize or (un)maximize window.
  *
  * @param window Window
  * @param rect Rectangle
+ * @param scop Size change operation
  *
  * @return EOK on success or an error code
  */
-errno_t ui_window_resize(ui_window_t *window, gfx_rect_t *rect)
+errno_t ui_window_size_change(ui_window_t *window, gfx_rect_t *rect,
+    ui_wnd_sc_op_t scop)
 {
 	gfx_coord2_t offs;
@@ -537,9 +705,24 @@
 	}
 
-	/* dwindow can be NULL in case of unit tests */
+	/* dwindow can be NULL in case of unit tests or fullscreen mode */
 	if (window->dwindow != NULL) {
-		rc = display_window_resize(window->dwindow, &offs, &nrect);
-		if (rc != EOK)
-			goto error;
+		switch (scop) {
+		case ui_wsc_resize:
+			rc = display_window_resize(window->dwindow, &offs,
+			    &nrect);
+			if (rc != EOK)
+				goto error;
+			break;
+		case ui_wsc_maximize:
+			rc = display_window_maximize(window->dwindow);
+			if (rc != EOK)
+				goto error;
+			break;
+		case ui_wsc_unmaximize:
+			rc = display_window_unmaximize(window->dwindow);
+			if (rc != EOK)
+				goto error;
+			break;
+		}
 	}
 
@@ -561,5 +744,4 @@
 			goto error;
 #endif
-
 		mem_gc_retarget(window->mgc, &win_params.rect, &win_alloc);
 
@@ -568,4 +750,5 @@
 	}
 
+	window->rect = nrect;
 	ui_wdecor_set_rect(window->wdecor, &nrect);
 	ui_wdecor_paint(window->wdecor);
@@ -589,4 +772,19 @@
 }
 
+/** Resize/move window.
+ *
+ * Resize window to the dimensions of @a rect. If @a rect.p0 is not 0,0,
+ * the top-left corner of the window will move on the screen accordingly.
+ *
+ * @param window Window
+ * @param rect Rectangle
+ *
+ * @return EOK on success or an error code
+ */
+errno_t ui_window_resize(ui_window_t *window, gfx_rect_t *rect)
+{
+	return ui_window_size_change(window, rect, ui_wsc_resize);
+}
+
 /** Set window callbacks.
  *
@@ -599,4 +797,33 @@
 	window->cb = cb;
 	window->arg = arg;
+}
+
+/** Change window caption.
+ *
+ * @param window Window
+ * @param caption New caption
+ *
+ * @return EOK on success or an error code
+ */
+errno_t ui_window_set_caption(ui_window_t *window, const char *caption)
+{
+	errno_t rc;
+
+	/* Set console caption if fullscreen window on console */
+	if (window->ui->console != NULL && window->placement ==
+	    ui_wnd_place_full_screen) {
+		rc = console_set_caption(window->ui->console, caption);
+		if (rc != EOK)
+			return rc;
+	}
+
+	/* Set display window caption if running on display service */
+	if (window->dwindow != NULL) {
+		rc = display_window_set_caption(window->dwindow, caption);
+		if (rc != EOK)
+			return rc;
+	}
+
+	return ui_wdecor_set_caption(window->wdecor, caption);
 }
 
@@ -746,12 +973,19 @@
 {
 	ui_window_t *window = (ui_window_t *) arg;
-
+	ui_t *ui = window->ui;
+
+	ui_lock(ui);
 	ui_window_send_close(window);
+	ui_unlock(ui);
 }
 
 /** Handle window focus event. */
-static void dwnd_focus_event(void *arg)
-{
-	ui_window_t *window = (ui_window_t *) arg;
+static void dwnd_focus_event(void *arg, unsigned nfocus)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+	ui_t *ui = window->ui;
+
+	ui_lock(ui);
+	(void)nfocus;
 
 	if (window->wdecor != NULL) {
@@ -760,5 +994,6 @@
 	}
 
-	ui_window_send_focus(window);
+	ui_window_send_focus(window, nfocus);
+	ui_unlock(ui);
 }
 
@@ -767,7 +1002,9 @@
 {
 	ui_window_t *window = (ui_window_t *) arg;
-
-	(void) window;
+	ui_t *ui = window->ui;
+
+	ui_lock(ui);
 	ui_window_send_kbd(window, kbd_event);
+	ui_unlock(ui);
 }
 
@@ -776,4 +1013,6 @@
 {
 	ui_window_t *window = (ui_window_t *) arg;
+	ui_t *ui = window->ui;
+	ui_evclaim_t claim;
 
 	/* Make sure we don't process events until fully initialized */
@@ -781,6 +1020,14 @@
 		return;
 
-	ui_wdecor_pos_event(window->wdecor, event);
+	ui_lock(ui);
+
+	claim = ui_wdecor_pos_event(window->wdecor, event);
+	if (claim == ui_claimed) {
+		ui_unlock(ui);
+		return;
+	}
+
 	ui_window_send_pos(window, event);
+	ui_unlock(ui);
 }
 
@@ -789,4 +1036,5 @@
 {
 	ui_window_t *window = (ui_window_t *) arg;
+	ui_t *ui = window->ui;
 
 	/* Make sure we don't process events until fully initialized */
@@ -797,19 +1045,127 @@
 		return;
 
+	ui_lock(ui);
 	(void) ui_window_resize(window, rect);
 	(void) ui_window_paint(window);
+	ui_unlock(ui);
 }
 
 /** Handle window unfocus event. */
-static void dwnd_unfocus_event(void *arg)
-{
-	ui_window_t *window = (ui_window_t *) arg;
-
-	if (window->wdecor != NULL) {
+static void dwnd_unfocus_event(void *arg, unsigned nfocus)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+	ui_t *ui = window->ui;
+
+	ui_lock(ui);
+
+	if (window->wdecor != NULL && nfocus == 0) {
 		ui_wdecor_set_active(window->wdecor, false);
 		ui_wdecor_paint(window->wdecor);
 	}
 
-	ui_window_send_unfocus(window);
+	ui_window_send_unfocus(window, nfocus);
+	ui_unlock(ui);
+}
+
+/** Window decoration requested opening of system menu.
+ *
+ * @param wdecor Window decoration
+ * @param arg Argument (window)
+ * @param idev_id Input device ID
+ */
+static void wd_sysmenu_open(ui_wdecor_t *wdecor, void *arg, sysarg_t idev_id)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+
+	ui_window_send_sysmenu(window, idev_id);
+}
+
+/** Window decoration requested moving left from system menu handle.
+ *
+ * @param wdecor Window decoration
+ * @param arg Argument (window)
+ * @param idev_id Input device ID
+ */
+static void wd_sysmenu_left(ui_wdecor_t *wdecor, void *arg, sysarg_t idev_id)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+
+	if (window->mbar != NULL) {
+		ui_wdecor_sysmenu_hdl_set_active(window->wdecor, false);
+		ui_menu_close(window->sysmenu);
+		ui_menu_bar_select_last(window->mbar, false, idev_id);
+	}
+}
+
+/** Window decoration requested moving right from system menu handle.
+ *
+ * @param wdecor Window decoration
+ * @param arg Argument (window)
+ * @param idev_id Input device ID
+ */
+static void wd_sysmenu_right(ui_wdecor_t *wdecor, void *arg, sysarg_t idev_id)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+
+	if (window->mbar != NULL) {
+		ui_wdecor_sysmenu_hdl_set_active(window->wdecor, false);
+		ui_menu_close(window->sysmenu);
+		ui_menu_bar_select_first(window->mbar, false, idev_id);
+	}
+}
+
+/** Window decoration detected accelerator press from system menu handle.
+ *
+ * @param wdecor Window decoration
+ * @param arg Argument (window)
+ * @param c Accelerator key
+ * @param idev_id Input device ID
+ */
+static void wd_sysmenu_accel(ui_wdecor_t *wdecor, void *arg, char32_t c,
+    sysarg_t idev_id)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+
+	if (window->mbar != NULL) {
+		ui_wdecor_sysmenu_hdl_set_active(window->wdecor, false);
+		ui_menu_close(window->sysmenu);
+		ui_menu_bar_press_accel(window->mbar, c, idev_id);
+	}
+}
+
+/** Window decoration requested window minimization.
+ *
+ * @param wdecor Window decoration
+ * @param arg Argument (window)
+ */
+static void wd_minimize(ui_wdecor_t *wdecor, void *arg)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+
+	ui_window_send_minimize(window);
+}
+
+/** Window decoration requested window maximization.
+ *
+ * @param wdecor Window decoration
+ * @param arg Argument (window)
+ */
+static void wd_maximize(ui_wdecor_t *wdecor, void *arg)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+
+	ui_window_send_maximize(window);
+}
+
+/** Window decoration requested window unmaximization.
+ *
+ * @param wdecor Window decoration
+ * @param arg Argument (window)
+ */
+static void wd_unmaximize(ui_wdecor_t *wdecor, void *arg)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+
+	ui_window_send_unmaximize(window);
 }
 
@@ -831,11 +1187,13 @@
  * @param arg Argument (window)
  * @param pos Position where the title bar was pressed
- */
-static void wd_move(ui_wdecor_t *wdecor, void *arg, gfx_coord2_t *pos)
+ * @param pos_id Positioning device ID
+ */
+static void wd_move(ui_wdecor_t *wdecor, void *arg, gfx_coord2_t *pos,
+    sysarg_t pos_id)
 {
 	ui_window_t *window = (ui_window_t *) arg;
 
 	if (window->dwindow != NULL)
-		(void) display_window_move_req(window->dwindow, pos);
+		(void) display_window_move_req(window->dwindow, pos, pos_id);
 }
 
@@ -846,12 +1204,16 @@
  * @param rsztype Resize type
  * @param pos Position where the button was pressed
+ * @param pos_id Positioning device ID
  */
 static void wd_resize(ui_wdecor_t *wdecor, void *arg,
-    ui_wdecor_rsztype_t rsztype, gfx_coord2_t *pos)
-{
-	ui_window_t *window = (ui_window_t *) arg;
-
-	if (window->dwindow != NULL)
-		(void) display_window_resize_req(window->dwindow, rsztype, pos);
+    ui_wdecor_rsztype_t rsztype, gfx_coord2_t *pos, sysarg_t pos_id)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+
+	if (window->dwindow != NULL) {
+		(void) display_window_resize_req(window->dwindow,
+		    (display_wnd_rsztype_t) rsztype, // Same constants in the enums
+		    pos, pos_id);
+	}
 }
 
@@ -914,4 +1276,53 @@
 }
 
+/** Send window sysmenu event.
+ *
+ * @param window Window
+ * @parma idev_id Input device ID
+ */
+void ui_window_send_sysmenu(ui_window_t *window, sysarg_t idev_id)
+{
+	if (window->cb != NULL && window->cb->sysmenu != NULL)
+		window->cb->sysmenu(window, window->arg, idev_id);
+	else
+		ui_window_def_sysmenu(window, idev_id);
+}
+
+/** Send window minimize event.
+ *
+ * @param window Window
+ */
+void ui_window_send_minimize(ui_window_t *window)
+{
+	if (window->cb != NULL && window->cb->maximize != NULL)
+		window->cb->minimize(window, window->arg);
+	else
+		ui_window_def_minimize(window);
+}
+
+/** Send window maximize event.
+ *
+ * @param window Window
+ */
+void ui_window_send_maximize(ui_window_t *window)
+{
+	if (window->cb != NULL && window->cb->maximize != NULL)
+		window->cb->maximize(window, window->arg);
+	else
+		ui_window_def_maximize(window);
+}
+
+/** Send window unmaximize event.
+ *
+ * @param window Window
+ */
+void ui_window_send_unmaximize(ui_window_t *window)
+{
+	if (window->cb != NULL && window->cb->unmaximize != NULL)
+		window->cb->unmaximize(window, window->arg);
+	else
+		ui_window_def_unmaximize(window);
+}
+
 /** Send window close event.
  *
@@ -927,9 +1338,10 @@
  *
  * @param window Window
- */
-void ui_window_send_focus(ui_window_t *window)
+ * @param nfocus New number of foci
+ */
+void ui_window_send_focus(ui_window_t *window, unsigned nfocus)
 {
 	if (window->cb != NULL && window->cb->focus != NULL)
-		window->cb->focus(window, window->arg);
+		window->cb->focus(window, window->arg, nfocus);
 }
 
@@ -943,5 +1355,5 @@
 		window->cb->kbd(window, window->arg, kbd);
 	else
-		return ui_window_def_kbd(window, kbd);
+		ui_window_def_kbd(window, kbd);
 }
 
@@ -973,11 +1385,120 @@
  *
  * @param window Window
- */
-void ui_window_send_unfocus(ui_window_t *window)
+ * @param nfocus Number of remaining foci
+ */
+void ui_window_send_unfocus(ui_window_t *window, unsigned nfocus)
 {
 	if (window->cb != NULL && window->cb->unfocus != NULL)
-		window->cb->unfocus(window, window->arg);
+		window->cb->unfocus(window, window->arg, nfocus);
 	else
-		return ui_window_def_unfocus(window);
+		return ui_window_def_unfocus(window, nfocus);
+}
+
+/** Default window sysmenu routine.
+ *
+ * @param window Window
+ * @param idev_id Input device ID
+ * @return EOK on success or an error code
+ */
+errno_t ui_window_def_sysmenu(ui_window_t *window, sysarg_t idev_id)
+{
+	errno_t rc;
+	ui_wdecor_geom_t geom;
+
+	if (ui_menu_is_open(window->sysmenu)) {
+		ui_menu_close(window->sysmenu);
+	} else {
+		ui_wdecor_get_geom(window->wdecor, &geom);
+
+		rc = ui_menu_open(window->sysmenu, &geom.title_bar_rect,
+		    idev_id);
+		if (rc != EOK)
+			goto error;
+	}
+
+	return EOK;
+error:
+	return rc;
+}
+
+/** Default window minimize routine.
+ *
+ * @param window Window
+ * @return EOK on success or an error code
+ */
+errno_t ui_window_def_minimize(ui_window_t *window)
+{
+	errno_t rc;
+
+	if (window->dwindow != NULL) {
+		rc = display_window_minimize(window->dwindow);
+		if (rc != EOK)
+			goto error;
+	}
+
+	return EOK;
+error:
+	return rc;
+}
+
+/** Default window maximize routine.
+ *
+ * @param window Window
+ * @return EOK on success or an error code
+ */
+errno_t ui_window_def_maximize(ui_window_t *window)
+{
+	errno_t rc;
+	gfx_rect_t old_rect;
+	gfx_rect_t rect;
+
+	old_rect = window->rect;
+
+	if (window->dwindow != NULL) {
+		rc = display_window_get_max_rect(window->dwindow, &rect);
+		if (rc != EOK)
+			return rc;
+	} else {
+		rect = window->ui->rect;
+	}
+
+	ui_wdecor_set_maximized(window->wdecor, true);
+	ui_menu_entry_set_disabled(window->sysmenu_restore, false);
+	ui_menu_entry_set_disabled(window->sysmenu_maximize, true);
+
+	rc = ui_window_size_change(window, &rect, ui_wsc_maximize);
+	if (rc != EOK) {
+		ui_wdecor_set_maximized(window->wdecor, false);
+		return rc;
+	}
+
+	window->normal_rect = old_rect;
+	(void) ui_window_paint(window);
+	return EOK;
+}
+
+/** Default window unmaximize routine.
+ *
+ * @param window Window
+ * @return EOK on success or an error code
+ */
+errno_t ui_window_def_unmaximize(ui_window_t *window)
+{
+	errno_t rc;
+
+	ui_wdecor_set_maximized(window->wdecor, false);
+	ui_menu_entry_set_disabled(window->sysmenu_restore, true);
+	ui_menu_entry_set_disabled(window->sysmenu_maximize, false);
+
+	rc = ui_window_size_change(window, &window->normal_rect,
+	    ui_wsc_unmaximize);
+	if (rc != EOK) {
+		ui_wdecor_set_maximized(window->wdecor, true);
+		printf("ui_window_size_change->error\n");
+		return rc;
+	}
+
+	(void) ui_window_paint(window);
+	return EOK;
 }
 
@@ -985,9 +1506,19 @@
  *
  * @param window Window
- */
-void ui_window_def_kbd(ui_window_t *window, kbd_event_t *kbd)
-{
+ * @return ui_claimed iff event was claimed
+ */
+ui_evclaim_t ui_window_def_kbd(ui_window_t *window, kbd_event_t *kbd)
+{
+	ui_evclaim_t claim;
+
 	if (window->control != NULL)
-		ui_control_kbd_event(window->control, kbd);
+		claim = ui_control_kbd_event(window->control, kbd);
+	else
+		claim = ui_unclaimed;
+
+	if (claim == ui_unclaimed)
+		return ui_wdecor_kbd_event(window->wdecor, kbd);
+
+	return ui_unclaimed;
 }
 
@@ -1035,10 +1566,122 @@
  *
  * @param window Window
+ * @param nfocus Number of remaining foci
  * @return EOK on success or an error code
  */
-void ui_window_def_unfocus(ui_window_t *window)
+void ui_window_def_unfocus(ui_window_t *window, unsigned nfocus)
 {
 	if (window->control != NULL)
-		ui_control_unfocus(window->control);
+		ui_control_unfocus(window->control, nfocus);
+}
+
+/** Handle system menu left event.
+ *
+ * @param sysmenu System menu
+ * @param arg Argument (ui_window_t *)
+ * @param idev_id Input device ID
+ */
+static void wnd_sysmenu_left(ui_menu_t *sysmenu, void *arg, sysarg_t idev_id)
+{
+	ui_window_t *window = (ui_window_t *)arg;
+
+	if (window->mbar != NULL) {
+		ui_wdecor_sysmenu_hdl_set_active(window->wdecor, false);
+		ui_menu_close(sysmenu);
+		ui_menu_bar_select_last(window->mbar, true, idev_id);
+	}
+}
+
+/** Handle system menu right event.
+ *
+ * @param sysmenu System menu
+ * @param arg Argument (ui_window_t *)
+ * @param idev_id Input device ID
+ */
+static void wnd_sysmenu_right(ui_menu_t *sysmenu, void *arg, sysarg_t idev_id)
+{
+	ui_window_t *window = (ui_window_t *)arg;
+
+	if (window->mbar != NULL) {
+		ui_wdecor_sysmenu_hdl_set_active(window->wdecor, false);
+		ui_menu_close(sysmenu);
+		ui_menu_bar_select_first(window->mbar, true, idev_id);
+	}
+}
+
+/** Handle system menu close request event.
+ *
+ * @param sysmenu System menu
+ * @param arg Argument (ui_window_t *)
+ * @param idev_id Input device ID
+ */
+static void wnd_sysmenu_close_req(ui_menu_t *sysmenu, void *arg)
+{
+	ui_window_t *window = (ui_window_t *)arg;
+
+	ui_wdecor_sysmenu_hdl_set_active(window->wdecor, false);
+	ui_menu_close(sysmenu);
+}
+
+/** Handle system menu Restore entry activation.
+ *
+ * @param mentry Menu entry
+ * @param arg Argument (ui_window_t *)
+ */
+static void wnd_sysmenu_erestore(ui_menu_entry_t *mentry, void *arg)
+{
+	ui_window_t *window = (ui_window_t *)arg;
+
+	ui_window_send_unmaximize(window);
+}
+
+/** Handle system menu Minimize entry activation.
+ *
+ * @param mentry Menu entry
+ * @param arg Argument (ui_window_t *)
+ */
+static void wnd_sysmenu_eminimize(ui_menu_entry_t *mentry, void *arg)
+{
+	ui_window_t *window = (ui_window_t *)arg;
+
+	ui_window_send_minimize(window);
+}
+
+/** Handle system menu Maximize entry activation.
+ *
+ * @param mentry Menu entry
+ * @param arg Argument (ui_window_t *)
+ */
+static void wnd_sysmenu_emaximize(ui_menu_entry_t *mentry, void *arg)
+{
+	ui_window_t *window = (ui_window_t *)arg;
+
+	ui_window_send_maximize(window);
+}
+
+/** Handle system menu Close entry activation.
+ *
+ * @param mentry Menu entry
+ * @param arg Argument (ui_window_t *)
+ */
+static void wnd_sysmenu_eclose(ui_menu_entry_t *mentry, void *arg)
+{
+	ui_window_t *window = (ui_window_t *)arg;
+
+	ui_window_send_close(window);
+}
+
+/** Handle system menu press accelerator key event.
+ *
+ * @param sysmenu System menu
+ * @param arg Argument (ui_window_t *)
+ * @param idev_id Input device ID
+ */
+static void wnd_sysmenu_press_accel(ui_menu_t *sysmenu, void *arg,
+    char32_t c, sysarg_t idev_id)
+{
+	(void)sysmenu;
+	(void)arg;
+	(void)c;
+	(void)idev_id;
 }
 
