Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions indra/llwindow/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,22 @@ if(USE_SDL_WINDOW)

target_compile_definitions(llwindow PUBLIC LL_SDL_WINDOW=1)

if(DARWIN)
# macOS-only Objective-C++ helper that fixes up SDL's auto-created Cocoa
# menu bar (strips the Cmd+W shortcut off its default Window > Close item).
target_sources(llwindow
PRIVATE
llsdl_macos.mm
PUBLIC
llsdl_macos.h
)

# llwindow reuses llprecompiled's C++ PCH; that PCH has no Objective-C++
# variant, so skip it for the .mm or CMake fails to resolve a cmake_pch.objcxx
# header it expects this target to provide.
set_source_files_properties(llsdl_macos.mm PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
endif()

target_link_libraries(llwindow
ll::SDL3
)
Expand Down
18 changes: 18 additions & 0 deletions indra/llwindow/llsdl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ bool gSDLMainHandled = false;
#include "llwin32headers.h" // CS_BYTEALIGNCLIENT / CS_OWNDC
#endif

#if LL_DARWIN
#include "llsdl_macos.h"
#endif

void sdl_logger(void *userdata, int category, SDL_LogPriority priority, const char *message)
{
switch (priority)
Expand Down Expand Up @@ -133,6 +137,12 @@ void set_sdl_hints()

// Momentum scrolling on macos is desirable for mac touchpads
{SDL_HINT_MAC_SCROLL_MOMENTUM, "1"},

// Don't let SDL post a synthetic SDL_EVENT_QUIT when the
// last window closes. The viewer owns its own shutdown
// sequence (LLAppViewer), and transient teardown of the
// main window must never be read as a request to quit.
{SDL_HINT_QUIT_ON_LAST_WINDOW_CLOSE, "0"},
};

for (auto hint: hintList)
Expand Down Expand Up @@ -200,6 +210,14 @@ void init_sdl(const std::string& app_name)
}
}
}

#if LL_DARWIN
// SDL has now registered the app and built its default Cocoa menu bar. Drop
// the Cmd+W shortcut off its auto-created Window > Close item so the
// shortcut reaches the viewer's own "Close Window" handler instead of
// tearing down the window (which the SDL backend reads as a quit request).
ll_sdl_macos_strip_default_close_shortcut();
#endif
}

void quit_sdl()
Expand Down
37 changes: 37 additions & 0 deletions indra/llwindow/llsdl_macos.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* @file llsdl_macos.h
* @brief macOS-specific SDL helpers
*
* $LicenseInfo:firstyear=2024&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2024, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/

#pragma once

// Remove the Cmd+W key equivalent from the "Close" item in the default macOS
// menu bar that SDL3's Cocoa backend creates for us. See the implementation in
// llsdl_macos.mm for the full rationale. Safe to call once after the first
// SDL_INIT_VIDEO (when SDL has registered the app and built its menu).
void ll_sdl_macos_strip_default_close_shortcut();

// Make the given SDL window key again after a native file dialog closes.
struct SDL_Window;
void ll_sdl_macos_make_window_key_deferred(struct SDL_Window* window);
104 changes: 104 additions & 0 deletions indra/llwindow/llsdl_macos.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* @file llsdl_macos.mm
* @brief macOS-specific SDL helpers
*
* $LicenseInfo:firstyear=2024&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2024, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/

#ifdef LL_DARWIN

#import <Cocoa/Cocoa.h>

#include "SDL3/SDL.h"

#include "llsdl_macos.h"

void ll_sdl_macos_strip_default_close_shortcut()
{
@autoreleasepool
{
// SDL3's Cocoa backend auto-creates a default menu bar (it does this
// whenever [NSApp mainMenu] is nil at registration) whose "Window"
// menu contains a "Close" item bound to Cmd+W via -performClose:. That
// native item swallows Cmd+W before it can reach the viewer's own
// in-window "Close Window" accelerator (menu_viewer.xml -> control|W ->
// File.CloseWindow, which just closes the frontmost floater). Worse,
// -performClose: on the sole viewer window triggers
// SDL_EVENT_WINDOW_CLOSE_REQUESTED, which LLWindowSDL turns into a full
// application quit. Clear the key equivalent so Cmd+W falls through to
// LLMenuGL instead of quitting the viewer. The menu item itself stays
// (still clickable); only its keyboard shortcut is removed.
// SDL parks "Close" in the menu it hands to -setWindowsMenu:, but scan
// every top-level submenu rather than assuming that location, so this
// keeps working if SDL ever rearranges its default bar.
NSMenu* main_menu = [NSApp mainMenu];
if (!main_menu)
{
return;
}

for (NSMenuItem* top_item in [main_menu itemArray])
{
NSMenu* submenu = [top_item submenu];
if (!submenu)
{
continue;
}

for (NSMenuItem* item in [submenu itemArray])
{
if ([item action] == @selector(performClose:))
{
[item setKeyEquivalent:@""];
[item setKeyEquivalentModifierMask:0];
}
}
}
}
}

void ll_sdl_macos_make_window_key_deferred(struct SDL_Window* window)
{
if (!window)
{
return;
}

NSWindow* ns_window = (__bridge NSWindow*)SDL_GetPointerProperty(
SDL_GetWindowProperties(window), SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr);
if (!ns_window)
{
return;
}

// After a native dialog closes, SDL only reactivates the app, leaving no
// key window: keystrokes hit no responder and AppKit beeps. Re-key the
// window so it becomes key again and SDL emits FOCUS_GAINED, restoring input.
// Defer to the next main-queue turn so this runs after SDL's own
// ReactivateAfterDialog instead of being clobbered by it.
dispatch_async(dispatch_get_main_queue(), ^{
[NSApp activateIgnoringOtherApps:YES];
[ns_window makeKeyAndOrderFront:nil];
});
}

#endif // LL_DARWIN
46 changes: 46 additions & 0 deletions indra/llwindow/llwindowsdl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ LLWindowSDL::WAYLAND_DATA LLWindowSDL::sWaylandData = {};
#include <CoreServices/CoreServices.h>
#include <CoreGraphics/CGDisplayConfiguration.h>
#include <SDL3_image/SDL_image.h>
#include "llsdl_macos.h"

bool LLWindowSDL::sUseMultGL = false;
#endif
Expand Down Expand Up @@ -1754,6 +1755,51 @@ SDL_Window* LLWindowSDL::getMainSDLWindow()
return gWindowImplementation ? gWindowImplementation->mWindow : nullptr;
}

//static
void LLWindowSDL::enterDialog()
{
if (gWindowImplementation)
{
gWindowImplementation->beforeDialog();
}
}

//static
void LLWindowSDL::exitDialog()
{
LLWindowSDL* self = gWindowImplementation;
if (!self)
{
return;
}

self->afterDialog();

if (self->mDialogDepth != 0 || !self->mWindow)
{
return;
}

// afterDialog() restores fullscreen and mouselook; the file dialog also needs
// key-window focus back once the last one closes. SDL's dialog sheet leaves no
// key window, so without this keystrokes hit no responder and AppKit beeps.
#if LL_DARWIN
ll_sdl_macos_make_window_key_deferred(self->mWindow);
#else
SDL_RaiseWindow(self->mWindow);
if (self->mCallbacks)
{
self->mCallbacks->handleFocus(self);
}
#endif
}

//static
bool LLWindowSDL::dialogOpen()
{
return gWindowImplementation && gWindowImplementation->mDialogDepth > 0;
}

//static
std::vector<std::string> LLWindowSDL::getDisplaysResolutionList()
{
Expand Down
12 changes: 12 additions & 0 deletions indra/llwindow/llwindowsdl.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,18 @@ class LLWindowSDL final : public LLWindow
// worker thread has its own GL context current.
static SDL_Window* getMainSDLWindow();

// Bracket an async OS dialog. SDL3's file picker resolves on a later frame,
// so beforeDialog()/afterDialog() can't wrap it on a single stack frame;
// enterDialog()/exitDialog() drive the same mDialogDepth machinery from the
// launch site and the completion callback instead. exitDialog() also restores
// key-window focus, which SDL leaves dropped after its dialog sheet closes.
static void enterDialog();
static void exitDialog();

// True while any OS dialog is up. The frame loop services modeless pickers,
// so it must skip BackgroundYieldTime while one is open or it turns laggy.
static bool dialogOpen();

#if LL_DARWIN
static U64 getVramSize();
static void setUseMultGL(bool use_mult_gl);
Expand Down
8 changes: 8 additions & 0 deletions indra/newview/llappviewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1558,9 +1558,17 @@ bool LLAppViewer::doFrame()
ms_sleep(non_interactive_ms_sleep_time);
}

// A native picker steals key focus but is serviced by this loop, so
// don't background-yield while one is open or it turns laggy.
bool native_dialog_open = false;
#if LL_SDL_WINDOW
native_dialog_open = LLWindowSDL::dialogOpen();
#endif

// yield cooperatively when not running as foreground window
// and when not quiting (causes trouble at mac's cleanup stage)
if (!LLApp::isExiting()
&& !native_dialog_open
&& ((gViewerWindow && !gViewerWindow->getWindow()->getVisible())
|| !gFocusMgr.getAppHasFocus()))
{
Expand Down
1 change: 1 addition & 0 deletions indra/newview/llnetmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,7 @@ void LLNetMap::reshape(S32 width, S32 height, bool called_from_parent)
{
LLUICtrl::reshape(width, height, called_from_parent);
createObjectImage();
createParcelImage();
}

LLVector3 LLNetMap::globalPosToView(const LLVector3d& global_pos)
Expand Down
9 changes: 9 additions & 0 deletions indra/newview/llsdlfiledialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ namespace LLSDLFileDialog
template <typename ResultT>
inline void trampoline(void* userdata, const char* const* filelist, int /*filter*/)
{
// Close the bracket opened in show(), on the main thread: the callback can
// fire on a worker thread (the Linux Zenity backend), and exitDialog()
// touches mDialogDepth and window focus. Runs inline when already on main.
SDL_RunOnMainThread([](void*) { LLWindowSDL::exitDialog(); }, nullptr, false);

auto* ctx = static_cast<LLSDLFileDialogContext<ResultT>*>(userdata);
auto* cb = ctx->mCallback;
auto* user = ctx->mUserdata;
Expand Down Expand Up @@ -147,6 +152,10 @@ namespace LLSDLFileDialog
SDL_SetStringProperty(props, SDL_PROP_FILE_DIALOG_LOCATION_STRING, location.c_str());
}

// Open the dialog bracket; trampoline() closes it. Drops fullscreen and
// mouselook for the dialog's duration, like the blocking pickers do.
LLWindowSDL::enterDialog();

SDL_ShowFileDialogWithProperties(type, &trampoline<ResultT>, ctx, props);

SDL_DestroyProperties(props);
Expand Down
Loading
Loading