mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
win32: pause cleanly during window size/move and menu loops
Second attempt at the problem91920289tried to solve, informed by its revert (7fbc868499). Dragging the title bar, resizing by the border or browsing a menu bar runs a modal loop inside DefWindowProc; with non-threaded video the run loop is on that thread and stops. Before this the client area went stale or black and the audio sink underran and popped on release. Now: on WM_ENTERSIZEMOVE / WM_ENTERMENULOOP the audio driver is stopped (unless RUNLOOP_FLAG_PAUSED is set - a user who paused keeps it paused) and a 16 ms timer re-presents the last frame at the current window size. Each tick makes the two calls the run loop makes per frame and nothing else: the driver's alive(), which is where win32_check_window() consumes WIN32_CMN_FLAG_RESIZED and arms the swapchain resize, then video_driver_cached_frame(). Nothing runs runloop_iterate(), the menu, input or the task queue from inside the modal loop; content pauses for the length of the drag, as in every other windowed game. Threaded video returns early at enter - the window is on the video thread and the run loop never blocked. One timer for the process with a stored owner HWND, so the companion and the main window cannot kill each other's; a depth counter for nested loops (system menu opened while sizing); abort on WM_CLOSE/WM_DESTROY/WM_QUIT with no audio restart. The companion's main wndproc routes the same messages; its floating dock panes are not wired in this change. No changes to runloop.c, ui_win32.c or the pace flags. Checked: tools/mingw_syntax_check.sh on the three .c files and --consumers-of win32_common.h (sdl3_gfx/uwp_egl_ctx fail identically on master); tools/companion_win32_test.sh under Wine. NOT yet run on Windows hardware: needs the row table (drag/resize/system menu/alt-tab/ companion menu bar/quit mid-drag, D3D11 and GL, vsync on/off, threaded on/off, paused) green from Tatsuya79 and sonninnos before merge.
This commit is contained in:
1 parent
e58643e67d
commit
6c0cedc945
4 files changed
+197
No files matched your search
@@ -130,6 +130,7 @@ typedef char win32_dwm_timing_info_size_check[
|
||||
#include "../../verbosity.h"
|
||||
#include "../../paths.h"
|
||||
#include "../../retroarch.h"
|
||||
#include "../../audio/audio_driver.h"
|
||||
#include "../../tasks/task_content.h"
|
||||
#include "../../tasks/tasks_internal.h"
|
||||
#include "../../core_info.h"
|
||||
@@ -686,6 +687,111 @@ uint16_t win32_get_keyboard_mods(void)
|
||||
return (uint16_t)retro_atomic_load_acquire_int(&win32_kb_mods);
|
||||
}
|
||||
|
||||
#if !defined(_XBOX)
|
||||
#ifndef WM_ENTERSIZEMOVE
|
||||
#define WM_ENTERSIZEMOVE 0x0231
|
||||
#endif
|
||||
#ifndef WM_EXITSIZEMOVE
|
||||
#define WM_EXITSIZEMOVE 0x0232
|
||||
#endif
|
||||
#ifndef WM_ENTERMENULOOP
|
||||
#define WM_ENTERMENULOOP 0x0211
|
||||
#endif
|
||||
#ifndef WM_EXITMENULOOP
|
||||
#define WM_EXITMENULOOP 0x0212
|
||||
#endif
|
||||
|
||||
/* Title-bar drags, border resizes and menu bars run a modal loop inside
|
||||
* DefWindowProc that does not return until the user lets go. With
|
||||
* non-threaded video the run loop is on this thread, so content pauses
|
||||
* for the duration - that is the norm for a windowed game and is not
|
||||
* changed here. What is done: the audio driver is stopped so the sink
|
||||
* does not underrun and pop, and a plain timer keeps the last frame on
|
||||
* screen at the current window size. Nothing in here runs the run
|
||||
* loop, the menu, input or the task queue; running those nested inside
|
||||
* a captured-mouse modal loop is what 91920289 did and why it was
|
||||
* reverted.
|
||||
*
|
||||
* Size/move and menu loops can nest (system menu opened while sizing):
|
||||
* arm on the first entry, disarm on the last exit. One timer for the
|
||||
* process; the window that armed it owns it, so the companion and the
|
||||
* main window never kill each other's. */
|
||||
#define WIN32_SIZEMOVE_TIMER_ID 0x5241
|
||||
|
||||
static uint8_t win32_sizemove_depth;
|
||||
static bool win32_sizemove_stopped_audio;
|
||||
static HWND win32_sizemove_timer_hwnd;
|
||||
|
||||
void win32_sizemove_enter(HWND hwnd)
|
||||
{
|
||||
if (win32_sizemove_depth++)
|
||||
return;
|
||||
/* The window lives on the video thread; the run loop is elsewhere
|
||||
* and keeps going on its own. */
|
||||
if (video_driver_is_threaded())
|
||||
return;
|
||||
|
||||
/* A user who pressed P already stopped the driver and must not get
|
||||
* it restarted on release. audio_driver_stop() returns false when
|
||||
* the driver is not alive, so the latch is a real transition. */
|
||||
win32_sizemove_stopped_audio = false;
|
||||
if (!(runloop_state_get_ptr()->flags & RUNLOOP_FLAG_PAUSED))
|
||||
win32_sizemove_stopped_audio = audio_driver_stop();
|
||||
|
||||
if (SetTimer(hwnd, WIN32_SIZEMOVE_TIMER_ID, 16, NULL))
|
||||
win32_sizemove_timer_hwnd = hwnd;
|
||||
}
|
||||
|
||||
void win32_sizemove_exit(HWND hwnd)
|
||||
{
|
||||
(void)hwnd;
|
||||
if (!win32_sizemove_depth || --win32_sizemove_depth)
|
||||
return;
|
||||
if (video_driver_is_threaded())
|
||||
return;
|
||||
|
||||
if (win32_sizemove_timer_hwnd)
|
||||
{
|
||||
KillTimer(win32_sizemove_timer_hwnd, WIN32_SIZEMOVE_TIMER_ID);
|
||||
win32_sizemove_timer_hwnd = NULL;
|
||||
}
|
||||
/* A failed start clears AUDIO_FLAG_ACTIVE for the session; say so. */
|
||||
if (win32_sizemove_stopped_audio && !audio_driver_start(false))
|
||||
RARCH_WARN("[Win32] Audio did not restart after a window size/move.\n");
|
||||
win32_sizemove_stopped_audio = false;
|
||||
}
|
||||
|
||||
/* A routed window is going away mid-drag. No audio restart: the driver
|
||||
* may already be gone, and audio_driver_start() failing mutes the
|
||||
* session. */
|
||||
void win32_sizemove_abort(void)
|
||||
{
|
||||
if (win32_sizemove_timer_hwnd)
|
||||
KillTimer(win32_sizemove_timer_hwnd, WIN32_SIZEMOVE_TIMER_ID);
|
||||
win32_sizemove_timer_hwnd = NULL;
|
||||
win32_sizemove_depth = 0;
|
||||
win32_sizemove_stopped_audio = false;
|
||||
}
|
||||
|
||||
/* WM_TIMER with WIN32_SIZEMOVE_TIMER_ID, delivered on the thread that
|
||||
* owns the driver. The same two calls the run loop makes per frame and
|
||||
* nothing else: the driver's alive() is where win32_check_window()
|
||||
* consumes WIN32_CMN_FLAG_RESIZED and arms the swapchain resize, and
|
||||
* video_driver_cached_frame() then presents the last frame into the
|
||||
* resized chain - the pause picture. current_video and data have
|
||||
* independent lifetimes during teardown, hence both checks. */
|
||||
void win32_sizemove_tick(void)
|
||||
{
|
||||
video_driver_state_t *video_st = video_state_get_ptr();
|
||||
|
||||
if (!win32_sizemove_depth || video_driver_is_threaded())
|
||||
return;
|
||||
if (video_st->current_video && video_st->data)
|
||||
video_st->current_video->alive(video_st->data);
|
||||
video_driver_cached_frame();
|
||||
}
|
||||
#endif
|
||||
|
||||
static LRESULT CALLBACK wnd_proc_common(
|
||||
bool *quit, HWND hwnd, UINT message,
|
||||
WPARAM wparam, LPARAM lparam)
|
||||
@@ -731,12 +837,32 @@ static LRESULT CALLBACK wnd_proc_common(
|
||||
case WM_CLOSE:
|
||||
case WM_DESTROY:
|
||||
case WM_QUIT:
|
||||
#if !defined(_XBOX)
|
||||
win32_sizemove_abort();
|
||||
#endif
|
||||
g_win32_flags |= WIN32_CMN_FLAG_QUIT;
|
||||
*quit = true;
|
||||
/* fall-through */
|
||||
case WM_MOVE:
|
||||
win32_save_position();
|
||||
break;
|
||||
#if !defined(_XBOX)
|
||||
case WM_ENTERSIZEMOVE:
|
||||
case WM_ENTERMENULOOP:
|
||||
win32_sizemove_enter(hwnd);
|
||||
break;
|
||||
case WM_EXITSIZEMOVE:
|
||||
case WM_EXITMENULOOP:
|
||||
win32_sizemove_exit(hwnd);
|
||||
break;
|
||||
case WM_TIMER:
|
||||
/* Someone else's timer falls through to DefWindowProc. */
|
||||
if (wparam != WIN32_SIZEMOVE_TIMER_ID)
|
||||
break;
|
||||
win32_sizemove_tick();
|
||||
*quit = true;
|
||||
return 0;
|
||||
#endif
|
||||
case WM_SIZE:
|
||||
/* Do not send resize message if we minimize. */
|
||||
if ( wparam != SIZE_MAXHIDE
|
||||
@@ -951,6 +1077,13 @@ static LRESULT CALLBACK wnd_proc_common_internal(HWND hwnd,
|
||||
case WM_QUIT:
|
||||
case WM_MOVE:
|
||||
case WM_SIZE:
|
||||
#if !defined(_XBOX)
|
||||
case WM_ENTERSIZEMOVE:
|
||||
case WM_EXITSIZEMOVE:
|
||||
case WM_ENTERMENULOOP:
|
||||
case WM_EXITMENULOOP:
|
||||
case WM_TIMER:
|
||||
#endif
|
||||
case WM_GETMINMAXINFO:
|
||||
case WM_COMMAND:
|
||||
#ifdef HAVE_THREADS
|
||||
@@ -1036,6 +1169,13 @@ static LRESULT CALLBACK wnd_proc_winraw_common_internal(HWND hwnd,
|
||||
case WM_QUIT:
|
||||
case WM_MOVE:
|
||||
case WM_SIZE:
|
||||
#if !defined(_XBOX)
|
||||
case WM_ENTERSIZEMOVE:
|
||||
case WM_EXITSIZEMOVE:
|
||||
case WM_ENTERMENULOOP:
|
||||
case WM_EXITMENULOOP:
|
||||
case WM_TIMER:
|
||||
#endif
|
||||
case WM_GETMINMAXINFO:
|
||||
case WM_COMMAND:
|
||||
#ifdef HAVE_THREADS
|
||||
@@ -1247,6 +1387,13 @@ static LRESULT CALLBACK wnd_proc_common_dinput_internal(HWND hwnd,
|
||||
case WM_QUIT:
|
||||
case WM_MOVE:
|
||||
case WM_SIZE:
|
||||
#if !defined(_XBOX)
|
||||
case WM_ENTERSIZEMOVE:
|
||||
case WM_EXITSIZEMOVE:
|
||||
case WM_ENTERMENULOOP:
|
||||
case WM_EXITMENULOOP:
|
||||
case WM_TIMER:
|
||||
#endif
|
||||
case WM_GETMINMAXINFO:
|
||||
case WM_COMMAND:
|
||||
#ifdef HAVE_THREADS
|
||||
|
||||
@@ -132,6 +132,17 @@ retro_time_t win32_dwm_last_vblank_time(void);
|
||||
void win32_clip_window(bool grab);
|
||||
#endif
|
||||
|
||||
#if !defined(_XBOX)
|
||||
/* Size/move and menu-loop handling for any window whose wndproc runs on
|
||||
* the run loop's thread: content pauses, audio is stopped cleanly and a
|
||||
* timer re-presents the last frame. See win32_common.c. */
|
||||
#define WIN32_SIZEMOVE_TIMER_ID 0x5241
|
||||
void win32_sizemove_enter(HWND hwnd);
|
||||
void win32_sizemove_exit(HWND hwnd);
|
||||
void win32_sizemove_tick(void);
|
||||
void win32_sizemove_abort(void);
|
||||
#endif
|
||||
|
||||
void win32_check_window(void *data,
|
||||
bool *quit,
|
||||
bool *resize, unsigned *width, unsigned *height);
|
||||
|
||||
@@ -53,6 +53,10 @@
|
||||
ui_window_win32_t main_window;
|
||||
static int stub_menu_loop_calls;
|
||||
LRESULT win32_menu_loop(HWND hwnd, WPARAM wparam) { (void)hwnd; (void)wparam; stub_menu_loop_calls++; return 0; }
|
||||
void win32_sizemove_enter(HWND h) { (void)h; }
|
||||
void win32_sizemove_exit(HWND h) { (void)h; }
|
||||
void win32_sizemove_tick(void) { }
|
||||
void win32_sizemove_abort(void) { }
|
||||
|
||||
extern settings_t test_settings;
|
||||
extern runloop_state_t test_runloop;
|
||||
|
||||
@@ -137,6 +137,20 @@
|
||||
|
||||
/* Control / command IDs. Kept clear of the ID_M_* range in ui_win32.h. */
|
||||
#include "ui_win32_companion_ids.h"
|
||||
|
||||
/* Pre-Win98 SDKs lack these; the messages themselves date from 95. */
|
||||
#ifndef WM_ENTERSIZEMOVE
|
||||
#define WM_ENTERSIZEMOVE 0x0231
|
||||
#endif
|
||||
#ifndef WM_EXITSIZEMOVE
|
||||
#define WM_EXITSIZEMOVE 0x0232
|
||||
#endif
|
||||
#ifndef WM_ENTERMENULOOP
|
||||
#define WM_ENTERMENULOOP 0x0211
|
||||
#endif
|
||||
#ifndef WM_EXITMENULOOP
|
||||
#define WM_EXITMENULOOP 0x0212
|
||||
#endif
|
||||
#include "../companion/companion_dock.h"
|
||||
|
||||
typedef struct ui_companion_win32_wimp
|
||||
@@ -3914,8 +3928,29 @@ static LRESULT CALLBACK cw_wndproc(HWND hwnd, UINT msg,
|
||||
|
||||
case WM_CLOSE:
|
||||
/* Closing the companion never quits RetroArch. */
|
||||
win32_sizemove_abort();
|
||||
ShowWindow(hwnd, SW_HIDE);
|
||||
return 0;
|
||||
case WM_DESTROY:
|
||||
win32_sizemove_abort();
|
||||
break;
|
||||
|
||||
/* Dragging or sizing this window, or browsing its menu bar, runs
|
||||
* a modal loop on the run loop's thread exactly as the main window
|
||||
* does; same handling, no run loop access. */
|
||||
case WM_ENTERSIZEMOVE:
|
||||
case WM_ENTERMENULOOP:
|
||||
win32_sizemove_enter(hwnd);
|
||||
break;
|
||||
case WM_EXITSIZEMOVE:
|
||||
case WM_EXITMENULOOP:
|
||||
win32_sizemove_exit(hwnd);
|
||||
break;
|
||||
case WM_TIMER:
|
||||
if (wparam != WIN32_SIZEMOVE_TIMER_ID)
|
||||
break;
|
||||
win32_sizemove_tick();
|
||||
return 0;
|
||||
|
||||
/* The docks: strips, tabs and gaps are the window's own surface. */
|
||||
case WM_PAINT:
|
||||
|
||||
Reference in new issue
Block a user