win32: the menu bar is kept while the window has none, not built again

The menu bar belongs to the windowed state. It was destroyed whenever
the window went fullscreen, or was taken back from the last video
driver, and a new one built - made from its template, every item put
into the user's language - each time the window was windowed again:
once for every return from fullscreen and for every driver restart.
It is the same menu each time.

It is now taken off the window and kept, and put back when the window
is windowed again; a window that still has its menu keeps it. A new
one is built only when there is none kept. One kept is dropped when
the language changes (win32_menubar_rebuild()), so that the next
windowed window gets one in the new language.

Under Wine, retroarch.exe with OpenGL and the menu bar on, through
four fullscreen toggles and a driver restart: the window had its menu
bar each time it was windowed and none while fullscreen, and the menu
was built once where it used to be built four times.
samples/input/windows_wine_smoke holds the count in its two OpenGL
scenarios.

Not run on Windows; that the kept menu still answers a click was not
tried.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-10-04 00:32:35 +02:00
1 parent 6d323f62d8
commit 71a610e92a
4 files changed
+83 -16

No files matched your search

+61 -15
View File
@@ -1862,6 +1862,56 @@ static HWND win32_window_take_kept(void)
return hwnd;
}
/* The menu bar while the window has none.
*
* The menu bar belongs to the windowed state. It was destroyed
* whenever the window went fullscreen, or was taken back from the
* last driver, and a new one built - made from its template and
* every item put into the user's language - each time the window was
* windowed again: once for every return from fullscreen and every
* driver restart. It is the same menu each time.
*
* It is now taken off the window and kept, and put back when the
* window is windowed again. A new one is built only when there is
* none kept, and the kept one is dropped when the language changes
* (win32_menu_kept_drop(), from win32_menubar_rebuild()). A menu that
* is on no window belongs to no window: it outlives the window it
* was taken from and goes onto the next. */
static HMENU win32_menu_kept;
void win32_menu_kept_drop(void)
{
if (win32_menu_kept)
DestroyMenu(win32_menu_kept);
win32_menu_kept = NULL;
}
/* The window's menu bar, if it has one, taken off it and kept. */
static void win32_menu_put_away(HWND hwnd)
{
HMENU menu = GetMenu(hwnd);
if (!menu)
return;
SetMenu(hwnd, NULL);
if (win32_menu_kept && win32_menu_kept != menu)
DestroyMenu(win32_menu_kept);
win32_menu_kept = menu;
}
/* A menu bar for a windowed window: the kept one, or a new one. */
static HMENU win32_menu_take(void)
{
HMENU menu = win32_menu_kept;
win32_menu_kept = NULL;
if (!menu)
{
menu = win32_resources_create_menu();
win32_localize_menu(menu);
RARCH_DBG("[Win32] Menu bar built.\n");
}
return menu;
}
static bool win32_window_create(void *data, unsigned style,
RECT *mon_rect, unsigned width,
unsigned height, bool fullscreen)
@@ -1906,12 +1956,10 @@ static bool win32_window_create(void *data, unsigned style,
win32_wnd_taken = true;
free(title_local);
/* win32_set_window() gives a windowed window its menu again */
/* win32_set_window() gives a windowed window its menu again:
* the same one, which is kept in the meantime */
if (menu)
{
SetMenu(hwnd, NULL);
DestroyMenu(menu);
}
win32_menu_put_away(hwnd);
/* The style, place and size it would have been created with. A
* windowed window stays where it is - unless it was fullscreen,
* and is put where a new one would have been as far as that can
@@ -2390,9 +2438,12 @@ void win32_set_window(unsigned *width, unsigned *height,
rc_temp.right = (LONG)*height;
rc_temp.bottom = 0x7FFF;
menuItem = win32_resources_create_menu();
win32_localize_menu(menuItem);
SetMenu(main_window.hwnd, menuItem);
/* (a window that has one keeps it) */
if (!(menuItem = GetMenu(main_window.hwnd)))
{
menuItem = win32_menu_take();
SetMenu(main_window.hwnd, menuItem);
}
SendMessage(main_window.hwnd, WM_NCCALCSIZE, FALSE, (LPARAM)&rc_temp);
g_win32_resize_height = *height += rc_temp.top + rect->top;
@@ -2608,13 +2659,8 @@ bool win32_window_set_fullscreen(unsigned dims, bool fullscreen)
if (fullscreen)
{
/* the menu bar belongs to the windowed state */
HMENU menu = GetMenu(hwnd);
if (menu)
{
SetMenu(hwnd, NULL);
DestroyMenu(menu);
}
/* the menu bar belongs to the windowed state: kept until then */
win32_menu_put_away(hwnd);
x = mon_rect.left;
y = mon_rect.top;
}
+5
View File
@@ -171,6 +171,11 @@ void win32_check_window(void *data,
bool *quit,
bool *resize, unsigned *dims);
/* The menu bar kept while the window has none is dropped, so that the
* next windowed window gets one built anew: for when what is in it
* has changed (the language). */
void win32_menu_kept_drop(void);
void win32_set_window(unsigned *width, unsigned *height,
bool fullscreen, bool windowed_full, void *rect_data);
+13 -1
View File
@@ -77,6 +77,12 @@
# own state and must work as before. Telling two keyboards apart is
# samples/input/winraw_keyboards' to hold.
#
# The OpenGL scenarios also count how often the window's menu bar was
# built. It is taken off the window while it is fullscreen and put
# back when it is windowed again, and goes with a kept window through
# a driver restart: built once, where it used to be built again each
# time.
#
# ONLY=<part of a scenario's name> runs just the scenarios that match.
#
# And the window. In every scenario above it is left up across both
@@ -231,7 +237,7 @@ failures=0
# passes through
scenario() {
local name=$1 threaded=$2 want_bulk=$3 want_joy=$4 nomsg=$5
local log="$work/$name.log" try rc video bulk joy msg ok b_down b_up j_seq stale win off byname deferred kbd
local log="$work/$name.log" try rc video bulk joy msg ok b_down b_up j_seq stale win off byname deferred kbd menus
local want_video=${WANT_VIDEO:-3}
if [ -n "${ONLY:-}" ]; then
case "$name" in
@@ -277,6 +283,9 @@ scenario() {
if [ "${6:-raw}" = raw ]; then
[ "$kbd" -ge 1 ] || ok=no
fi
# the menu bar: built this many times, where it is asked
menus=$(count "$log" 'Win32\] Menu bar built')
[ -z "${WANT_MENUS:-}" ] || [ "$menus" = "$WANT_MENUS" ] || ok=no
deferred=$(count "$log" 'joypad driver is restarted by the poll')
if [ "$threaded" = true ]; then
[ "$deferred" = 1 ] || ok=no
@@ -311,6 +320,7 @@ scenario() {
"key events on a thread that is not the core's: $off (want 0)," \
"input driver picked by the setting's name: $byname (want 0)," \
"keyboards named by the raw input driver: $kbd (want at least 1 with raw input)," \
"menu bars built: $menus (want ${WANT_MENUS:-any})," \
"joypad restarts left to the poll: $deferred (want $([ "$threaded" = true ] && echo 1 || echo 0))" \
"$([ "$nomsg" = nomsg ] && echo ", driver instances with reports taken as messages: $msg (want 0)")"
sed 's/\x1b\[[0-9;]*m//g' "$log" | tr -d '\r' | grep -av "ALSA lib\|Playlist\]" | tail -25
@@ -330,8 +340,10 @@ WANT_WINDOW="2 0 2" RETROARCH_WINDOW_KEEP=2 scenario "window left up and not tak
EXTRA_CFG='input_player1_keyboard_index = "1"' \
scenario "the first port given a keyboard" true 1 2 nomsg
VIDEO_DRIVER=gl VIDEO_STARTED='Found GL context' WANT_VIDEO=2 WANT_WINDOW="1 1 0" \
WANT_MENUS=1 EXTRA_CFG='ui_menubar_enable = "true"' \
scenario "OpenGL" true 1 2 nomsg
VIDEO_DRIVER=gl VIDEO_STARTED='Found GL context' WANT_VIDEO=3 WANT_WINDOW="2 2 0" \
WANT_MENUS=1 EXTRA_CFG='ui_menubar_enable = "true"' \
RETROARCH_FULLSCREEN_IN_PLACE=0 scenario "OpenGL, toggle by restart" true 1 2 nomsg
if [ "$failures" != 0 ]; then
+4
View File
@@ -1607,6 +1607,10 @@ void win32_menubar_rebuild(void)
if (!hwnd)
return;
/* one kept for a window that has none now (fullscreen) is in the
* old language too */
win32_menu_kept_drop();
old_menu = GetMenu(hwnd);
/* No menubar is currently attached (e.g. fullscreen, or menubar
* disabled via ui_menubar_enable). Nothing to rebuild. */