Index: uspace/lib/ui/src/menu.c
===================================================================
--- uspace/lib/ui/src/menu.c	(revision ff6e91bcf78218e49444169033c0d4edb9c50021)
+++ uspace/lib/ui/src/menu.c	(revision 59768c77459332a9e19b4be9e1a0aa6659f1b7c9)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -47,4 +47,5 @@
 #include <ui/popup.h>
 #include <ui/menu.h>
+#include <ui/menubar.h>
 #include <ui/menuentry.h>
 #include <ui/resource.h>
@@ -63,8 +64,10 @@
 
 static void ui_menu_popup_close(ui_popup_t *, void *);
+static void ui_menu_popup_kbd(ui_popup_t *, void *, kbd_event_t *);
 static void ui_menu_popup_pos(ui_popup_t *, void *, pos_event_t *);
 
 static ui_popup_cb_t ui_menu_popup_cb = {
 	.close = ui_menu_popup_close,
+	.kbd = ui_menu_popup_kbd,
 	.pos = ui_menu_popup_pos
 };
@@ -149,4 +152,36 @@
 
 	link = list_next(&cur->lmenus, &cur->mbar->menus);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, ui_menu_t, lmenus);
+}
+
+/** Get last menu in menu bar.
+ *
+ * @param mbar Menu bar
+ * @return Last menu or @c NULL if there is none
+ */
+ui_menu_t *ui_menu_last(ui_menu_bar_t *mbar)
+{
+	link_t *link;
+
+	link = list_last(&mbar->menus);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, ui_menu_t, lmenus);
+}
+
+/** Get previous menu in menu bar.
+ *
+ * @param cur Current menu
+ * @return Previous menu or @c NULL if @a cur is the fist one
+ */
+ui_menu_t *ui_menu_prev(ui_menu_t *cur)
+{
+	link_t *link;
+
+	link = list_prev(&cur->lmenus, &cur->mbar->menus);
 	if (link == NULL)
 		return NULL;
@@ -240,4 +275,7 @@
 	errno_t rc;
 
+	/* Select first entry */
+	menu->selected = ui_menu_entry_first(menu);
+
 	/* Determine menu dimensions */
 
@@ -269,4 +307,14 @@
 	ui_popup_destroy(menu->popup);
 	menu->popup = NULL;
+}
+
+/** Determine if menu is open.
+ *
+ * @param menu Menu
+ * @return @c true iff menu is open
+ */
+bool ui_menu_is_open(ui_menu_t *menu)
+{
+	return menu->popup != NULL;
 }
 
@@ -439,5 +487,5 @@
 		/* Press outside menu - close it */
 		if (event->type == POS_PRESS)
-			ui_menu_bar_select(menu->mbar, NULL, NULL);
+			ui_menu_bar_deactivate(menu->mbar);
 	}
 
@@ -454,6 +502,128 @@
 	ui_menu_t *menu = (ui_menu_t *)arg;
 
-	/* Close the menu */
-	ui_menu_bar_select(menu->mbar, NULL, NULL);
+	/* Deactivate menu bar, close menu */
+	ui_menu_bar_deactivate(menu->mbar);
+}
+
+/** Move one entry up.
+ *
+ * Non-selectable entries are skipped. If we are already at the top,
+ * we wrap around.
+ *
+ * @param menu Menu
+ */
+void ui_menu_up(ui_menu_t *menu)
+{
+	gfx_coord2_t mpos;
+	ui_menu_entry_t *nentry;
+
+	if (menu->selected == NULL)
+		return;
+
+	nentry = ui_menu_entry_prev(menu->selected);
+	if (nentry == NULL)
+		nentry = ui_menu_entry_last(menu);
+
+	/* Need to find a selectable entry */
+	while (!ui_menu_entry_selectable(nentry)) {
+		nentry = ui_menu_entry_prev(nentry);
+		if (nentry == NULL)
+			nentry = ui_menu_entry_last(menu);
+
+		/* Went completely around and found nothing? */
+		if (nentry == menu->selected)
+			return;
+	}
+
+	menu->selected = nentry;
+
+	mpos.x = 0;
+	mpos.y = 0;
+	(void) ui_menu_paint(menu, &mpos);
+}
+
+/** Move one entry down.
+ *
+ * Non-selectable entries are skipped. If we are already at the bottom,
+ * we wrap around.
+ *
+ * @param menu Menu
+ */
+void ui_menu_down(ui_menu_t *menu)
+{
+	gfx_coord2_t mpos;
+	ui_menu_entry_t *nentry;
+
+	if (menu->selected == NULL)
+		return;
+
+	nentry = ui_menu_entry_next(menu->selected);
+	if (nentry == NULL)
+		nentry = ui_menu_entry_first(menu);
+
+	/* Need to find a selectable entry */
+	while (!ui_menu_entry_selectable(nentry)) {
+		nentry = ui_menu_entry_next(nentry);
+		if (nentry == NULL)
+			nentry = ui_menu_entry_first(menu);
+
+		/* Went completely around and found nothing? */
+		if (nentry == menu->selected)
+			return;
+	}
+
+	menu->selected = nentry;
+
+	mpos.x = 0;
+	mpos.y = 0;
+	(void) ui_menu_paint(menu, &mpos);
+}
+
+/** Handle key press without modifiers in menu popup window.
+ *
+ * @param menu Menu
+ * @param event Keyboard event
+ */
+static void ui_menu_key_press_unmod(ui_menu_t *menu, kbd_event_t *event)
+{
+	switch (event->key) {
+	case KC_ESCAPE:
+		ui_menu_bar_deactivate(menu->mbar);
+		break;
+	case KC_LEFT:
+		ui_menu_bar_left(menu->mbar);
+		break;
+	case KC_RIGHT:
+		ui_menu_bar_right(menu->mbar);
+		break;
+	case KC_UP:
+		ui_menu_up(menu);
+		break;
+	case KC_DOWN:
+		ui_menu_down(menu);
+		break;
+	case KC_ENTER:
+		if (menu->selected != NULL)
+			ui_menu_entry_activate(menu->selected);
+		break;
+	default:
+		break;
+	}
+}
+
+/** Handle keyboard event in menu popup window.
+ *
+ * @param popup Menu popup window
+ * @param arg Argument (ui_menu_t *)
+ * @param event Keyboard event
+ */
+static void ui_menu_popup_kbd(ui_popup_t *popup, void *arg, kbd_event_t *event)
+{
+	ui_menu_t *menu = (ui_menu_t *)arg;
+
+	if (event->type == KEY_PRESS && (event->mods &
+	    (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
+		ui_menu_key_press_unmod(menu, event);
+	}
 }
 
