Skip to content

Commit 27992f1

Browse files
RyeMuttclaude
andcommitted
Fix macOS momentum scrolling: apply SDL hints before first video init
SDL_HINT_MAC_SCROLL_MOMENTUM is consumed exactly once by the Cocoa backend's registerUserDefaults during the first SDL_InitSubSystem( SDL_INIT_VIDEO), which registers AppleMomentumScrollSupported. The splash screen brings video up before init_sdl() set the hint, so SDL read the unset hint (default off) and momentum-phase scroll events were never delivered. The later hint in init_sdl() was dead because video was already up (ref-counted), so registerUserDefaults never re-ran. Extract the hints into set_sdl_hints() (once-guarded) and call it before the first video init from every entry point that can reach it first: the splash screen, init_sdl(), and OSMessageBoxSDL (early fatal dialogs route through Cocoa_RegisterApp too). Also restores SDL_HINT_MAC_PRESS_AND_HOLD, which fed the same registerUserDefaults and was being applied just as late. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent c896b93 commit 27992f1

3 files changed

Lines changed: 66 additions & 29 deletions

File tree

indra/llwindow/llsdl.cpp

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -72,37 +72,17 @@ void sdl_logger(void *userdata, int category, SDL_LogPriority priority, const ch
7272

7373
#endif
7474

75-
void init_sdl(const std::string& app_name)
75+
void set_sdl_hints()
7676
{
77-
#ifndef LL_SDL_WINDOW
78-
if (!gSDLMainHandled)
79-
{
80-
SDL_SetMainReady();
81-
}
82-
83-
SDL_SetLogOutputFunction(&sdl_logger, nullptr);
84-
85-
const int c_sdl_version = SDL_VERSION;
86-
LL_INFOS() << "Compiled against SDL "
87-
<< SDL_VERSIONNUM_MAJOR(c_sdl_version) << "."
88-
<< SDL_VERSIONNUM_MINOR(c_sdl_version) << "."
89-
<< SDL_VERSIONNUM_MICRO(c_sdl_version) << LL_ENDL;
90-
const int r_sdl_version = SDL_GetVersion();
91-
LL_INFOS() << "Running with SDL "
92-
<< SDL_VERSIONNUM_MAJOR(r_sdl_version) << "."
93-
<< SDL_VERSIONNUM_MINOR(r_sdl_version) << "."
94-
<< SDL_VERSIONNUM_MICRO(r_sdl_version) << LL_ENDL;
95-
#endif
96-
97-
#if LL_WINDOWS && defined(LL_SDL_WINDOW)
98-
Uint32 style = 0;
99-
#if defined(CS_BYTEALIGNCLIENT) && defined(CS_OWNDC)
100-
style = (CS_BYTEALIGNCLIENT | CS_OWNDC);
101-
#endif
102-
SDL_RegisterApp(app_name.c_str(), style, nullptr);
103-
#endif
104-
10577
#if LL_SDL_WINDOW
78+
// Run once. SDL_SetHint is itself idempotent, but the registerUserDefaults
79+
// hints only take effect on the *first* video init, so re-setting them after
80+
// that point would be pointless — bail early to make the intent explicit.
81+
static bool sHintsSet = false;
82+
if (sHintsSet)
83+
return;
84+
sHintsSet = true;
85+
10686
std::initializer_list<std::tuple< char const*, char const * > > hintList =
10787
{
10888
// Don't ask the compositor to bypass us in fullscreen —
@@ -163,6 +143,42 @@ void init_sdl(const std::string& app_name)
163143
SDL_SetHint(std::get<0>(hint), std::get<1>(hint));
164144
}
165145
#endif
146+
}
147+
148+
void init_sdl(const std::string& app_name)
149+
{
150+
#ifndef LL_SDL_WINDOW
151+
if (!gSDLMainHandled)
152+
{
153+
SDL_SetMainReady();
154+
}
155+
156+
SDL_SetLogOutputFunction(&sdl_logger, nullptr);
157+
158+
const int c_sdl_version = SDL_VERSION;
159+
LL_INFOS() << "Compiled against SDL "
160+
<< SDL_VERSIONNUM_MAJOR(c_sdl_version) << "."
161+
<< SDL_VERSIONNUM_MINOR(c_sdl_version) << "."
162+
<< SDL_VERSIONNUM_MICRO(c_sdl_version) << LL_ENDL;
163+
const int r_sdl_version = SDL_GetVersion();
164+
LL_INFOS() << "Running with SDL "
165+
<< SDL_VERSIONNUM_MAJOR(r_sdl_version) << "."
166+
<< SDL_VERSIONNUM_MINOR(r_sdl_version) << "."
167+
<< SDL_VERSIONNUM_MICRO(r_sdl_version) << LL_ENDL;
168+
#endif
169+
170+
#if LL_WINDOWS && defined(LL_SDL_WINDOW)
171+
Uint32 style = 0;
172+
#if defined(CS_BYTEALIGNCLIENT) && defined(CS_OWNDC)
173+
style = (CS_BYTEALIGNCLIENT | CS_OWNDC);
174+
#endif
175+
SDL_RegisterApp(app_name.c_str(), style, nullptr);
176+
#endif
177+
178+
// Hints must be in place before the first video init. The splash screen
179+
// also calls this before its own SDL_InitSubSystem(SDL_INIT_VIDEO); the
180+
// once-guard inside makes a second call here a no-op.
181+
set_sdl_hints();
166182

167183
// SDL_INIT_VIDEO is the only subsystem the viewer actually uses through
168184
// SDL3. Joystick / gamepad input goes through libndof (llviewerjoystick),

indra/llwindow/llsdl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,11 @@
3030

3131
extern bool gSDLMainHandled;
3232

33+
// Apply our SDL hints. Must run before the *first* SDL_InitSubSystem(SDL_INIT_VIDEO)
34+
// of the process: some hints (notably SDL_HINT_MAC_SCROLL_MOMENTUM and
35+
// SDL_HINT_MAC_PRESS_AND_HOLD) are consumed by SDL's Cocoa registerUserDefaults,
36+
// which runs once during the first video init and is never revisited. The splash
37+
// screen brings video up before init_sdl(), so it calls this first too. Idempotent.
38+
void set_sdl_hints();
3339
void init_sdl(const std::string& app_name);
3440
void quit_sdl();

indra/llwindow/llwindowsdl.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3548,6 +3548,14 @@ void LLSplashScreenSDL::showImpl()
35483548
// subsystem may not be up yet. SDL_InitSubSystem is reference-counted, so
35493549
// initialising it here is safe; hideImpl() releases exactly this reference,
35503550
// leaving the count the main window's own init_sdl() established.
3551+
//
3552+
// This is the process's *first* video init, so the SDL hints must already
3553+
// be set: the Cocoa backend's registerUserDefaults reads them once here and
3554+
// never again (e.g. SDL_HINT_MAC_SCROLL_MOMENTUM → AppleMomentumScrollSupported).
3555+
// Setting them only in init_sdl() would be too late and silently disable
3556+
// macOS momentum scrolling. set_sdl_hints() is idempotent.
3557+
set_sdl_hints();
3558+
35513559
if (!SDL_InitSubSystem(SDL_INIT_VIDEO))
35523560
{
35533561
LL_WARNS() << "Splash: SDL_InitSubSystem(VIDEO) failed: " << SDL_GetError() << LL_ENDL;
@@ -3740,6 +3748,13 @@ void LLSplashScreenSDL::hideImpl()
37403748

37413749
S32 OSMessageBoxSDL(const std::string& text, const std::string& caption, U32 type)
37423750
{
3751+
// A fatal-error dialog can fire before the splash or main window has brought
3752+
// video up. On macOS SDL_ShowMessageBox routes through Cocoa_RegisterApp →
3753+
// registerUserDefaults, which reads our hints exactly once. Make sure they're
3754+
// set first so an early dialog doesn't lock in the wrong defaults (e.g.
3755+
// momentum scrolling). Idempotent if hints are already applied.
3756+
set_sdl_hints();
3757+
37433758
// Use the main viewer window as the message box's parent so it is modal
37443759
// to the viewer and stacks correctly above fullscreen on compositors that
37453760
// place dialogs relative to their parent window.

0 commit comments

Comments
 (0)