Index: uspace/lib/ui/src/menu.c
===================================================================
--- uspace/lib/ui/src/menu.c	(revision 7470d97aaec04abd08ce2ad450cda9e6ff912bd4)
+++ uspace/lib/ui/src/menu.c	(revision 3c8c5808e19c14b9cbc2453181414284f31291a4)
@@ -45,4 +45,5 @@
 #include <ui/control.h>
 #include <ui/paint.h>
+#include <ui/popup.h>
 #include <ui/menu.h>
 #include <ui/menuentry.h>
@@ -59,4 +60,10 @@
 };
 
+static void ui_menu_popup_pos(ui_popup_t *, void *, pos_event_t *);
+
+static ui_popup_cb_t ui_menu_popup_cb = {
+	.pos = ui_menu_popup_pos
+};
+
 /** Create new menu.
  *
@@ -203,4 +210,56 @@
 }
 
+/** Get UI resource from menu.
+ *
+ * @param menu Menu
+ * @return UI resource
+ */
+ui_resource_t *ui_menu_get_res(ui_menu_t *menu)
+{
+	return ui_popup_get_res(menu->popup);
+}
+
+/** Open menu.
+ *
+ * @param menu Menu
+ * @param prect Parent rectangle around which the menu should be placed
+ */
+errno_t ui_menu_open(ui_menu_t *menu, gfx_rect_t *prect)
+{
+	ui_popup_t *popup = NULL;
+	ui_popup_params_t params;
+	ui_menu_geom_t geom;
+	gfx_coord2_t mpos;
+	errno_t rc;
+
+	/* Determine menu dimensions */
+
+	mpos.x = 0;
+	mpos.y = 0;
+	ui_menu_get_geom(menu, &mpos, &geom);
+
+	ui_popup_params_init(&params);
+	params.rect = geom.outer_rect;
+
+	rc = ui_popup_create(menu->mbar->ui, &params, &popup);
+	if (rc != EOK)
+		return rc;
+
+	menu->popup = popup;
+	ui_popup_set_cb(popup, &ui_menu_popup_cb, menu);
+
+	return ui_menu_paint(menu, &mpos);
+}
+
+/** Close menu.
+ *
+ * @param menu Menu
+ */
+void ui_menu_close(ui_menu_t *menu)
+{
+	ui_popup_destroy(menu->popup);
+	menu->popup = NULL;
+}
+
 /** Paint menu.
  *
@@ -218,5 +277,5 @@
 	errno_t rc;
 
-	res = menu->mbar->res;
+	res = ui_menu_get_res(menu);
 	ui_menu_get_geom(menu, spos, &geom);
 
@@ -261,15 +320,4 @@
 error:
 	return rc;
-}
-
-/** Unpaint menu.
- *
- * @param menu Menu
- * @return EOK on success or an error code
- */
-errno_t ui_menu_unpaint(ui_menu_t *menu)
-{
-	ui_resource_expose(menu->mbar->res);
-	return EOK;
 }
 
@@ -312,6 +360,6 @@
 	} else {
 		/* Press outside menu - close it */
-		if (event->type == POS_PRESS)
-			ui_menu_bar_select(menu->mbar, NULL, NULL);
+//		if (event->type == POS_PRESS)
+//			ui_menu_bar_select(menu->mbar, NULL, NULL);
 	}
 
@@ -319,4 +367,20 @@
 }
 
+/** Handle position event in menu popup window.
+ *
+ * @param popup Menu popup window
+ * @param arg Argument (ui_menu_t *)
+ * @param event Position event
+ */
+static void ui_menu_popup_pos(ui_popup_t *popup, void *arg, pos_event_t *event)
+{
+	ui_menu_t *menu = (ui_menu_t *)arg;
+	gfx_coord2_t spos;
+
+	spos.x = 0;
+	spos.y = 0;
+	ui_menu_pos_event(menu, &spos, event);
+}
+
 /** @}
  */
Index: uspace/lib/ui/src/menubar.c
===================================================================
--- uspace/lib/ui/src/menubar.c	(revision 7470d97aaec04abd08ce2ad450cda9e6ff912bd4)
+++ uspace/lib/ui/src/menubar.c	(revision 3c8c5808e19c14b9cbc2453181414284f31291a4)
@@ -72,9 +72,11 @@
 /** Create new menu bar.
  *
+ * @param ui UI
  * @param res UI resource
  * @param rmbar Place to store pointer to new menu bar
  * @return EOK on success, ENOMEM if out of memory
  */
-errno_t ui_menu_bar_create(ui_resource_t *res, ui_menu_bar_t **rmbar)
+errno_t ui_menu_bar_create(ui_t *ui, ui_resource_t *res,
+    ui_menu_bar_t **rmbar)
 {
 	ui_menu_bar_t *mbar;
@@ -91,4 +93,5 @@
 	}
 
+	mbar->ui = ui;
 	mbar->res = res;
 	list_initialize(&mbar->menus);
@@ -232,11 +235,10 @@
  *
  * @param mbar Menu bar
- * @param pos Position (top-left corner) of menu bar entry
+ * @param rect Menu bar entry rectangle
  * @param menu Menu to select (or deselect if selected) or @c NULL
  */
-void ui_menu_bar_select(ui_menu_bar_t *mbar, gfx_coord2_t *pos,
+void ui_menu_bar_select(ui_menu_bar_t *mbar, gfx_rect_t *rect,
     ui_menu_t *menu)
 {
-	gfx_coord2_t spos;
 	ui_menu_t *old_menu;
 
@@ -248,19 +250,12 @@
 		mbar->selected = NULL;
 
-	/* Need to clear the menu has just been closed */
+	/* Close previously open menu */
 	if (old_menu != NULL)
-		(void) ui_menu_unpaint(old_menu);
+		(void) ui_menu_close(old_menu);
 
 	(void) ui_menu_bar_paint(mbar);
 
 	if (mbar->selected != NULL) {
-		/* Cache position of selected entry */
-		mbar->sel_pos = *pos;
-
-		/* Position menu under selected menu bar entry */
-		spos.x = pos->x;
-		spos.y = mbar->rect.p1.y;
-
-		(void) ui_menu_paint(mbar->selected, &spos);
+		(void) ui_menu_open(mbar->selected, rect);
 	}
 }
@@ -275,5 +270,4 @@
 {
 	gfx_coord2_t pos;
-	gfx_coord2_t spos;
 	gfx_rect_t rect;
 	ui_menu_t *menu;
@@ -282,5 +276,4 @@
 	gfx_coord_t hpad;
 	gfx_coord2_t ppos;
-	ui_evclaim_t claimed;
 
 	ppos.x = event->hpos;
@@ -307,20 +300,8 @@
 		if (event->type == POS_PRESS &&
 		    gfx_pix_inside_rect(&ppos, &rect)) {
-			ui_menu_bar_select(mbar, &pos, menu);
+			ui_menu_bar_select(mbar, &rect, menu);
 			return ui_claimed;
 		}
 
-		if (menu == mbar->selected) {
-			/* Open menu is positioned below menu bar entry */
-			spos.x = pos.x;
-			spos.y = mbar->rect.p1.y;
-
-			ui_menu_get_rect(menu, &spos, &rect);
-
-			claimed = ui_menu_pos_event(menu, &spos, event);
-			if (claimed == ui_claimed)
-				return ui_claimed;
-		}
-
 		pos.x += width;
 		menu = ui_menu_next(menu);
@@ -336,5 +317,5 @@
 void ui_menu_bar_unfocus(ui_menu_bar_t *mbar)
 {
-	ui_menu_bar_select(mbar, NULL, NULL);
+//	ui_menu_bar_select(mbar, NULL, NULL);
 }
 
Index: uspace/lib/ui/src/menuentry.c
===================================================================
--- uspace/lib/ui/src/menuentry.c	(revision 7470d97aaec04abd08ce2ad450cda9e6ff912bd4)
+++ uspace/lib/ui/src/menuentry.c	(revision 3c8c5808e19c14b9cbc2453181414284f31291a4)
@@ -203,4 +203,10 @@
 	ui_resource_t *res;
 
+	/*
+	 * This needs to work even if the menu is not open, so we cannot
+	 * use the menu's resource, which is only created after the menu
+	 * is open (and its window is created). Use the menu bar's
+	 * resource instead.
+	 */
 	res = mentry->menu->mbar->res;
 
@@ -223,4 +229,10 @@
 	gfx_coord_t width;
 
+	/*
+	 * This needs to work even if the menu is not open, so we cannot
+	 * use the menu's resource, which is only created after the menu
+	 * is open (and its window is created). Use the menu bar's
+	 * resource instead.
+	 */
 	res = menu->mbar->res;
 
@@ -256,4 +268,10 @@
 	gfx_coord_t vpad;
 
+	/*
+	 * This needs to work even if the menu is not open, so we cannot
+	 * use the menu's resource, which is only created after the menu
+	 * is open (and its window is created). Use the menu bar's
+	 * resource instead.
+	 */
 	res = mentry->menu->mbar->res;
 
@@ -294,5 +312,5 @@
 	errno_t rc;
 
-	res = mentry->menu->mbar->res;
+	res = ui_menu_get_res(mentry->menu);
 
 	ui_menu_entry_get_geom(mentry, pos, &geom);
@@ -379,6 +397,5 @@
 	if (mentry->inside) {
 		/* Close menu */
-		ui_menu_bar_select(mentry->menu->mbar,
-		    &mentry->menu->mbar->sel_pos, NULL);
+		ui_menu_bar_select(mentry->menu->mbar, NULL, NULL);
 
 		/* Call back */
@@ -487,5 +504,5 @@
 	gfx_coord_t width;
 
-	res = mentry->menu->mbar->res;
+	res = ui_menu_get_res(mentry->menu);
 
 	if (res->textmode) {
Index: uspace/lib/ui/src/popup.c
===================================================================
--- uspace/lib/ui/src/popup.c	(revision 3c8c5808e19c14b9cbc2453181414284f31291a4)
+++ uspace/lib/ui/src/popup.c	(revision 3c8c5808e19c14b9cbc2453181414284f31291a4)
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2021 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libui
+ * @{
+ */
+/**
+ * @file Popup window
+ */
+
+#include <errno.h>
+#include <gfx/context.h>
+//#include <io/kbd_event.h>
+#include <io/pos_event.h>
+#include <mem.h>
+#include <stdlib.h>
+#include <ui/control.h>
+#include <ui/popup.h>
+#include <ui/ui.h>
+#include <ui/window.h>
+#include "../private/popup.h"
+
+static void ui_popup_window_pos(ui_window_t *, void *, pos_event_t *);
+
+static ui_window_cb_t ui_popup_window_cb = {
+	.pos = ui_popup_window_pos
+};
+
+/** Initialize popup parameters structure.
+ *
+ * Popup parameters structure must always be initialized using this function
+ * first.
+ *
+ * @param params Popup parameters structure
+ */
+void ui_popup_params_init(ui_popup_params_t *params)
+{
+	memset(params, 0, sizeof(ui_popup_params_t));
+}
+
+/** Create new popup window.
+ *
+ * @param ui User interface
+ * @param params Popup parameters
+ * @param rpopup Place to store pointer to new popup window
+ * @return EOK on success or an error code
+ */
+errno_t ui_popup_create(ui_t *ui, ui_popup_params_t *params,
+    ui_popup_t **rpopup)
+{
+	ui_popup_t *popup;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t wparams;
+	errno_t rc;
+
+	popup = calloc(1, sizeof(ui_popup_t));
+	if (popup == NULL)
+		return ENOMEM;
+
+	ui_wnd_params_init(&wparams);
+	wparams.rect = params->rect;
+	wparams.caption = "";
+	wparams.style &= ~ui_wds_decorated;
+
+	rc = ui_window_create(ui, &wparams, &window);
+	if (rc != EOK)
+		goto error;
+
+	popup->ui = ui;
+	popup->window = window;
+
+	ui_window_set_cb(window, &ui_popup_window_cb, popup);
+
+	*rpopup = popup;
+	return EOK;
+error:
+	free(popup);
+	return rc;
+}
+
+/** Destroy popup window.
+ *
+ * @param popup Popup window or @c NULL
+ */
+void ui_popup_destroy(ui_popup_t *popup)
+{
+	if (popup == NULL)
+		return;
+
+	ui_window_destroy(popup->window);
+	free(popup);
+}
+
+/** Add control to popup window.
+ *
+ * Only one control can be added to a popup window. If more than one control
+ * is added, the results are undefined.
+ *
+ * @param popup Popup window
+ * @param control Control
+ * @return EOK on success, ENOMEM if out of memory
+ */
+void ui_popup_add(ui_popup_t *popup, ui_control_t *control)
+{
+	ui_window_add(popup->window, control);
+}
+
+/** Remove control from popup window.
+ *
+ * @param popup Popup window
+ * @param control Control
+ */
+void ui_popup_remove(ui_popup_t *popup, ui_control_t *control)
+{
+	ui_window_remove(popup->window, control);
+}
+
+/** Set popup window callbacks.
+ *
+ * @param popup Popup window
+ * @param cb Popup window callbacks
+ * @param arg Callback argument
+ */
+void ui_popup_set_cb(ui_popup_t *popup, ui_popup_cb_t *cb, void *arg)
+{
+	popup->cb = cb;
+	popup->arg = arg;
+}
+
+/** Get UI resource from popup window.
+ *
+ * @param window Window
+ * @return UI resource
+ */
+ui_resource_t *ui_popup_get_res(ui_popup_t *popup)
+{
+	return ui_window_get_res(popup->window);
+}
+
+/** Get popup window GC.
+ *
+ * @param popup Popup window
+ * @return GC (relative to popup window)
+ */
+gfx_context_t *ui_popup_get_gc(ui_popup_t *popup)
+{
+	return ui_window_get_gc(popup->window);
+}
+
+/** Handle position event in popup window.
+ *
+ * @param window Window
+ * @param arg Argument (ui_popup_t *)
+ * @param event Position event
+ */
+static void ui_popup_window_pos(ui_window_t *window, void *arg,
+    pos_event_t *event)
+{
+	ui_popup_t *popup = (ui_popup_t *)arg;
+
+	if (popup->cb != NULL && popup->cb->pos != NULL)
+		popup->cb->pos(popup, popup->arg, event);
+}
+
+/** @}
+ */
Index: uspace/lib/ui/src/window.c
===================================================================
--- uspace/lib/ui/src/window.c	(revision 7470d97aaec04abd08ce2ad450cda9e6ff912bd4)
+++ uspace/lib/ui/src/window.c	(revision 3c8c5808e19c14b9cbc2453181414284f31291a4)
@@ -336,5 +336,5 @@
  * is added, the results are undefined.
  *
- * @param fixed Fixed layout
+ * @param window Window
  * @param control Control
  * @return EOK on success, ENOMEM if out of memory
@@ -509,5 +509,5 @@
  *
  * @param window Window
- * @param cb Window decoration callbacks
+ * @param cb Window callbacks
  * @param arg Callback argument
  */
