mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 08:10:30 -10:00
win32: remove the modal size/move run loop pump
Reverts the effect of91920289e3. Running runloop_iterate() from a tick posted into DefWindowProc's size/move and menu loops re-enters the menu and input drivers while the modal loop has the mouse captured: window drag stalls, focus sticks, the menu bar stops taking clicks after the first, and menu hover sounds keep firing on a hidden window. Bisected by Tatsuya79 to Sept 4..5; sonninnos confirmed the symptoms go away once the pump is disabled. The win32 side is removed outright: the clock thread, the WM_TIMER fallback, the tick handler, the message routing in every wndproc and the companion window, and the mouse-message filtering in the ui driver's pump. RUNLOOP_PACE_EXTERNAL stays in runloop.c since the pacing rework ine0acbe2639is built on it; nothing sets it now.
This commit is contained in:
1 parent
300b32b5ee
commit
7fbc868499
5 files changed
+1
-407
No files matched your search
@@ -617,255 +617,6 @@ uint16_t win32_get_keyboard_mods(void)
|
||||
return (uint16_t)retro_atomic_load_acquire_int(&win32_kb_mods);
|
||||
}
|
||||
|
||||
#if !defined(_XBOX)
|
||||
/* A title bar drag, a border resize, or an open menu enters a modal
|
||||
* loop inside DefWindowProc that does not return until the user lets
|
||||
* go. The run loop lives on this thread, so for that whole time no
|
||||
* frame runs, no audio is written, and the frame limiter falls behind
|
||||
* by the length of the drag.
|
||||
*
|
||||
* The loop does dispatch posted messages, so for its duration a helper
|
||||
* thread sleeps at the content rate on the high resolution timer and
|
||||
* posts a tick; the tick runs one iteration with RUNLOOP_PACE_EXTERNAL
|
||||
* set, which makes the iteration return as soon as the frame is out
|
||||
* instead of sleeping until the next one. Content and audio keep their
|
||||
* rate, and the loop has the mouse back within a frame's work, not a
|
||||
* frame's period. Where there is no thread to be had the fallback is
|
||||
* WM_TIMER, whose coarse cadence still beats a freeze.
|
||||
*
|
||||
* Every API here dates from Windows 95; the constants an older SDK
|
||||
* lacks are defined below. */
|
||||
|
||||
#ifndef USER_TIMER_MINIMUM
|
||||
#define USER_TIMER_MINIMUM 0x0000000A
|
||||
#endif
|
||||
#ifndef WM_ENTERMENULOOP
|
||||
#define WM_ENTERMENULOOP 0x0211
|
||||
#endif
|
||||
#ifndef WM_EXITMENULOOP
|
||||
#define WM_EXITMENULOOP 0x0212
|
||||
#endif
|
||||
#ifndef WM_ENTERSIZEMOVE
|
||||
#define WM_ENTERSIZEMOVE 0x0231
|
||||
#endif
|
||||
#ifndef WM_EXITSIZEMOVE
|
||||
#define WM_EXITSIZEMOVE 0x0232
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef HAVE_THREADS
|
||||
sthread_t *thread;
|
||||
/* Signalled to start a clock session; the thread parks on it
|
||||
* between sessions rather than being created per drag, which
|
||||
* would also leave retro_sleep()'s per-thread timer handle behind
|
||||
* each time. Both events auto-reset. */
|
||||
HANDLE wake;
|
||||
HANDLE parked;
|
||||
#endif
|
||||
HWND hwnd;
|
||||
/* Set once a tick is posted and cleared when it is consumed, so a
|
||||
* main thread that falls behind gets one message, not a backlog. */
|
||||
retro_atomic_int_t pending;
|
||||
retro_atomic_int_t stop;
|
||||
retro_atomic_int_t quit;
|
||||
/* Size/move and menu loops can nest (a menu opened from the system
|
||||
* menu of a window being sized is the one case), so arm on the
|
||||
* first entry and disarm on the last exit. */
|
||||
uint8_t depth;
|
||||
} win32_modal_t;
|
||||
|
||||
static win32_modal_t win32_modal;
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
static void win32_modal_clock(void *data)
|
||||
{
|
||||
win32_modal_t *m = (win32_modal_t*)data;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
retro_time_t next;
|
||||
|
||||
WaitForSingleObject(m->wake, INFINITE);
|
||||
if (retro_atomic_load_acquire_int(&m->quit))
|
||||
break;
|
||||
|
||||
next = cpu_features_get_time_usec();
|
||||
while (!retro_atomic_load_acquire_int(&m->stop))
|
||||
{
|
||||
/* core_hz is written on the main thread and only read here;
|
||||
* a torn read costs one oddly timed tick, nothing worse. */
|
||||
retro_time_t period = runloop_content_frame_time_us(
|
||||
video_state_get_ptr()->core_hz);
|
||||
retro_time_t now = cpu_features_get_time_usec();
|
||||
retro_time_t wait;
|
||||
|
||||
next += period;
|
||||
wait = next - now;
|
||||
if (wait > 0)
|
||||
retro_sleep_us((unsigned)wait);
|
||||
else
|
||||
next = now; /* fell behind: no burst, just resume */
|
||||
|
||||
if (retro_atomic_cas_int(&m->pending, 0, 1))
|
||||
PostMessage(m->hwnd, WM_RA_MODAL_TICK, 0, 0);
|
||||
}
|
||||
|
||||
SetEvent(m->parked);
|
||||
}
|
||||
}
|
||||
|
||||
/* One thread for the life of the process, created on first use. NULL
|
||||
* on any failure, in which case the WM_TIMER fallback is used. */
|
||||
static sthread_t *win32_modal_thread_get(win32_modal_t *m)
|
||||
{
|
||||
if (m->thread)
|
||||
return m->thread;
|
||||
|
||||
if (!m->wake && !(m->wake = CreateEvent(NULL, FALSE, FALSE, NULL)))
|
||||
return NULL;
|
||||
if (!m->parked && !(m->parked = CreateEvent(NULL, FALSE, FALSE, NULL)))
|
||||
return NULL;
|
||||
|
||||
retro_atomic_store_release_int(&m->quit, 0);
|
||||
return (m->thread = sthread_create(win32_modal_clock, m));
|
||||
}
|
||||
#endif
|
||||
|
||||
void win32_modal_enter(HWND hwnd)
|
||||
{
|
||||
win32_modal_t *m = &win32_modal;
|
||||
|
||||
if (m->depth++)
|
||||
return;
|
||||
|
||||
/* With threaded video this window, and so this loop, belong to the
|
||||
* video thread; the run loop is elsewhere and is not blocked. */
|
||||
if (video_driver_is_threaded())
|
||||
return;
|
||||
|
||||
m->hwnd = hwnd;
|
||||
retro_atomic_store_release_int(&m->pending, 0);
|
||||
retro_atomic_store_release_int(&m->stop, 0);
|
||||
runloop_state_get_ptr()->pace_external = true;
|
||||
g_win32_flags |= WIN32_CMN_FLAG_MODAL_TIMER;
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
if (win32_modal_thread_get(m))
|
||||
{
|
||||
SetEvent(m->wake);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
SetTimer(hwnd, WIN32_MODAL_TIMER_ID, USER_TIMER_MINIMUM, NULL);
|
||||
}
|
||||
|
||||
/* Stops the clock whatever the nesting depth: the last exit message,
|
||||
* a quit from inside a tick, and a window going away mid-drag all
|
||||
* end here. */
|
||||
static void win32_modal_stop(HWND hwnd)
|
||||
{
|
||||
win32_modal_t *m = &win32_modal;
|
||||
|
||||
m->depth = 0;
|
||||
if (!(g_win32_flags & WIN32_CMN_FLAG_MODAL_TIMER))
|
||||
return;
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
if (m->thread)
|
||||
{
|
||||
/* The clock is inside a sleep of at most one frame; once it has
|
||||
* parked it cannot post again until the next wake. */
|
||||
retro_atomic_store_release_int(&m->stop, 1);
|
||||
WaitForSingleObject(m->parked, INFINITE);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
KillTimer(hwnd, WIN32_MODAL_TIMER_ID);
|
||||
|
||||
/* A tick that was posted before the stop is still in the queue;
|
||||
* the flag clear below makes it a no-op when it arrives. */
|
||||
g_win32_flags &= ~WIN32_CMN_FLAG_MODAL_TIMER;
|
||||
runloop_state_get_ptr()->pace_external = false;
|
||||
}
|
||||
|
||||
void win32_modal_exit(HWND hwnd)
|
||||
{
|
||||
win32_modal_t *m = &win32_modal;
|
||||
|
||||
if (!m->depth || --m->depth)
|
||||
return;
|
||||
win32_modal_stop(hwnd);
|
||||
}
|
||||
|
||||
/* The window is going away. Any session is ended and the thread, if
|
||||
* one was ever started, is joined. */
|
||||
void win32_modal_window_destroyed(HWND hwnd)
|
||||
{
|
||||
win32_modal_t *m = &win32_modal;
|
||||
/* Only the window whose loop is being clocked ends the session;
|
||||
* another window closing during a drag is none of our business. */
|
||||
if (m->hwnd == hwnd)
|
||||
win32_modal_stop(hwnd);
|
||||
}
|
||||
|
||||
static void win32_modal_deinit(HWND hwnd)
|
||||
{
|
||||
win32_modal_t *m = &win32_modal;
|
||||
|
||||
win32_modal_stop(hwnd);
|
||||
#ifdef HAVE_THREADS
|
||||
if (m->thread)
|
||||
{
|
||||
retro_atomic_store_release_int(&m->quit, 1);
|
||||
SetEvent(m->wake);
|
||||
sthread_join(m->thread);
|
||||
m->thread = NULL;
|
||||
}
|
||||
if (m->wake)
|
||||
{
|
||||
CloseHandle(m->wake);
|
||||
m->wake = NULL;
|
||||
}
|
||||
if (m->parked)
|
||||
{
|
||||
CloseHandle(m->parked);
|
||||
m->parked = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void win32_modal_tick(HWND hwnd)
|
||||
{
|
||||
win32_modal_t *m = &win32_modal;
|
||||
int ret;
|
||||
|
||||
retro_atomic_store_release_int(&m->pending, 0);
|
||||
|
||||
if (!(g_win32_flags & WIN32_CMN_FLAG_MODAL_TIMER))
|
||||
return;
|
||||
|
||||
/* The iteration pumps this thread's queue, and a tick pulled out
|
||||
* there would land here again, inside the iteration it belongs to. */
|
||||
if (g_win32_flags & WIN32_CMN_FLAG_MODAL_TICK)
|
||||
return;
|
||||
|
||||
/* A quit that was already accepted is reported by the main loop's
|
||||
* own next iteration; repeating it here would run the shutdown
|
||||
* work once per tick until the loop lets go. */
|
||||
if (runloop_state_get_ptr()->flags & RUNLOOP_FLAG_SHUTDOWN_INITIATED)
|
||||
return;
|
||||
|
||||
g_win32_flags |= WIN32_CMN_FLAG_MODAL_TICK;
|
||||
ret = runloop_iterate();
|
||||
task_queue_check();
|
||||
g_win32_flags &= ~WIN32_CMN_FLAG_MODAL_TICK;
|
||||
|
||||
if (ret == -1)
|
||||
win32_modal_stop(hwnd);
|
||||
}
|
||||
#endif /* !defined(_XBOX) */
|
||||
|
||||
static LRESULT CALLBACK wnd_proc_common(
|
||||
bool *quit, HWND hwnd, UINT message,
|
||||
WPARAM wparam, LPARAM lparam)
|
||||
@@ -911,32 +662,12 @@ static LRESULT CALLBACK wnd_proc_common(
|
||||
case WM_CLOSE:
|
||||
case WM_DESTROY:
|
||||
case WM_QUIT:
|
||||
#if !defined(_XBOX)
|
||||
win32_modal_deinit(hwnd);
|
||||
#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_modal_enter(hwnd);
|
||||
break;
|
||||
case WM_EXITSIZEMOVE:
|
||||
case WM_EXITMENULOOP:
|
||||
win32_modal_exit(hwnd);
|
||||
break;
|
||||
case WM_RA_MODAL_TICK:
|
||||
win32_modal_tick(hwnd);
|
||||
break;
|
||||
case WM_TIMER:
|
||||
if (wparam == WIN32_MODAL_TIMER_ID)
|
||||
win32_modal_tick(hwnd);
|
||||
break;
|
||||
#endif
|
||||
case WM_SIZE:
|
||||
/* Do not send resize message if we minimize. */
|
||||
if ( wparam != SIZE_MAXHIDE
|
||||
@@ -1153,14 +884,6 @@ static LRESULT CALLBACK wnd_proc_common_internal(HWND hwnd,
|
||||
case WM_SIZE:
|
||||
case WM_GETMINMAXINFO:
|
||||
case WM_COMMAND:
|
||||
#if !defined(_XBOX)
|
||||
case WM_ENTERSIZEMOVE:
|
||||
case WM_EXITSIZEMOVE:
|
||||
case WM_ENTERMENULOOP:
|
||||
case WM_EXITMENULOOP:
|
||||
case WM_TIMER:
|
||||
case WM_RA_MODAL_TICK:
|
||||
#endif
|
||||
#ifdef HAVE_THREADS
|
||||
case WM_BROWSER_OPEN_RESULT:
|
||||
case WM_BROWSER_CANCELLED:
|
||||
@@ -1246,14 +969,6 @@ static LRESULT CALLBACK wnd_proc_winraw_common_internal(HWND hwnd,
|
||||
case WM_SIZE:
|
||||
case WM_GETMINMAXINFO:
|
||||
case WM_COMMAND:
|
||||
#if !defined(_XBOX)
|
||||
case WM_ENTERSIZEMOVE:
|
||||
case WM_EXITSIZEMOVE:
|
||||
case WM_ENTERMENULOOP:
|
||||
case WM_EXITMENULOOP:
|
||||
case WM_TIMER:
|
||||
case WM_RA_MODAL_TICK:
|
||||
#endif
|
||||
#ifdef HAVE_THREADS
|
||||
case WM_BROWSER_OPEN_RESULT:
|
||||
case WM_BROWSER_CANCELLED:
|
||||
@@ -1465,14 +1180,6 @@ static LRESULT CALLBACK wnd_proc_common_dinput_internal(HWND hwnd,
|
||||
case WM_SIZE:
|
||||
case WM_GETMINMAXINFO:
|
||||
case WM_COMMAND:
|
||||
#if !defined(_XBOX)
|
||||
case WM_ENTERSIZEMOVE:
|
||||
case WM_EXITSIZEMOVE:
|
||||
case WM_ENTERMENULOOP:
|
||||
case WM_EXITMENULOOP:
|
||||
case WM_TIMER:
|
||||
case WM_RA_MODAL_TICK:
|
||||
#endif
|
||||
#ifdef HAVE_THREADS
|
||||
case WM_BROWSER_OPEN_RESULT:
|
||||
case WM_BROWSER_CANCELLED:
|
||||
|
||||
@@ -70,40 +70,11 @@ enum win32_common_flags
|
||||
WIN32_CMN_FLAG_TASKBAR_CREATED = (1 << 2),
|
||||
WIN32_CMN_FLAG_RESTORE_DESKTOP = (1 << 3),
|
||||
WIN32_CMN_FLAG_INITED = (1 << 4),
|
||||
WIN32_CMN_FLAG_SWAP_MOUSE_BTNS = (1 << 5),
|
||||
/* The window is inside a modal size/move or menu loop owned by
|
||||
* DefWindowProc, and a timer is driving the run loop from within
|
||||
* it. */
|
||||
WIN32_CMN_FLAG_MODAL_TIMER = (1 << 6),
|
||||
/* A run loop iteration started from that timer is in progress. */
|
||||
WIN32_CMN_FLAG_MODAL_TICK = (1 << 7)
|
||||
WIN32_CMN_FLAG_SWAP_MOUSE_BTNS = (1 << 5)
|
||||
};
|
||||
|
||||
extern uint8_t g_win32_flags;
|
||||
|
||||
#if !defined(_XBOX)
|
||||
/* Modal size/move and menu loops (DefWindowProc runs them on this
|
||||
* thread and returns only when they end) are clocked so the run loop
|
||||
* keeps iterating: WM_ENTERSIZEMOVE / WM_ENTERMENULOOP -> _enter,
|
||||
* WM_EXITSIZEMOVE / WM_EXITMENULOOP -> _exit, and the ticks arrive as
|
||||
* WM_RA_MODAL_TICK or as WM_TIMER with WIN32_MODAL_TIMER_ID -> _tick.
|
||||
* Any top-level window on the main thread - the companion window
|
||||
* included - must route these, or dragging it freezes RetroArch.
|
||||
* Chosen not to collide with anything else that might use timers or
|
||||
* WM_APP messages on those windows. */
|
||||
#ifndef WM_APP
|
||||
#define WM_APP 0x8000
|
||||
#endif
|
||||
#define WIN32_MODAL_TIMER_ID 0x5241
|
||||
#define WM_RA_MODAL_TICK (WM_APP + 0x52)
|
||||
|
||||
void win32_modal_enter(HWND hwnd);
|
||||
void win32_modal_exit(HWND hwnd);
|
||||
void win32_modal_tick(HWND hwnd);
|
||||
/* Ends the session if @hwnd is the window being clocked. */
|
||||
void win32_modal_window_destroyed(HWND hwnd);
|
||||
#endif
|
||||
|
||||
#if !defined(_XBOX)
|
||||
extern unsigned g_win32_resize_width;
|
||||
extern unsigned g_win32_resize_height;
|
||||
|
||||
@@ -51,10 +51,6 @@
|
||||
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_modal_enter(HWND h) { (void)h; }
|
||||
void win32_modal_exit(HWND h) { (void)h; }
|
||||
void win32_modal_tick(HWND h) { (void)h; }
|
||||
void win32_modal_window_destroyed(HWND h) { (void)h; }
|
||||
|
||||
extern settings_t test_settings;
|
||||
extern runloop_state_t test_runloop;
|
||||
|
||||
@@ -100,24 +100,6 @@ static void ui_application_win32_process_events(void)
|
||||
{
|
||||
MSG msg;
|
||||
|
||||
/* Called from a run loop iteration that a modal size/move or menu
|
||||
* loop is driving from its timer (win32_common.c). That loop has
|
||||
* the mouse captured and reads its own movement and button-up from
|
||||
* the queue; taking those here would leave it tracking nothing.
|
||||
* Everything else - keyboard, raw input, WM_TIMER, posted commands
|
||||
* - is dispatched as usual. Ranges are numeric so that they do not
|
||||
* move with the SDK's idea of WM_MOUSELAST. */
|
||||
if (g_win32_flags & WIN32_CMN_FLAG_MODAL_TICK)
|
||||
{
|
||||
while (PeekMessage(&msg, 0, 0x0000, 0x009F, PM_REMOVE))
|
||||
ui_application_win32_dispatch(&msg);
|
||||
while (PeekMessage(&msg, 0, 0x00B0, 0x01FF, PM_REMOVE))
|
||||
ui_application_win32_dispatch(&msg);
|
||||
while (PeekMessage(&msg, 0, 0x0210, 0xFFFF, PM_REMOVE))
|
||||
ui_application_win32_dispatch(&msg);
|
||||
return;
|
||||
}
|
||||
|
||||
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
|
||||
ui_application_win32_dispatch(&msg);
|
||||
}
|
||||
|
||||
@@ -130,20 +130,6 @@
|
||||
* cut, so appends stay O(line) instead of the control's O(text). */
|
||||
#define COMPANION_WIN32_LOG_MAX (256 * 1024)
|
||||
|
||||
/* Pre-Win98 SDKs lack these; the messages themselves date from 95. */
|
||||
#ifndef WM_ENTERMENULOOP
|
||||
#define WM_ENTERMENULOOP 0x0211
|
||||
#endif
|
||||
#ifndef WM_EXITMENULOOP
|
||||
#define WM_EXITMENULOOP 0x0212
|
||||
#endif
|
||||
#ifndef WM_ENTERSIZEMOVE
|
||||
#define WM_ENTERSIZEMOVE 0x0231
|
||||
#endif
|
||||
#ifndef WM_EXITSIZEMOVE
|
||||
#define WM_EXITSIZEMOVE 0x0232
|
||||
#endif
|
||||
|
||||
/* Control / command IDs. Kept clear of the ID_M_* range in ui_win32.h. */
|
||||
#include "ui_win32_companion_ids.h"
|
||||
|
||||
@@ -2779,27 +2765,6 @@ static LRESULT CALLBACK cw_cores_wndproc(HWND hwnd, UINT msg,
|
||||
case WM_CLOSE:
|
||||
ShowWindow(hwnd, SW_HIDE);
|
||||
return 0;
|
||||
case WM_DESTROY:
|
||||
win32_modal_window_destroyed(hwnd);
|
||||
break;
|
||||
case WM_ENTERSIZEMOVE:
|
||||
case WM_ENTERMENULOOP:
|
||||
win32_modal_enter(hwnd);
|
||||
break;
|
||||
case WM_EXITSIZEMOVE:
|
||||
case WM_EXITMENULOOP:
|
||||
win32_modal_exit(hwnd);
|
||||
break;
|
||||
case WM_RA_MODAL_TICK:
|
||||
win32_modal_tick(hwnd);
|
||||
return 0;
|
||||
case WM_TIMER:
|
||||
if (wparam == WIN32_MODAL_TIMER_ID)
|
||||
{
|
||||
win32_modal_tick(hwnd);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case WM_COMMAND:
|
||||
if (!w)
|
||||
break;
|
||||
@@ -3079,33 +3044,6 @@ static LRESULT CALLBACK cw_wndproc(HWND hwnd, UINT msg,
|
||||
ShowWindow(hwnd, SW_HIDE);
|
||||
return 0;
|
||||
|
||||
case WM_DESTROY:
|
||||
win32_modal_window_destroyed(hwnd);
|
||||
break;
|
||||
|
||||
/* Dragging or sizing this window, or browsing its menu bar, runs a
|
||||
* modal loop inside DefWindowProc on the main thread. Clock the
|
||||
* run loop through it exactly as the main window does, or
|
||||
* RetroArch's video stops for the duration. */
|
||||
case WM_ENTERSIZEMOVE:
|
||||
case WM_ENTERMENULOOP:
|
||||
win32_modal_enter(hwnd);
|
||||
break;
|
||||
case WM_EXITSIZEMOVE:
|
||||
case WM_EXITMENULOOP:
|
||||
win32_modal_exit(hwnd);
|
||||
break;
|
||||
case WM_RA_MODAL_TICK:
|
||||
win32_modal_tick(hwnd);
|
||||
return 0;
|
||||
case WM_TIMER:
|
||||
if (wparam == WIN32_MODAL_TIMER_ID)
|
||||
{
|
||||
win32_modal_tick(hwnd);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
/* Splitter */
|
||||
case WM_SETCURSOR:
|
||||
if (w && (HWND)wparam == hwnd && LOWORD(lparam) == HTCLIENT)
|
||||
|
||||
Reference in new issue
Block a user