Skip to content

Add opt-in SDL3 window backend for Windows#310

Open
RyeMutt wants to merge 18 commits into
developfrom
rye/sdl3-windows
Open

Add opt-in SDL3 window backend for Windows#310
RyeMutt wants to merge 18 commits into
developfrom
rye/sdl3-windows

Conversation

@RyeMutt

@RyeMutt RyeMutt commented Jun 13, 2026

Copy link
Copy Markdown
Member

Summary

Brings the SDL3 window/input backend (already the default on Linux) to Windows as an opt-in alternative to the native Win32 backend, behind USE_SDL_WINDOW (vs2026-os-sdl presets). The native Win32 backend remains the default and is unchanged for normal builds.

Along the way this lands the high-precision (sub-notch) scroll-wheel work and restores distinct-numpad avatar control, both shared across all backends.

What's included

SDL3 window backend (Windows)

  • Unified SDL main-callbacks entry point shared with Linux/macOS; LLAppViewerWin32 is driven through the SDL_App* callbacks, keeping the Win32 platform overrides (NVAPI, Velopack, crash handling, single-instance mutex, WM_COPYDATA SLURL hand-off, DirectInput8/joystick, serial number).
  • Native shared GL contexts for worker threads via the platform GL API behind SDL (WGL sibling context on Windows; CGL/GLX/EGL elsewhere) instead of SDL's hidden-carrier-window pattern.
  • LLWindowSDL splash screen (SDL_ttf + SDL_image, branded icon).
  • Branded cursors loaded from the exe's embedded .cur resources; DXGI VRAM-budget query shared with the native backend; COM init tolerant of SDL's early COM bring-up.

Input / scrolling (all backends)

  • High-precision floating-point scroll (LLScrollDelta: integer mClicks for discrete consumers, mPrecise for continuous) — smooth sub-notch scrolling/zoom for scrollbars, camera, minimap/world-map/previews and mouselook FOV; MouseWheelScrollSensitivity setting; momentum scrolling on macOS.
  • Distinct numpad avatar/camera control restored via the NumpadControl setting (off / NumLock-off / always), matched across the SDL and Win32 keyboards.

Hardening / fixes

  • Fix gIconResource undeclared in the default (non-SDL) Windows build.
  • Sub-notch scroll no longer double-scrolls nested scroll areas; numpad press/release stays symmetric across a mid-press NumLock toggle.
  • Assorted SDL backend robustness: TEXT_INPUT Ctrl-accelerator filter, empty-resolution guard, degenerate-resize floor, teardown null-window guards, splash partial-init cleanup, flash-icon guards.
  • Cache window pixel density/height off the per-event input path (drops per-event GetClientRect).

Platform / opt-in

  • Windows — opt-in via -DUSE_SDL_WINDOW=ON (or the vs2026-os-sdl presets). Default builds use the native Win32 backend and are unaffected.
  • Linux / macOS — SDL remains the default backend.

Testing

  • Default (non-SDL) Windows build compiles (verified the restored gIconResource path).
  • SDL Windows build compiles and runs; exercised in-world during native-vs-SDL performance comparison.

🤖 Generated with Claude Code

RyeMutt and others added 16 commits June 12, 2026 23:58
Make the SDL3 window backend (LLWindowSDL) buildable and runnable on
Windows as a build-time opt-in (USE_SDL_WINDOW=ON); the native Win32
backend remains the default. Adds a vs2026-os-sdl preset.

When enabled, Windows uses the same SDL main-callbacks entry point as
Linux/macOS (llappviewersdl.cpp), but instantiates LLAppViewerWin32 so
all Win32 platform overrides are retained (BugSplat, console, WER,
velopack, NVAPI, single-instance mutex, FindWindow SLURL send). The
native WINMAIN is carved out under !LL_SDL_WINDOW and its NVAPI session
lifecycle is extracted into reusable helpers.

LLWindowSDL gains Windows parity under LL_WINDOWS: a WndProc subclass to
receive WM_COPYDATA second-instance SLURL hand-offs, DirectInput8 access
for the joystick/SpaceNavigator path, a shared
LLDXHardware::updateVRAMBudgetFromDXGI() VRAM budget query, and loading
of the branded cursors from the exe's embedded .cur resources (handling
1bpp/8bpp/32bpp formats, hot-spots taken from the resources). The
res-sdl BMP cursor path is now Linux-only and res-sdl is not shipped on
Windows.

The Win32 window/keyboard/dragdrop sources are excluded from SDL builds
(llkeyboardwin32 can't compile under the SDL NATIVE_KEY_TYPE, and the
factory never instantiates LLWindowWin32 there); lldxhardware stays
compiled for both backends. Also fixes an AltGr LLWindowWin32 downcast in
llviewerwindow that is invalid under the SDL backend.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
LLWindowSDL's worker-thread shared GL contexts (texture upload, VBO
streaming) were each backed by a hidden 1x1 SDL_Window, which forced a
fragile main-thread-only deferred-window-destruction queue (SDL_DestroyWindow
isn't safe to call off the main thread on X11).

Replace that with platform-native context creation behind SDL:
  * Windows  — WGL sibling context on the main window's HDC
               (wglCreateContextAttribsARB with version fallback), mirroring
               LLWindowWin32.
  * macOS    — CGLCreateContext sharing the current CGLContextObj; bound
               drawable-less (renders to FBOs).
  * X11      — a 1x1 offscreen GLXPbuffer + glXCreateNewContext. Each worker
               needs its own drawable (reusing the main window's would
               BadAccess, as it's current on the main thread), but a pbuffer
               touches no window manager and is destroyable from the worker.
  * Wayland  — a surfaceless EGL context (EGL_NO_SURFACE,
               EGL_KHR_surfaceless_context), version requested explicitly with
               eglBindAPI(EGL_OPENGL_API) re-asserted per worker thread.

GLX/EGL entry points are resolved via SDL_GL_GetProcAddress /
SDL_EGL_GetProcAddress (the viewer doesn't link libGL/libEGL under SDL); the
EGL display/config come from SDL_EGL_GetCurrentDisplay/Config. Each shared
context is an opaque heap LLSDLSharedContext; destroySharedContext now tears
down directly on the worker thread with no deferral. The live handles are
tracked only so destroyContext can reclaim any a worker failed to release,
replacing mOSRContexts/mDeadOSRWindows and the processMiscNativeEvents drain.

Only the WGL path is build-verified here; the GLX/EGL/CGL paths are written
against the API specs but compile-unverified without a Linux/macOS toolchain.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The SDL splash (LLSplashScreenSDL) was a no-op. Implement it like the
Win32 splash loader: a small borderless, always-on-top window showing the
branded app icon and an updatable status line while the viewer loads.

It draws with a software renderer (SDL_CreateSoftwareRenderer on the
window surface) rather than an accelerated one, so SDL doesn't spin up a
GL/D3D device on the splash window that could collide with the main
window's OpenGL context initialisation. Status text is rendered with
SDL3_ttf using the bundled Inter variable font (WOFF2, decompressed by
FreeType/brotli); the icon is loaded with SDL3_image from a branded PNG
copied out of BRANDING_SOURCE_DIR into app_settings at configure time.

The splash precedes createWindow()/init_sdl(), so showImpl() brings up
SDL_INIT_VIDEO (reference counted) and TTF_Init(); hideImpl() releases
exactly those, leaving the subsystem up for the main window. render()
pumps events itself since no SDL event loop is running yet.

Adds sdl3-ttf and the png feature of sdl3-image to the vcpkg manifest and
links them through ll::SDL3. The implementation is platform-neutral, so
it builds for the Linux and macOS SDL backends as well.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Carry mouse scroll-wheel deltas as LLScrollDelta (integer mClicks plus a raw
float mPrecise) end-to-end instead of an integer click count, so high-resolution
wheels and touchpads deliver smooth sub-notch scrolling while discrete widgets
keep their exact whole-notch behavior.

- New LLScrollDelta type bundles the backend-accumulated integer notches with
  the raw per-event float delta, in coherent detent units (1.0 == one notch).
- Thread it through the dispatch spine: LLWindowCallbacks, LLMouseHandler, and
  LLView (childrenHandleMouseEvent template unchanged), into LLViewerWindow.
- SDL and Win32 backends emit both fields on every wheel event; the existing
  accumulators still drive mClicks, so discrete consumers are unaffected.
- Camera zoom reads mPrecise for smooth zoom; LLAgentCamera and LLFollowCam
  take F32. Scale LLFollowCam's minimum-zoom floor by |z| so sub-notch events
  are not snapped up to the whole-notch minimum.
- Discrete widgets (spin, menu, chiclet, fast-timer, emoji) read mClicks;
  chiclet gains a zero-guard for the new every-event dispatch.
- Media scroll quantizes to mClicks at the plugin boundary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
LLScrollbar accumulates the precise (sub-notch) scroll delta in an F32 residue
and applies whole lines as the accumulator crosses an integer, carrying the
remainder. mDocPos is integer "lines", so this is what lets high-resolution
wheels and touchpads scroll smoothly instead of dropping everything below one
whole notch; a whole notch still moves mStepSize lines exactly as before.

Flows to every scrollbar-backed widget: scroll lists, scroll containers,
accordions, hex editor, emoji picker, and fast-timer view.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Read the precise (sub-notch) scroll delta instead of integer clicks in the
continuous-zoom handlers so high-resolution wheels and touchpads zoom smoothly:
minimap (LLNetMap), world-map floater, mesh/image/anim upload previews, and
mouselook FOV adjust (LLToolCompGun). These already used float zoom math, so a
whole notch zooms exactly as before.

LLSlider is intentionally left on integer clicks: it snaps to increments, so
one notch == one increment remains correct.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New F32 setting (default 1.0) scaling the precise scroll component in
LLViewerWindow, so it tunes every smooth path at once: camera zoom, smooth
list/container scrolling, and minimap/world-map/preview zoom. Item steppers
(spinners, menus, combo boxes) read the integer click count and are
intentionally unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Re-introduce the numpad-distinct mechanism removed upstream in 2012
(9b27e32): LLKeyboard::e_numpad_distinct + the NumpadControl setting,
so the numeric keypad emits distinct KEY_PAD_* codes and can drive the
avatar/camera independently of the arrow keys and the digit row.

- LLKeyboard: restore e_numpad_distinct enum + get/setNumpadDistinct.
- SDL backend: translateNumpadKey honors the mode + NumLock against the
  raw SDLK_KP_* keysym, tried before the NumLock-fold fallback; symmetric
  on key up so levels do not stick. SDLK_KP_DIVIDE -> KEY_DIVIDE for Win32
  parity (keeps numpad / driving start_gesture).
- Win32 backend: restore the mode-aware translateExtendedKey (early
  numpad-map lookup + MASK_EXTENDED split) and the ND_NUMLOCK_ON guard in
  inverseTranslateExtendedKey.
- Operators + - * / stay non-distinct; numpad +/- zoom via the existing
  KEY_ADD/KEY_SUBTRACT bindings (avoids the SDL TEXT_INPUT char path).
- Numpad Enter stays aliased to Enter (keeps numpad-Enter-sends-chat).
- llsdl.cpp: pin SDL_HINT_KEYCODE_OPTIONS (must not add hide_numpad) -
  numpad-distinct relies on receiving raw SDLK_KP_* keycodes.
- NumpadControl S32 setting (default 1) + change listener and an initial
  sync in the LLViewerWindow ctor; default PAD_* bindings for
  first_person/third_person/sitting plus a Preferences combo.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The include was dropped in the SDL backend cleanup, but gIconResource (declared in llwindowwin32.h) is still referenced by WINMAIN, which is compiled only when USE_SDL_WINDOW is off. Restore the include under that guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
scrollByWheel returned changeLine()'s result, which is false until the residue crosses a whole line, so non-opaque nested scrollers forwarded sub-notch precise events to the parent and both scrolled on one gesture. Report the gesture as handled mid-content; keep chaining to the parent at the scroll boundary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The numpad-distinct decision read live NumLock at both key edges, so toggling NumLock while a numpad key was held left the down KEY's level stuck (e.g. avatar kept moving). Record the down translation and replay it on key-up. Win32 keys by scancode because the VK swaps with NumLock (VK_NUMPAD2 vs VK_DOWN); SDL keys by its stable keysym.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- TEXT_INPUT Ctrl-accelerator filter reads the keystroke-captured mKeyModifiers
  instead of live SDL_GetModState(), so a fast Ctrl+A whose Ctrl-release lands
  in the same pump can no longer leak its literal char into a focused field.
- getNativeAspectRatio guards an empty resolution list (was OOB resolutions[-1]
  plus a divide-by-zero on a garbage height).
- Resize handlers (RESIZED and PIXEL_SIZE_CHANGED) floor width/height to 1px so a
  0-dimension event never reaches handleResize as a degenerate 0x0 viewport.
- handleEvents drops events once mWindow is null (teardown), covering the
  unguarded mWindow derefs in the focus/resize/DPI handlers.
- LLSplashScreenSDL tears its window down if the software renderer fails rather
  than leaving a blank always-on-top window up for the whole load.
- flashIcon/maybeStopFlashIcon/gatherInput guard mWindow before SDL_FlashWindow.
- unsetenv("LD_PRELOAD") scoped to Linux (it was a no-op on the macOS branch;
  macOS injects via DYLD_INSERT_LIBRARIES).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The per-event input handlers (mouse motion at up to the mouse poll rate,
button, drop) and convertCoords each called SDL_GetWindowPixelDensity /
SDL_GetWindowSizeInPixels, both of which hit GetClientRect (a USER32 syscall)
on Windows — thousands per second on the render thread during mouselook.

Cache the density and pixel height in refreshPixelMetrics(), refreshed at every
resize / DPI / monitor change (createContext, switchContext, RESIZED,
PIXEL_SIZE_CHANGED, DISPLAY_CHANGED, DISPLAY_SCALE_CHANGED) and reused by
refreshMinSizePixelShadow. convertCoords keeps a live fallback until the first
refresh. Events drain in order, so a resize/pixel-size event refreshes the
cache before any later mouse event in the same pump — no stale coordinates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 34c2212f-22ae-4e3c-829e-bb0e4f971d15

📥 Commits

Reviewing files that changed from the base of the PR and between 9c0842b and 58e378a.

📒 Files selected for processing (1)
  • indra/llwindow/llwindowsdl.cpp

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Numeric keypad preference with three modes (arrow / NumLock-dependent / always)
    • Mouse wheel scroll sensitivity setting
    • Higher-precision, smoother scrolling across UI and camera zoom (uses fractional scroll deltas)
    • Optional SDL window backend on Windows (flagged experimental)
    • Branded SDL splash/logo included
  • Bug Fixes

    • More consistent numpad press/release behavior across NumLock changes
    • Windows COM initialization made more tolerant of prior setups

Walkthrough

CMake/presets and packaging add SDL configuration and splash assets; LLScrollDelta is introduced and propagated through window callbacks, UI controls, scrollbars, and viewer classes; precise wheel accumulation and sensitivity are added; numpad distinctness, bindings, and translation/state are implemented; Windows SDL runtime gets DXGI, DirectInput, splash, WM_COPYDATA, and shared GL context updates.

Changes

SDL windowing and input migration

Layer / File(s) Summary
All changes checkpoint
indra/..., indra/llwindow/..., indra/llui/..., indra/newview/..., .gitignore
CMake/presets and packaging updated for SDL; new LLScrollDelta type added and propagated through llwindow callbacks, LLMouseHandler, UI controls, scrollbars, and viewer consumers; precise wheel accumulation and sensitivity scaling implemented; numpad distinctness, translation, settings, and bindings added; Windows SDL runtime extended with DXGI VRAM updates, DirectInput, WM_COPYDATA IPC, shared GL context lifecycle, splash rendering, and related viewer startup/teardown changes.

Sequence Diagram(s)

sequenceDiagram
  participant LLWindowSDL
  participant LLWindowCallbacks
  participant LLViewerWindow
  participant LLScrollbar
  participant LLAgentCamera
  LLWindowSDL->>LLWindowCallbacks: handleScrollWheel(LLScrollDelta)
  LLWindowCallbacks->>LLViewerWindow: handleScrollWheel(LLScrollDelta)
  LLViewerWindow->>LLScrollbar: handleScrollWheel(LLScrollDelta)
  LLViewerWindow->>LLAgentCamera: handleScrollWheel(delta.mPrecise)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

🐰 I hop through deltas, precise and new,
Numpad and SDL stitched through and through.
Splash lights glow and VRAM counts rise,
Windows and GL share careful ties.
Code carrots finished — a springtime prize.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
indra/llui/llscrollcontainer.cpp (1)

267-281: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve the scrollbar's unhandled result at the vertical boundary.

Line 281 always returns true, even when vertical->handleScrollWheel() returned false. That defeats the new boundary-chaining behavior in LLScrollbar::scrollByWheel(), so an inner scroll container with a visible vertical bar will trap wheel events at top/bottom instead of letting the parent container continue scrolling.

Suggested fix
     LLScrollbar* vertical = mScrollbar[VERTICAL];
     if (vertical->getVisible()
         && vertical->getEnabled())
     {
-        // Pretend the mouse is over the scrollbar
-        if (vertical->handleScrollWheel( 0, 0, delta ) )
+        const bool handled = vertical->handleScrollWheel(0, 0, delta);
+        if (handled)
         {
             updateScroll();
         }
-        // Always eat the event
-        return true;
+        return handled;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@indra/llui/llscrollcontainer.cpp` around lines 267 - 281, The vertical
scrollbar block unconditionally returns true, swallowing wheel events even when
vertical->handleScrollWheel(...) returns false; change it to capture the boolean
result (e.g., bool handled = vertical->handleScrollWheel(0,0,delta)), call
updateScroll() only if handled is true, and return handled so unhandled boundary
results from LLScrollbar::scrollByWheel() can propagate to parent containers;
reference mScrollbar[VERTICAL], LLScrollbar::handleScrollWheel, updateScroll,
and LLScrollbar::scrollByWheel.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@indra/llui/llscrollbar.cpp`:
- Around line 415-437: The handler currently advances mScrollWheelResidue before
checking boundary forwarding, leaving fractional residue when the event is
forwarded; fix by clearing that residue when you decide to forward at an end
stop: inside the branch after computing lines (the code around
mScrollWheelResidue, lines and changeLine(lines, true)), if you are going to
return false because the scroller is parked at the boundary (the cases using
precise>0 with mDocPos < getDocPosMax() or precise<0 with mDocPos > 0 that
currently return false), set mScrollWheelResidue = 0 (or otherwise revert the
fractional advance) before returning so the child doesn't keep leftover fraction
for subsequent gestures.

In `@indra/llui/llslider.cpp`:
- Around line 280-289: In LLSlider::handleScrollWheel, preserve high-precision
wheel input by using delta.mPrecise when delta.mClicks is zero instead of
discarding the motion: compute an effective delta (use delta.mClicks when
non-zero, otherwise delta.mPrecise) and multiply that by getIncrement() when
computing new_val, then call setValueAndCommit(new_val); apply the same change
to the other handleScrollWheel overload (the second occurrence around lines
291-300) so both vertical/horizontal paths use mPrecise as a fallback.

In `@indra/llwindow/lldxhardware.cpp`:
- Around line 489-539: Replace the unsafe reinterpret_cast and unchecked call to
QueryVideoMemoryInfo: call EnumAdapters to get an IDXGIAdapter*, then call
p_dxgi_adapter->QueryInterface(__uuidof(IDXGIAdapter3),
(void**)&p_dxgi_adapter3) to obtain a real IDXGIAdapter3* (and handle/release
interfaces correctly) instead of reinterpret_casting to IDXGIAdapter3; then
zero-initialize a DXGI_QUERY_VIDEO_MEMORY_INFO local (info = {}) and call
p_dxgi_adapter3->QueryVideoMemoryInfo(...), check the HRESULT and if it fails
log/continue and do not read info.Budget; on success compute budget_mb from
info.Budget and proceed to the existing VRAM logic, release p_dxgi_adapter3 when
done. Ensure any early exits still Release the original IDXGIAdapter* where
appropriate and remove the reinterpret_cast usage.

In `@indra/llwindow/llkeyboardsdl.cpp`:
- Around line 254-282: mapSDLtoWin currently only accepts the raw SDL key and
thus misses the NumLock-aware keypad folding you added in
translateNumpadKey/adjustNativekeyFromUnhandledMask; change mapSDLtoWin to
accept the event modifier state (e.g. add a MASK or SDL_Keymod parameter), and
inside mapSDLtoWin perform the same NumLock folding (mirror the logic from
adjustNativekeyFromUnhandledMask/translateNumpadKey so SDLK_KP_* keys yield
VK_DECIMAL or VK_0..VK_9 when SDL_KMOD_NUM is set and produce VK_LEFT/UP/etc
when not). Update every caller of mapSDLtoWin to pass the current mask (the
sites noted in the review), and keep the existing behavior for non-numpad keys;
ensure SDLK_KP_DECIMAL maps to VK_DECIMAL when NumLock is active and preserve
mNumpadKeyDown/mTranslateNumpadMap semantics in the forwarding path.

In `@indra/newview/llappviewersdl.cpp`:
- Around line 1026-1037: Replace the blocking SendMessage call with
SendMessageTimeout so the handoff can't hang and verify the receiver actually
handled WM_COPYDATA: build the same COPYDATASTRUCT, call
SendMessageTimeout(other_window, WM_COPYDATA, NULL, (LPARAM)&cds,
SMTO_ABORTIFHUNG, <timeout_ms e.g. 2000>, &dwResult), check that
SendMessageTimeout returned non-zero and that dwResult (the lpdwResult) is
non-zero before treating it as success; if either check fails, log the failure
(include getWindowTitle()) and return false instead of returning true. Ensure
you update the msg_result handling to use the SendMessageTimeout return and the
dwResult delivery check.

In `@indra/newview/llviewercontrol.cpp`:
- Around line 192-199: The handler handleNumpadControlChanged currently casts
newvalue.asInteger() directly to LLKeyboard::e_numpad_distinct and can pass
invalid values to gKeyboard->setNumpadDistinct; change it to validate/normalize
the integer first (e.g., clamp to the known enum range or map unknown values to
a safe default like LLKeyboard::NPAD_DISTINCT_OFF) and only call
gKeyboard->setNumpadDistinct with a verified LLKeyboard::e_numpad_distinct
value; reference gKeyboard, setNumpadDistinct, and LLKeyboard::e_numpad_distinct
when making the change so the cast is guarded and undefined modes cannot be set.

---

Outside diff comments:
In `@indra/llui/llscrollcontainer.cpp`:
- Around line 267-281: The vertical scrollbar block unconditionally returns
true, swallowing wheel events even when vertical->handleScrollWheel(...) returns
false; change it to capture the boolean result (e.g., bool handled =
vertical->handleScrollWheel(0,0,delta)), call updateScroll() only if handled is
true, and return handled so unhandled boundary results from
LLScrollbar::scrollByWheel() can propagate to parent containers; reference
mScrollbar[VERTICAL], LLScrollbar::handleScrollWheel, updateScroll, and
LLScrollbar::scrollByWheel.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1421782c-7acc-4479-b6a7-346a2609ce0c

📥 Commits

Reviewing files that changed from the base of the PR and between 03a5af0 and 8b30fd9.

📒 Files selected for processing (107)
  • .gitignore
  • indra/CMakeLists.txt
  • indra/CMakePresets.json
  • indra/cmake/Linking.cmake
  • indra/cmake/SDL3.cmake
  • indra/cmake/UI.cmake
  • indra/llrender/CMakeLists.txt
  • indra/llui/fsvirtualtrackpad.cpp
  • indra/llui/fsvirtualtrackpad.h
  • indra/llui/llaccordionctrl.cpp
  • indra/llui/llaccordionctrl.h
  • indra/llui/llaccordionctrltab.cpp
  • indra/llui/llaccordionctrltab.h
  • indra/llui/llcombobox.cpp
  • indra/llui/llcombobox.h
  • indra/llui/llfloater.cpp
  • indra/llui/llfloater.h
  • indra/llui/llflyoutbutton.cpp
  • indra/llui/llflyoutbutton.h
  • indra/llui/llmenugl.cpp
  • indra/llui/llmenugl.h
  • indra/llui/llmodaldialog.cpp
  • indra/llui/llmodaldialog.h
  • indra/llui/llscrollbar.cpp
  • indra/llui/llscrollbar.h
  • indra/llui/llscrollcontainer.cpp
  • indra/llui/llscrollcontainer.h
  • indra/llui/llscrolllistctrl.cpp
  • indra/llui/llscrolllistctrl.h
  • indra/llui/llslider.cpp
  • indra/llui/llslider.h
  • indra/llui/llspinctrl.cpp
  • indra/llui/llspinctrl.h
  • indra/llui/lltextbase.cpp
  • indra/llui/lltextbase.h
  • indra/llui/lltooltip.cpp
  • indra/llui/lltooltip.h
  • indra/llui/llview.cpp
  • indra/llui/llview.h
  • indra/llwindow/CMakeLists.txt
  • indra/llwindow/lldxhardware.cpp
  • indra/llwindow/lldxhardware.h
  • indra/llwindow/llkeyboard.cpp
  • indra/llwindow/llkeyboard.h
  • indra/llwindow/llkeyboardsdl.cpp
  • indra/llwindow/llkeyboardsdl.h
  • indra/llwindow/llkeyboardwin32.cpp
  • indra/llwindow/llkeyboardwin32.h
  • indra/llwindow/llmousehandler.h
  • indra/llwindow/llscrolldelta.h
  • indra/llwindow/llsdl.cpp
  • indra/llwindow/llwindowcallbacks.cpp
  • indra/llwindow/llwindowcallbacks.h
  • indra/llwindow/llwindowsdl.cpp
  • indra/llwindow/llwindowsdl.h
  • indra/llwindow/llwindowwin32.cpp
  • indra/newview/CMakeLists.txt
  • indra/newview/app_settings/key_bindings.xml
  • indra/newview/app_settings/settings_alchemy.xml
  • indra/newview/llagentcamera.cpp
  • indra/newview/llagentcamera.h
  • indra/newview/llappviewersdl.cpp
  • indra/newview/llappviewerwin32.cpp
  • indra/newview/llappviewerwin32.h
  • indra/newview/llchiclet.cpp
  • indra/newview/llchiclet.h
  • indra/newview/llfasttimerview.cpp
  • indra/newview/llfasttimerview.h
  • indra/newview/llfloaterbvhpreview.cpp
  • indra/newview/llfloaterbvhpreview.h
  • indra/newview/llfloaterimagepreview.cpp
  • indra/newview/llfloaterimagepreview.h
  • indra/newview/llfloatermodelpreview.cpp
  • indra/newview/llfloatermodelpreview.h
  • indra/newview/llfloaterworldmap.cpp
  • indra/newview/llfloaterworldmap.h
  • indra/newview/llfollowcam.cpp
  • indra/newview/llfollowcam.h
  • indra/newview/llhexeditor.cpp
  • indra/newview/llhexeditor.h
  • indra/newview/llmachineid.cpp
  • indra/newview/llmediactrl.cpp
  • indra/newview/llmediactrl.h
  • indra/newview/llnetmap.cpp
  • indra/newview/llnetmap.h
  • indra/newview/llpanelemojicomplete.cpp
  • indra/newview/llpanelemojicomplete.h
  • indra/newview/llpanelprimmediacontrols.cpp
  • indra/newview/llpanelprimmediacontrols.h
  • indra/newview/llpanelpulldown.cpp
  • indra/newview/llpanelpulldown.h
  • indra/newview/llpopupview.cpp
  • indra/newview/llpopupview.h
  • indra/newview/lltool.cpp
  • indra/newview/lltool.h
  • indra/newview/lltoolcomp.cpp
  • indra/newview/lltoolcomp.h
  • indra/newview/lltoolpie.cpp
  • indra/newview/lltoolpie.h
  • indra/newview/llviewercontrol.cpp
  • indra/newview/llviewermedia.h
  • indra/newview/llviewerwindow.cpp
  • indra/newview/llviewerwindow.h
  • indra/newview/llwindowlistener.cpp
  • indra/newview/skins/default/xui/en/panel_preferences_move_general.xml
  • indra/newview/viewer_manifest.py
  • indra/vcpkg.json

Comment thread indra/llui/llscrollbar.cpp
Comment thread indra/llui/llslider.cpp
Comment thread indra/llwindow/lldxhardware.cpp Outdated
Comment thread indra/llwindow/llkeyboardsdl.cpp
Comment thread indra/newview/llappviewersdl.cpp Outdated
Comment thread indra/newview/llviewercontrol.cpp
RyeMutt and others added 2 commits June 13, 2026 15:37
- llscrollbar: clear the residue when a sub-notch wheel event is forwarded to a
  parent scroller at a boundary, so reversing direction responds immediately
  instead of first unwinding leftover fraction.
- lldxhardware: QueryInterface to IDXGIAdapter3 (not a reinterpret_cast of the
  IDXGIAdapter from EnumAdapters), and zero-init + HRESULT-check
  QueryVideoMemoryInfo before reading info.Budget.
- llappviewersdl: SendMessageTimeout (SMTO_ABORTIFHUNG, 2s) for the second-
  instance SLURL handoff so a hung primary can't block startup; require delivery
  before treating the URL as handed off.
- llviewercontrol / llviewerwindow: clamp NumpadControl to the valid enum range
  before the e_numpad_distinct cast.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
createSharedContext's GLX path freed the glXChooseFBConfig result with a
direct XFree, but the SDL backend doesn't link libX11 (GLX entry points are
resolved through SDL_GL_GetProcAddress), so the ubuntu Viewer build failed to
link with "undefined reference to XFree".

Resolve XFree from the already-resident libX11 at runtime via SDL_LoadObject /
SDL_LoadFunction, matching how the GLX functions are resolved. On the X11
server path libX11 is always loaded; if it can't be found the array is left
unfreed (a tiny, bounded, once-per-worker leak) rather than crashing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant