diff options
Diffstat (limited to 'indra/llwindow')
33 files changed, 3473 insertions, 2391 deletions
diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt index 6debd54665..e35fcff06e 100644 --- a/indra/llwindow/CMakeLists.txt +++ b/indra/llwindow/CMakeLists.txt @@ -12,12 +12,14 @@ project(llwindow) include(00-Common) include(DragDrop) +include(Linking) include(LLCommon) include(LLImage) include(LLWindow) include(UI) include(ViewerMiscLibs) include(GLM) +include(SDL3) set(llwindow_SOURCE_FILES llcursortypes.cpp @@ -48,6 +50,49 @@ set(viewer_HEADER_FILES llmousehandler.h ) +# Platform-specific files for IDE visibility (excluded from build on other platforms) +set(macosx_SOURCE_FILES + llkeyboardmacosx.cpp + llwindowmacosx.cpp + llwindowmacosx-objc.mm + llopenglview-objc.mm + ) + +set(macosx_HEADER_FILES + llkeyboardmacosx.h + llwindowmacosx.h + llwindowmacosx-objc.h + llwindowmacosx_iokit.h + llopenglview-objc.h + llappdelegate-objc.h + ) + +set(linux_SOURCE_FILES + llsdl.cpp + llkeyboardsdl.cpp + llwindowsdl.cpp + ) + +set(linux_HEADER_FILES + llsdl.h + llkeyboardsdl.h + llwindowsdl.h + ) + +set(windows_SOURCE_FILES + llwindowwin32.cpp + lldxhardware.cpp + llkeyboardwin32.cpp + lldragdropwin32.cpp + ) + +set(windows_HEADER_FILES + llwindowwin32.h + lldxhardware.h + llkeyboardwin32.h + lldragdropwin32.h + ) + set(llwindow_LINK_LIBRARIES llcommon llimage @@ -57,75 +102,43 @@ set(llwindow_LINK_LIBRARIES ll::glm ll::glext ll::uilibraries - ll::SDL ) include_directories(${CMAKE_SOURCE_DIR}/llrender) -# Libraries on which this library depends, needed for Linux builds -# Sort by high-level to low-level -if (NOT (DARWIN OR WINDOWS)) - list(APPEND viewer_SOURCE_FILES - llkeyboardsdl.cpp - llwindowsdl.cpp - ) - list(APPEND viewer_HEADER_FILES - llkeyboardsdl.h - llwindowsdl.h - ) +if (USE_SDL_WINDOW) + list(APPEND viewer_SOURCE_FILES ${linux_SOURCE_FILES}) + list(APPEND viewer_HEADER_FILES ${linux_HEADER_FILES}) - if (BUILD_HEADLESS) - set(llwindowheadless_LINK_LIBRARIES - ${LLCOMMON_LIBRARIES} - ${LLIMAGE_LIBRARIES} - ${LLMATH_LIBRARIES} - ${LLRENDER_HEADLESS_LIBRARIES} - ${LLFILESYSTEM_LIBRARIES} - ${LLWINDOW_HEADLESS_LIBRARIES} - ${LLXML_LIBRARIES} - fontconfig # For FCInit and other FC* functions. - ) - endif (BUILD_HEADLESS) + list(APPEND llwindow_LINK_LIBRARIES + ll::SDL3 + ) endif () if (DARWIN) - list(APPEND llwindow_SOURCE_FILES - llkeyboardmacosx.cpp - llwindowmacosx.cpp - llwindowmacosx-objc.mm - llopenglview-objc.mm - ) - list(APPEND llwindow_HEADER_FILES - llkeyboardmacosx.h - llwindowmacosx.h - llwindowmacosx-objc.h - llopenglview-objc.h - llappdelegate-objc.h - ) + list(APPEND llwindow_SOURCE_FILES ${macosx_SOURCE_FILES}) + list(APPEND llwindow_HEADER_FILES ${macosx_HEADER_FILES}) # We use a bunch of deprecated system APIs. set_source_files_properties( llkeyboardmacosx.cpp llwindowmacosx.cpp PROPERTIES - COMPILE_FLAGS "-Wno-deprecated-declarations -fpascal-strings" + COMPILE_FLAGS "-fpascal-strings" ) + + set_source_files_properties( + llwindowmacosx-objc.mm + llopenglview-objc.mm + PROPERTIES + SKIP_PRECOMPILE_HEADERS TRUE + ) endif (DARWIN) if (WINDOWS) - list(APPEND llwindow_SOURCE_FILES - llwindowwin32.cpp - lldxhardware.cpp - llkeyboardwin32.cpp - lldragdropwin32.cpp - ) - list(APPEND llwindow_HEADER_FILES - llwindowwin32.h - lldxhardware.h - llkeyboardwin32.h - lldragdropwin32.h - ) + list(APPEND llwindow_SOURCE_FILES ${windows_SOURCE_FILES}) + list(APPEND llwindow_HEADER_FILES ${windows_HEADER_FILES}) list(APPEND llwindow_LINK_LIBRARIES comdlg32 # Common Dialogs for ChooseColor ole32 @@ -134,32 +147,55 @@ if (WINDOWS) ) endif (WINDOWS) -if (SOLARIS) - list(APPEND llwindow_SOURCE_FILES - llwindowsolaris.cpp - ) - list(APPEND llwindow_HEADER_FILES - llwindowsolaris.h - ) -endif (SOLARIS) if (BUILD_HEADLESS) set(llwindowheadless_SOURCE_FILES - llwindowmesaheadless.cpp - llmousehandler.cpp - ) + llcursortypes.cpp + llkeyboard.cpp + llkeyboardheadless.cpp + llwindowheadless.cpp + llwindowcallbacks.cpp + llwindow.cpp + llwindowmesaheadless.cpp + llmousehandler.cpp + ) + set(llwindowheadless_HEADER_FILES - llwindowmesaheadless.h - llmousehandler.h - ) + llcursortypes.h + llkeyboard.h + llkeyboardheadless.h + llwindowheadless.h + llwindowcallbacks.h + llwindowmesaheadless.h + llmousehandler.h + ) + add_library (llwindowheadless - ${llwindow_SOURCE_FILES} ${llwindowheadless_SOURCE_FILES} + ${llwindowheadless_HEADER_FILES} ) - set_property(TARGET llwindowheadless - PROPERTY COMPILE_DEFINITIONS LL_MESA=1 LL_MESA_HEADLESS=1 - ) - target_link_libraries (llwindowheadless ${llwindowheadless_LINK_LIBRARIES} dl) + + target_compile_definitions( llwindowheadless PUBLIC LL_MESA_HEADLESS=1 ) + + target_link_libraries (llwindowheadless + llcommon + llimage + llmath + llrenderheadless + llfilesystem + llxml + ll::glm + ll::glext + ll::zlib-ng + PkgConfig::OSMESA + fontconfig + dl + ) + target_include_directories(llwindowheadless INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + + if (USE_PRECOMPILED_HEADERS) + target_precompile_headers(llwindowheadless REUSE_FROM llprecompiled) + endif () endif (BUILD_HEADLESS) if (llwindow_HEADER_FILES) @@ -168,22 +204,61 @@ endif (llwindow_HEADER_FILES) list(APPEND viewer_SOURCE_FILES ${viewer_HEADER_FILES}) + # Collect all platform files for IDE visibility, excluding already-added current platform + set(ide_visibility_files "") + if (NOT DARWIN) + list(APPEND ide_visibility_files ${macosx_SOURCE_FILES} ${macosx_HEADER_FILES}) + endif() + if (NOT (LINUX OR CMAKE_SYSTEM_NAME MATCHES FreeBSD)) + list(APPEND ide_visibility_files ${linux_SOURCE_FILES} ${linux_HEADER_FILES}) + endif() + if (NOT WINDOWS) + list(APPEND ide_visibility_files ${windows_SOURCE_FILES} ${windows_HEADER_FILES}) + endif() + add_library (llwindow ${llwindow_SOURCE_FILES} ${viewer_SOURCE_FILES} + # Add non-platform files for IDE visibility only + ${ide_visibility_files} ) -if (SDL_FOUND) - set_property(TARGET llwindow - PROPERTY COMPILE_DEFINITIONS LL_SDL=1 +# Mark non-platform files as excluded from build +if (WINDOWS) + set_source_files_properties( + ${macosx_SOURCE_FILES} + ${linux_SOURCE_FILES} + PROPERTIES + HEADER_FILE_ONLY TRUE + ) +elseif (DARWIN) + set_source_files_properties( + ${windows_SOURCE_FILES} + ${linux_SOURCE_FILES} + PROPERTIES + HEADER_FILE_ONLY TRUE + ) +elseif (LINUX OR CMAKE_SYSTEM_NAME MATCHES FreeBSD) + set_source_files_properties( + ${macosx_SOURCE_FILES} + ${windows_SOURCE_FILES} + PROPERTIES + HEADER_FILE_ONLY TRUE ) -endif (SDL_FOUND) +endif() + +target_link_libraries (llwindow ${llwindow_LINK_LIBRARIES}) +target_include_directories(llwindow INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) - target_link_libraries (llwindow ${llwindow_LINK_LIBRARIES}) - target_include_directories(llwindow INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +if (USE_SDL_WINDOW) + target_compile_definitions( llwindow PUBLIC LL_SDL_WINDOW=1 ) +endif() + +if (USE_PRECOMPILED_HEADERS) + target_precompile_headers(llwindow REUSE_FROM llprecompiled) +endif () if (DARWIN) - find_library(CARBON_LIBRARY Carbon) target_link_libraries(llwindow ${CARBON_LIBRARY}) endif (DARWIN) diff --git a/indra/llwindow/llappdelegate-objc.h b/indra/llwindow/llappdelegate-objc.h index ef36f7d4a8..5b6e11e4e4 100644 --- a/indra/llwindow/llappdelegate-objc.h +++ b/indra/llwindow/llappdelegate-objc.h @@ -36,16 +36,17 @@ std::string secondLogPath; } -@property (assign) IBOutlet LLNSWindow *window; -@property (assign) IBOutlet NSWindow *inputWindow; -@property (assign) IBOutlet LLNonInlineTextView *inputView; +@property (assign) IBOutlet LLNSWindow * _Nullable window; +@property (assign) IBOutlet NSWindow * _Nullable inputWindow; +@property (assign) IBOutlet LLNonInlineTextView * _Nullable inputView; -@property (retain) NSString *currentInputLanguage; +@property (retain) NSString * _Nullable currentInputLanguage; - (void) oneFrame; -- (void) showInputWindow:(bool)show withEvent:(NSEvent*)textEvent; +- (void) showInputWindow:(bool)show withEvent:(nullable NSEvent *)textEvent; - (void) languageUpdated; - (bool) romanScript; +- (void) setBugsplatValue:(nullable NSString *)value forAttribute:(nullable NSString *)attribute; @end @interface LLApplication : NSApplication diff --git a/indra/llwindow/lldxhardware.cpp b/indra/llwindow/lldxhardware.cpp index 387982dfc2..ff85b2cb14 100644 --- a/indra/llwindow/lldxhardware.cpp +++ b/indra/llwindow/lldxhardware.cpp @@ -312,7 +312,7 @@ std::string get_string(IDxDiagContainer *containerp, const WCHAR *wszPropName) WCHAR wszPropValue[256]; get_wstring(containerp, wszPropName, wszPropValue, 256); - return utf16str_to_utf8str(wszPropValue); + return ll_convert<std::string>(std::wstring(wszPropValue)); } LLDXHardware::LLDXHardware() @@ -440,7 +440,7 @@ LLSD LLDXHardware::getDisplayInfo() // print the value // windows doesn't guarantee to be null terminated release_version[RV_SIZE - 1] = NULL; - ret["DriverVersion"] = utf16str_to_utf8str(release_version); + ret["DriverVersion"] = ll_convert<std::string>(std::wstring(release_version)); } RegCloseKey(hKey); diff --git a/indra/llwindow/llkeyboard.cpp b/indra/llwindow/llkeyboard.cpp index a16c0a318a..df69b1390c 100644 --- a/indra/llwindow/llkeyboard.cpp +++ b/indra/llwindow/llkeyboard.cpp @@ -30,6 +30,8 @@ #include "llwindowcallbacks.h" +#include "llstl.h" + // // Globals // @@ -161,6 +163,7 @@ void LLKeyboard::resetKeyDownAndHandle() mCallbacks->handleTranslatedKeyUp(i, mask); } } + mCurTranslatedKey = KEY_NONE; } // BUG this has to be called when an OS dialog is shown, otherwise modifier key state @@ -196,7 +199,6 @@ void LLKeyboard::resetKeys() bool LLKeyboard::translateKey(const NATIVE_KEY_TYPE os_key, KEY *out_key) { - // Only translate keys in the map, ignore all other keys for now auto iter = mTranslateKeyMap.find(os_key); if (iter == mTranslateKeyMap.end()) @@ -226,7 +228,7 @@ LLKeyboard::NATIVE_KEY_TYPE LLKeyboard::inverseTranslateKey(const KEY translated } -bool LLKeyboard::handleTranslatedKeyDown(KEY translated_key, U32 translated_mask) +bool LLKeyboard::handleTranslatedKeyDown(KEY translated_key, MASK translated_mask) { bool handled = false; bool repeated = false; @@ -254,7 +256,7 @@ bool LLKeyboard::handleTranslatedKeyDown(KEY translated_key, U32 translated_mask } -bool LLKeyboard::handleTranslatedKeyUp(KEY translated_key, U32 translated_mask) +bool LLKeyboard::handleTranslatedKeyUp(KEY translated_key, MASK translated_mask) { bool handled = false; if( mKeyLevel[translated_key] ) diff --git a/indra/llwindow/llkeyboard.h b/indra/llwindow/llkeyboard.h index a527227672..a092787cef 100644 --- a/indra/llwindow/llkeyboard.h +++ b/indra/llwindow/llkeyboard.h @@ -27,10 +27,9 @@ #ifndef LL_LLKEYBOARD_H #define LL_LLKEYBOARD_H +#include <functional> #include <map> -#include <boost/function.hpp> -#include "llstringtable.h" #include "lltimer.h" #include "indra_constants.h" @@ -41,7 +40,7 @@ enum EKeystate KEYSTATE_UP }; -typedef boost::function<bool(EKeystate keystate)> LLKeyFunc; +typedef std::function<bool(EKeystate keystate)> LLKeyFunc; typedef std::string (LLKeyStringTranslatorFunc)(std::string_view); enum EKeyboardInsertMode @@ -55,10 +54,12 @@ class LLWindowCallbacks; class LLKeyboard { public: -#ifndef LL_SDL - typedef U16 NATIVE_KEY_TYPE; -#else +#ifdef LL_SDL_WINDOW + // linux relies on SDL which uses U32 for its native key type typedef U32 NATIVE_KEY_TYPE; +#else + // on non-linux platforms we can get by with a smaller native key type + typedef U16 NATIVE_KEY_TYPE; #endif LLKeyboard(); virtual ~LLKeyboard(); @@ -73,14 +74,14 @@ public: bool getKeyRepeated(const KEY key) { return mKeyRepeated[key]; } bool translateKey(const NATIVE_KEY_TYPE os_key, KEY *translated_key); - NATIVE_KEY_TYPE inverseTranslateKey(const KEY translated_key); - bool handleTranslatedKeyUp(KEY translated_key, U32 translated_mask); // Translated into "Linden" keycodes - bool handleTranslatedKeyDown(KEY translated_key, U32 translated_mask); // Translated into "Linden" keycodes + NATIVE_KEY_TYPE inverseTranslateKey(const KEY translated_key); + bool handleTranslatedKeyUp(KEY translated_key, MASK translated_mask); // Translated into "Linden" keycodes + bool handleTranslatedKeyDown(KEY translated_key, MASK translated_mask); // Translated into "Linden" keycodes virtual bool handleKeyUp(const NATIVE_KEY_TYPE key, MASK mask) = 0; virtual bool handleKeyDown(const NATIVE_KEY_TYPE key, MASK mask) = 0; -#if defined(LL_DARWIN) && !defined(LL_SDL) +#if LL_DARWIN && !LL_SDL_WINDOW // We only actually use this for macOS. virtual void handleModifier(MASK mask) = 0; #endif // LL_DARWIN diff --git a/indra/llwindow/llkeyboardheadless.cpp b/indra/llwindow/llkeyboardheadless.cpp index 6f8c829dd5..50d03aaf20 100644 --- a/indra/llwindow/llkeyboardheadless.cpp +++ b/indra/llwindow/llkeyboardheadless.cpp @@ -31,17 +31,23 @@ LLKeyboardHeadless::LLKeyboardHeadless() { } -void LLKeyboardHeadless::resetMaskKeys() -{ } - - +bool LLKeyboardHeadless::handleKeyUp(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) +{ + return false; +} +bool LLKeyboardHeadless::handleKeyDown(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) +{ + return false; +} +void LLKeyboardHeadless::resetMaskKeys() +{ } MASK LLKeyboardHeadless::currentMask(bool for_mouse_event) { return MASK_NONE; } -#ifdef LL_DARWIN +#if LL_DARWIN && !LL_SDL_WINDOW void LLKeyboardHeadless::handleModifier(MASK mask) { @@ -61,6 +67,7 @@ void LLKeyboardHeadless::scanKeyboard() mCallbacks->handleScanKey(key, mKeyDown[key], mKeyUp[key], mKeyLevel[key]); } } + mCurScanKey = KEY_NONE; // Reset edges for next frame for (S32 key = 0; key < KEY_COUNT; key++) diff --git a/indra/llwindow/llkeyboardheadless.h b/indra/llwindow/llkeyboardheadless.h index 439abaf25b..364fb03fe7 100644 --- a/indra/llwindow/llkeyboardheadless.h +++ b/indra/llwindow/llkeyboardheadless.h @@ -33,20 +33,15 @@ class LLKeyboardHeadless : public LLKeyboard { public: LLKeyboardHeadless(); - /*virtual*/ ~LLKeyboardHeadless() {}; + ~LLKeyboardHeadless() = default; -#ifndef LL_SDL - /*virtual*/ bool handleKeyUp(const U16 key, MASK mask) { return false; } - /*virtual*/ bool handleKeyDown(const U16 key, MASK mask) { return false; } -#else - /*virtual*/ bool handleKeyUp(const U32 key, MASK mask) { return false; } - /*virtual*/ bool handleKeyDown(const U32 key, MASK mask) { return false; } -#endif - /*virtual*/ void resetMaskKeys(); - /*virtual*/ MASK currentMask(bool for_mouse_event); - /*virtual*/ void scanKeyboard(); -#ifdef LL_DARWIN - /*virtual*/ void handleModifier(MASK mask); + bool handleKeyUp(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) override; + bool handleKeyDown(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) override; + void resetMaskKeys() override; + MASK currentMask(bool for_mouse_event) override; + void scanKeyboard() override; +#if LL_DARWIN && !LL_SDL_WINDOW + void handleModifier(MASK mask) override; #endif }; diff --git a/indra/llwindow/llkeyboardmacosx.cpp b/indra/llwindow/llkeyboardmacosx.cpp index 89ff7c6d3f..7d270a4392 100644 --- a/indra/llwindow/llkeyboardmacosx.cpp +++ b/indra/llwindow/llkeyboardmacosx.cpp @@ -32,9 +32,32 @@ #include "llwindowmacosx-objc.h" +#include <CoreServices/CoreServices.h> // Required for TIS functions +#include <Carbon/Carbon.h> + LLKeyboardMacOSX::LLKeyboardMacOSX() { // Virtual keycode mapping table. Yes, this was as annoying to generate as it looks. + + bool isAzerty = false; + + TISInputSourceRef source = TISCopyCurrentKeyboardLayoutInputSource(); + if (source) + { + CFStringRef inputSourceID = (CFStringRef)TISGetInputSourceProperty(source, kTISPropertyInputSourceID); + if (inputSourceID) + { + char buf[128]; + // Convert CFString to a C-string for easy comparison + if (CFStringGetCString(inputSourceID, buf, sizeof(buf), kCFStringEncodingUTF8)) + { + // On macOS, AZERTY layouts are identified by "French" in their Input Source ID + isAzerty = (strstr(buf, "French") != nullptr); + } + } + CFRelease(source); + } + mTranslateKeyMap[0x00] = 'A'; mTranslateKeyMap[0x01] = 'S'; mTranslateKeyMap[0x02] = 'D'; @@ -52,6 +75,7 @@ LLKeyboardMacOSX::LLKeyboardMacOSX() mTranslateKeyMap[0x0f] = 'R'; mTranslateKeyMap[0x10] = 'Y'; mTranslateKeyMap[0x11] = 'T'; + mTranslateKeyMap[0x12] = '1'; mTranslateKeyMap[0x13] = '2'; mTranslateKeyMap[0x14] = '3'; @@ -132,6 +156,16 @@ LLKeyboardMacOSX::LLKeyboardMacOSX() mTranslateKeyMap[0x7d] = KEY_DOWN; mTranslateKeyMap[0x7e] = KEY_UP; + // azerty fix + if(isAzerty) + { + LL_WARNS() << "keyboard is AZERTY" << LL_ENDL; + mTranslateKeyMap[0x00] = 'Q'; + mTranslateKeyMap[0x06] = 'W'; + mTranslateKeyMap[0x0c] = 'A'; + mTranslateKeyMap[0x0d] = 'Z'; + } + // Build inverse map std::map<U16, KEY>::iterator iter; for (iter = mTranslateKeyMap.begin(); iter != mTranslateKeyMap.end(); iter++) @@ -162,7 +196,7 @@ LLKeyboardMacOSX::LLKeyboardMacOSX() void LLKeyboardMacOSX::resetMaskKeys() { - U32 mask = getModifiers(); + MASK mask = getModifiers(); // MBW -- XXX -- This mirrors the operation of the Windows version of resetMaskKeys(). // It looks a bit suspicious, as it won't correct for keys that have been released. @@ -187,7 +221,7 @@ void LLKeyboardMacOSX::resetMaskKeys() } /* -static bool translateKeyMac(const U16 key, const U32 mask, KEY &outKey, U32 &outMask) +static bool translateKeyMac(const U16 key, const MASK mask, KEY &outKey, U32 &outMask) { // Translate the virtual keycode into the keycodes the keyboard system expects. U16 virtualKey = (mask >> 24) & 0x0000007F; @@ -203,7 +237,7 @@ void LLKeyboardMacOSX::handleModifier(MASK mask) updateModifiers(mask); } -MASK LLKeyboardMacOSX::updateModifiers(const U32 mask) +MASK LLKeyboardMacOSX::updateModifiers(const MASK mask) { // translate the mask MASK out_mask = 0; @@ -226,7 +260,7 @@ MASK LLKeyboardMacOSX::updateModifiers(const U32 mask) return out_mask; } -bool LLKeyboardMacOSX::handleKeyDown(const U16 key, const U32 mask) +bool LLKeyboardMacOSX::handleKeyDown(const U16 key, MASK mask) { KEY translated_key = 0; U32 translated_mask = 0; @@ -243,7 +277,7 @@ bool LLKeyboardMacOSX::handleKeyDown(const U16 key, const U32 mask) } -bool LLKeyboardMacOSX::handleKeyUp(const U16 key, const U32 mask) +bool LLKeyboardMacOSX::handleKeyUp(const U16 key, MASK mask) { KEY translated_key = 0; U32 translated_mask = 0; @@ -262,7 +296,7 @@ bool LLKeyboardMacOSX::handleKeyUp(const U16 key, const U32 mask) MASK LLKeyboardMacOSX::currentMask(bool for_mouse_event) { MASK result = MASK_NONE; - U32 mask = getModifiers(); + MASK mask = getModifiers(); if (mask & MAC_SHIFT_KEY) result |= MASK_SHIFT; if (mask & MAC_CTRL_KEY) result |= MASK_CONTROL; @@ -291,6 +325,7 @@ void LLKeyboardMacOSX::scanKeyboard() mCallbacks->handleScanKey(key, mKeyDown[key], mKeyUp[key], mKeyLevel[key]); } } + mCurScanKey = KEY_NONE; // Reset edges for next frame for (key = 0; key < KEY_COUNT; key++) diff --git a/indra/llwindow/llkeyboardmacosx.h b/indra/llwindow/llkeyboardmacosx.h index 92ab5c9a85..c824e7b713 100644 --- a/indra/llwindow/llkeyboardmacosx.h +++ b/indra/llwindow/llkeyboardmacosx.h @@ -42,17 +42,17 @@ class LLKeyboardMacOSX : public LLKeyboard { public: LLKeyboardMacOSX(); - /*virtual*/ ~LLKeyboardMacOSX() {}; + ~LLKeyboardMacOSX() = default; - /*virtual*/ bool handleKeyUp(const U16 key, MASK mask); - /*virtual*/ bool handleKeyDown(const U16 key, MASK mask); - /*virtual*/ void resetMaskKeys(); - /*virtual*/ MASK currentMask(bool for_mouse_event); - /*virtual*/ void scanKeyboard(); - /*virtual*/ void handleModifier(MASK mask); + bool handleKeyUp(const NATIVE_KEY_TYPE key, MASK mask) override; + bool handleKeyDown(const NATIVE_KEY_TYPE key, MASK mask) override; + void resetMaskKeys() override; + MASK currentMask(bool for_mouse_event) override; + void scanKeyboard() override; + void handleModifier(MASK mask) override; protected: - MASK updateModifiers(const U32 mask); + MASK updateModifiers(const MASK mask); void setModifierKeyLevel( KEY key, bool new_state ); bool translateNumpadKey( const U16 os_key, KEY *translated_key ); U16 inverseTranslateNumpadKey(const KEY translated_key); diff --git a/indra/llwindow/llkeyboardsdl.cpp b/indra/llwindow/llkeyboardsdl.cpp index 72b4577276..d951ce331e 100644 --- a/indra/llwindow/llkeyboardsdl.cpp +++ b/indra/llwindow/llkeyboardsdl.cpp @@ -1,5 +1,6 @@ /** - * @author This module has many fathers, and it shows. + * @file llkeyboardsdl.cpp + * @brief Handler for assignable key bindings * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code @@ -23,11 +24,12 @@ * $/LicenseInfo$ */ +#include <set> #include "linden_common.h" #include "llkeyboardsdl.h" #include "llwindowcallbacks.h" -#include "SDL2/SDL.h" -#include "SDL2/SDL_keycode.h" + +#include "SDL3/SDL_keycode.h" LLKeyboardSDL::LLKeyboardSDL() { @@ -38,11 +40,7 @@ LLKeyboardSDL::LLKeyboardSDL() // Virtual key mappings from SDL_keysym.h ... // SDL maps the letter keys to the ASCII you'd expect, but it's lowercase... - - // <FS:ND> Looks like we need to map those despite of SDL_TEXTINPUT handling most of this, but without - // the translation lower->upper here accelerators will not work. - - U16 cur_char; + LLKeyboard::NATIVE_KEY_TYPE cur_char; for (cur_char = 'A'; cur_char <= 'Z'; cur_char++) { mTranslateKeyMap[cur_char] = cur_char; @@ -70,7 +68,7 @@ LLKeyboardSDL::LLKeyboardSDL() //mTranslateKeyMap[SDLK_KP3] = KEY_PAGE_DOWN; //mTranslateKeyMap[SDLK_KP0] = KEY_INSERT; - mTranslateKeyMap[SDLK_SPACE] = ' '; // <FS:ND/> Those are handled by SDL2 via text input, do not map them + mTranslateKeyMap[SDLK_SPACE] = ' '; mTranslateKeyMap[SDLK_RETURN] = KEY_RETURN; mTranslateKeyMap[SDLK_LEFT] = KEY_LEFT; mTranslateKeyMap[SDLK_RIGHT] = KEY_RIGHT; @@ -112,17 +110,17 @@ LLKeyboardSDL::LLKeyboardSDL() mTranslateKeyMap[SDLK_F10] = KEY_F10; mTranslateKeyMap[SDLK_F11] = KEY_F11; mTranslateKeyMap[SDLK_F12] = KEY_F12; - mTranslateKeyMap[SDLK_PLUS] = '='; // <FS:ND/> Those are handled by SDL2 via text input, do not map them - mTranslateKeyMap[SDLK_COMMA] = ','; // <FS:ND/> Those are handled by SDL2 via text input, do not map them - mTranslateKeyMap[SDLK_MINUS] = '-'; // <FS:ND/> Those are handled by SDL2 via text input, do not map them - mTranslateKeyMap[SDLK_PERIOD] = '.'; // <FS:ND/> Those are handled by SDL2 via text input, do not map them - mTranslateKeyMap[SDLK_BACKQUOTE] = '`'; // <FS:ND/> Those are handled by SDL2 via text input, do not map them - mTranslateKeyMap[SDLK_SLASH] = KEY_DIVIDE; // <FS:ND/> Those are handled by SDL2 via text input, do not map them - mTranslateKeyMap[SDLK_SEMICOLON] = ';'; // <FS:ND/> Those are handled by SDL2 via text input, do not map them - mTranslateKeyMap[SDLK_LEFTBRACKET] = '['; // <FS:ND/> Those are handled by SDL2 via text input, do not map them - mTranslateKeyMap[SDLK_BACKSLASH] = '\\'; // <FS:ND/> Those are handled by SDL2 via text input, do not map them - mTranslateKeyMap[SDLK_RIGHTBRACKET] = ']'; // <FS:ND/> Those are handled by SDL2 via text input, do not map them - mTranslateKeyMap[SDLK_QUOTE] = '\''; // <FS:ND/> Those are handled by SDL2 via text input, do not map them + mTranslateKeyMap[SDLK_PLUS] = '='; + mTranslateKeyMap[SDLK_COMMA] = ','; + mTranslateKeyMap[SDLK_MINUS] = '-'; + mTranslateKeyMap[SDLK_PERIOD] = '.'; + mTranslateKeyMap[SDLK_GRAVE] = '`'; + mTranslateKeyMap[SDLK_SLASH] = KEY_DIVIDE; + mTranslateKeyMap[SDLK_SEMICOLON] = ';'; + mTranslateKeyMap[SDLK_LEFTBRACKET] = '['; + mTranslateKeyMap[SDLK_BACKSLASH] = '\\'; + mTranslateKeyMap[SDLK_RIGHTBRACKET] = ']'; + mTranslateKeyMap[SDLK_APOSTROPHE] = '\''; // Build inverse map for (auto iter = mTranslateKeyMap.begin(); iter != mTranslateKeyMap.end(); iter++) @@ -160,12 +158,12 @@ void LLKeyboardSDL::resetMaskKeys() // It looks a bit suspicious, as it won't correct for keys that have been released. // Is this the way it's supposed to work? - if(mask & KMOD_SHIFT) + if(mask & SDL_KMOD_SHIFT) { mKeyLevel[KEY_SHIFT] = true; } - if(mask & (KMOD_CTRL + if(mask & (SDL_KMOD_CTRL #ifdef LL_DARWIN | KMOD_GUI #endif @@ -174,24 +172,24 @@ void LLKeyboardSDL::resetMaskKeys() mKeyLevel[KEY_CONTROL] = true; } - if(mask & KMOD_ALT) + if(mask & SDL_KMOD_ALT) { mKeyLevel[KEY_ALT] = true; } } -MASK LLKeyboardSDL::updateModifiers(const U32 mask) +MASK LLKeyboardSDL::updateModifiers(const MASK mask) { // translate the mask MASK out_mask = MASK_NONE; - if(mask & KMOD_SHIFT) + if(mask & SDL_KMOD_SHIFT) { out_mask |= MASK_SHIFT; } - if(mask & (KMOD_CTRL + if(mask & (SDL_KMOD_CTRL #ifdef LL_DARWIN | KMOD_GUI #endif @@ -200,7 +198,7 @@ MASK LLKeyboardSDL::updateModifiers(const U32 mask) out_mask |= MASK_CONTROL; } - if(mask & KMOD_ALT) + if(mask & SDL_KMOD_ALT) { out_mask |= MASK_ALT; } @@ -209,12 +207,12 @@ MASK LLKeyboardSDL::updateModifiers(const U32 mask) } -static U32 adjustNativekeyFromUnhandledMask(const U32 key, const U32 mask) +LLKeyboard::NATIVE_KEY_TYPE adjustNativekeyFromUnhandledMask(const LLKeyboard::NATIVE_KEY_TYPE key, const MASK mask) { // SDL doesn't automatically adjust the keysym according to // whether NUMLOCK is engaged, so we massage the keysym manually. - U32 rtn = key; - if (!(mask & KMOD_NUM)) + LLKeyboard::NATIVE_KEY_TYPE rtn = key; + if (!(mask & SDL_KMOD_NUM)) { switch (key) { @@ -234,12 +232,12 @@ static U32 adjustNativekeyFromUnhandledMask(const U32 key, const U32 mask) } -bool LLKeyboardSDL::handleKeyDown(const U32 key, const U32 mask) +bool LLKeyboardSDL::handleKeyDown(const LLKeyboard::NATIVE_KEY_TYPE key, const MASK mask) { - U32 adjusted_nativekey; + LLKeyboard::NATIVE_KEY_TYPE adjusted_nativekey; KEY translated_key = 0; - U32 translated_mask = MASK_NONE; - bool handled = false; + MASK translated_mask = MASK_NONE; + bool handled = false; adjusted_nativekey = adjustNativekeyFromUnhandledMask(key, mask); @@ -254,12 +252,12 @@ bool LLKeyboardSDL::handleKeyDown(const U32 key, const U32 mask) } -bool LLKeyboardSDL::handleKeyUp(const U32 key, const U32 mask) +bool LLKeyboardSDL::handleKeyUp(const LLKeyboard::NATIVE_KEY_TYPE key, const MASK mask) { - U32 adjusted_nativekey; + LLKeyboard::NATIVE_KEY_TYPE adjusted_nativekey; KEY translated_key = 0; U32 translated_mask = MASK_NONE; - bool handled = false; + bool handled = false; adjusted_nativekey = adjustNativekeyFromUnhandledMask(key, mask); @@ -278,17 +276,17 @@ MASK LLKeyboardSDL::currentMask(bool for_mouse_event) MASK result = MASK_NONE; SDL_Keymod mask = SDL_GetModState(); - if (mask & KMOD_SHIFT) + if (mask & SDL_KMOD_SHIFT) result |= MASK_SHIFT; - if (mask & KMOD_CTRL) + if (mask & SDL_KMOD_CTRL) result |= MASK_CONTROL; - if (mask & KMOD_ALT) + if (mask & SDL_KMOD_ALT) result |= MASK_ALT; // For keyboard events, consider Meta keys equivalent to Control if (!for_mouse_event) { - if (mask & KMOD_GUI) + if (mask & SDL_KMOD_GUI) result |= MASK_CONTROL; } @@ -308,6 +306,7 @@ void LLKeyboardSDL::scanKeyboard() mCallbacks->handleScanKey(key, mKeyDown[key], mKeyUp[key], mKeyLevel[key]); } } + mCurScanKey = KEY_NONE; // Reset edges for next frame for (S32 key = 0; key < KEY_COUNT; key++) @@ -322,347 +321,346 @@ void LLKeyboardSDL::scanKeyboard() } -bool LLKeyboardSDL::translateNumpadKey( const U32 os_key, KEY *translated_key) +bool LLKeyboardSDL::translateNumpadKey( const LLKeyboard::NATIVE_KEY_TYPE os_key, KEY *translated_key) { return translateKey(os_key, translated_key); } -U16 LLKeyboardSDL::inverseTranslateNumpadKey(const KEY translated_key) +LLKeyboard::NATIVE_KEY_TYPE LLKeyboardSDL::inverseTranslateNumpadKey(const KEY translated_key) { return inverseTranslateKey(translated_key); } enum class WindowsVK : U32 { - VK_UNKNOWN = 0, - VK_CANCEL = 0x03, - VK_BACK = 0x08, - VK_TAB = 0x09, - VK_CLEAR = 0x0C, - VK_RETURN = 0x0D, - VK_SHIFT = 0x10, - VK_CONTROL = 0x11, - VK_MENU = 0x12, - VK_PAUSE = 0x13, - VK_CAPITAL = 0x14, - VK_KANA = 0x15, - VK_HANGUL = 0x15, - VK_JUNJA = 0x17, - VK_FINAL = 0x18, - VK_HANJA = 0x19, - VK_KANJI = 0x19, - VK_ESCAPE = 0x1B, - VK_CONVERT = 0x1C, - VK_NONCONVERT = 0x1D, - VK_ACCEPT = 0x1E, - VK_MODECHANGE = 0x1F, - VK_SPACE = 0x20, - VK_PRIOR = 0x21, - VK_NEXT = 0x22, - VK_END = 0x23, - VK_HOME = 0x24, - VK_LEFT = 0x25, - VK_UP = 0x26, - VK_RIGHT = 0x27, - VK_DOWN = 0x28, - VK_SELECT = 0x29, - VK_PRINT = 0x2A, - VK_EXECUTE = 0x2B, - VK_SNAPSHOT = 0x2C, - VK_INSERT = 0x2D, - VK_DELETE = 0x2E, - VK_HELP = 0x2F, - VK_0 = 0x30, - VK_1 = 0x31, - VK_2 = 0x32, - VK_3 = 0x33, - VK_4 = 0x34, - VK_5 = 0x35, - VK_6 = 0x36, - VK_7 = 0x37, - VK_8 = 0x38, - VK_9 = 0x39, - VK_A = 0x41, - VK_B = 0x42, - VK_C = 0x43, - VK_D = 0x44, - VK_E = 0x45, - VK_F = 0x46, - VK_G = 0x47, - VK_H = 0x48, - VK_I = 0x49, - VK_J = 0x4A, - VK_K = 0x4B, - VK_L = 0x4C, - VK_M = 0x4D, - VK_N = 0x4E, - VK_O = 0x4F, - VK_P = 0x50, - VK_Q = 0x51, - VK_R = 0x52, - VK_S = 0x53, - VK_T = 0x54, - VK_U = 0x55, - VK_V = 0x56, - VK_W = 0x57, - VK_X = 0x58, - VK_Y = 0x59, - VK_Z = 0x5A, - VK_LWIN = 0x5B, - VK_RWIN = 0x5C, - VK_APPS = 0x5D, - VK_SLEEP = 0x5F, - VK_NUMPAD0 = 0x60, - VK_NUMPAD1 = 0x61, - VK_NUMPAD2 = 0x62, - VK_NUMPAD3 = 0x63, - VK_NUMPAD4 = 0x64, - VK_NUMPAD5 = 0x65, - VK_NUMPAD6 = 0x66, - VK_NUMPAD7 = 0x67, - VK_NUMPAD8 = 0x68, - VK_NUMPAD9 = 0x69, - VK_MULTIPLY = 0x6A, - VK_ADD = 0x6B, - VK_SEPARATOR = 0x6C, - VK_SUBTRACT = 0x6D, - VK_DECIMAL = 0x6E, - VK_DIVIDE = 0x6F, - VK_F1 = 0x70, - VK_F2 = 0x71, - VK_F3 = 0x72, - VK_F4 = 0x73, - VK_F5 = 0x74, - VK_F6 = 0x75, - VK_F7 = 0x76, - VK_F8 = 0x77, - VK_F9 = 0x78, - VK_F10 = 0x79, - VK_F11 = 0x7A, - VK_F12 = 0x7B, - VK_F13 = 0x7C, - VK_F14 = 0x7D, - VK_F15 = 0x7E, - VK_F16 = 0x7F, - VK_F17 = 0x80, - VK_F18 = 0x81, - VK_F19 = 0x82, - VK_F20 = 0x83, - VK_F21 = 0x84, - VK_F22 = 0x85, - VK_F23 = 0x86, - VK_F24 = 0x87, - VK_NUMLOCK = 0x90, - VK_SCROLL = 0x91, - VK_LSHIFT = 0xA0, - VK_RSHIFT = 0xA1, - VK_LCONTROL = 0xA2, - VK_RCONTROL = 0xA3, - VK_LMENU = 0xA4, - VK_RMENU = 0xA5, - VK_BROWSER_BACK = 0xA6, - VK_BROWSER_FORWARD = 0xA7, - VK_BROWSER_REFRESH = 0xA8, - VK_BROWSER_STOP = 0xA9, - VK_BROWSER_SEARCH = 0xAA, - VK_BROWSER_FAVORITES = 0xAB, - VK_BROWSER_HOME = 0xAC, - VK_VOLUME_MUTE = 0xAD, - VK_VOLUME_DOWN = 0xAE, - VK_VOLUME_UP = 0xAF, - VK_MEDIA_NEXT_TRACK = 0xB0, - VK_MEDIA_PREV_TRACK = 0xB1, - VK_MEDIA_STOP = 0xB2, - VK_MEDIA_PLAY_PAUSE = 0xB3, - VK_MEDIA_LAUNCH_MAIL = 0xB4, - VK_MEDIA_LAUNCH_MEDIA_SELECT = 0xB5, - VK_MEDIA_LAUNCH_APP1 = 0xB6, - VK_MEDIA_LAUNCH_APP2 = 0xB7, - VK_OEM_1 = 0xBA, - VK_OEM_PLUS = 0xBB, - VK_OEM_COMMA = 0xBC, - VK_OEM_MINUS = 0xBD, - VK_OEM_PERIOD = 0xBE, - VK_OEM_2 = 0xBF, - VK_OEM_3 = 0xC0, - VK_OEM_4 = 0xDB, - VK_OEM_5 = 0xDC, - VK_OEM_6 = 0xDD, - VK_OEM_7 = 0xDE, - VK_OEM_8 = 0xDF, - VK_OEM_102 = 0xE2, - VK_PROCESSKEY = 0xE5, - VK_PACKET = 0xE7, - VK_ATTN = 0xF6, - VK_CRSEL = 0xF7, - VK_EXSEL = 0xF8, - VK_EREOF = 0xF9, - VK_PLAY = 0xFA, - VK_ZOOM = 0xFB, - VK_NONAME = 0xFC, - VK_PA1 = 0xFD, - VK_OEM_CLEAR = 0xFE, + LL_VK_UNKNOWN = 0, + LL_VK_CANCEL = 0x03, + LL_VK_BACK = 0x08, + LL_VK_TAB = 0x09, + LL_VK_CLEAR = 0x0C, + LL_VK_RETURN = 0x0D, + LL_VK_SHIFT = 0x10, + LL_VK_CONTROL = 0x11, + LL_VK_MENU = 0x12, + LL_VK_PAUSE = 0x13, + LL_VK_CAPITAL = 0x14, + LL_VK_KANA = 0x15, + LL_VK_HANGUL = 0x15, + LL_VK_JUNJA = 0x17, + LL_VK_FINAL = 0x18, + LL_VK_HANJA = 0x19, + LL_VK_KANJI = 0x19, + LL_VK_ESCAPE = 0x1B, + LL_VK_CONVERT = 0x1C, + LL_VK_NONCONVERT = 0x1D, + LL_VK_ACCEPT = 0x1E, + LL_VK_MODECHANGE = 0x1F, + LL_VK_SPACE = 0x20, + LL_VK_PRIOR = 0x21, + LL_VK_NEXT = 0x22, + LL_VK_END = 0x23, + LL_VK_HOME = 0x24, + LL_VK_LEFT = 0x25, + LL_VK_UP = 0x26, + LL_VK_RIGHT = 0x27, + LL_VK_DOWN = 0x28, + LL_VK_SELECT = 0x29, + LL_VK_PRINT = 0x2A, + LL_VK_EXECUTE = 0x2B, + LL_VK_SNAPSHOT = 0x2C, + LL_VK_INSERT = 0x2D, + LL_VK_DELETE = 0x2E, + LL_VK_HELP = 0x2F, + LL_VK_0 = 0x30, + LL_VK_1 = 0x31, + LL_VK_2 = 0x32, + LL_VK_3 = 0x33, + LL_VK_4 = 0x34, + LL_VK_5 = 0x35, + LL_VK_6 = 0x36, + LL_VK_7 = 0x37, + LL_VK_8 = 0x38, + LL_VK_9 = 0x39, + LL_VK_A = 0x41, + LL_VK_B = 0x42, + LL_VK_C = 0x43, + LL_VK_D = 0x44, + LL_VK_E = 0x45, + LL_VK_F = 0x46, + LL_VK_G = 0x47, + LL_VK_H = 0x48, + LL_VK_I = 0x49, + LL_VK_J = 0x4A, + LL_VK_K = 0x4B, + LL_VK_L = 0x4C, + LL_VK_M = 0x4D, + LL_VK_N = 0x4E, + LL_VK_O = 0x4F, + LL_VK_P = 0x50, + LL_VK_Q = 0x51, + LL_VK_R = 0x52, + LL_VK_S = 0x53, + LL_VK_T = 0x54, + LL_VK_U = 0x55, + LL_VK_V = 0x56, + LL_VK_W = 0x57, + LL_VK_X = 0x58, + LL_VK_Y = 0x59, + LL_VK_Z = 0x5A, + LL_VK_LWIN = 0x5B, + LL_VK_RWIN = 0x5C, + LL_VK_APPS = 0x5D, + LL_VK_SLEEP = 0x5F, + LL_VK_NUMPAD0 = 0x60, + LL_VK_NUMPAD1 = 0x61, + LL_VK_NUMPAD2 = 0x62, + LL_VK_NUMPAD3 = 0x63, + LL_VK_NUMPAD4 = 0x64, + LL_VK_NUMPAD5 = 0x65, + LL_VK_NUMPAD6 = 0x66, + LL_VK_NUMPAD7 = 0x67, + LL_VK_NUMPAD8 = 0x68, + LL_VK_NUMPAD9 = 0x69, + LL_VK_MULTIPLY = 0x6A, + LL_VK_ADD = 0x6B, + LL_VK_SEPARATOR = 0x6C, + LL_VK_SUBTRACT = 0x6D, + LL_VK_DECIMAL = 0x6E, + LL_VK_DIVIDE = 0x6F, + LL_VK_F1 = 0x70, + LL_VK_F2 = 0x71, + LL_VK_F3 = 0x72, + LL_VK_F4 = 0x73, + LL_VK_F5 = 0x74, + LL_VK_F6 = 0x75, + LL_VK_F7 = 0x76, + LL_VK_F8 = 0x77, + LL_VK_F9 = 0x78, + LL_VK_F10 = 0x79, + LL_VK_F11 = 0x7A, + LL_VK_F12 = 0x7B, + LL_VK_F13 = 0x7C, + LL_VK_F14 = 0x7D, + LL_VK_F15 = 0x7E, + LL_VK_F16 = 0x7F, + LL_VK_F17 = 0x80, + LL_VK_F18 = 0x81, + LL_VK_F19 = 0x82, + LL_VK_F20 = 0x83, + LL_VK_F21 = 0x84, + LL_VK_F22 = 0x85, + LL_VK_F23 = 0x86, + LL_VK_F24 = 0x87, + LL_VK_NUMLOCK = 0x90, + LL_VK_SCROLL = 0x91, + LL_VK_LSHIFT = 0xA0, + LL_VK_RSHIFT = 0xA1, + LL_VK_LCONTROL = 0xA2, + LL_VK_RCONTROL = 0xA3, + LL_VK_LMENU = 0xA4, + LL_VK_RMENU = 0xA5, + LL_VK_BROWSER_BACK = 0xA6, + LL_VK_BROWSER_FORWARD = 0xA7, + LL_VK_BROWSER_REFRESH = 0xA8, + LL_VK_BROWSER_STOP = 0xA9, + LL_VK_BROWSER_SEARCH = 0xAA, + LL_VK_BROWSER_FAVORITES = 0xAB, + LL_VK_BROWSER_HOME = 0xAC, + LL_VK_VOLUME_MUTE = 0xAD, + LL_VK_VOLUME_DOWN = 0xAE, + LL_VK_VOLUME_UP = 0xAF, + LL_VK_MEDIA_NEXT_TRACK = 0xB0, + LL_VK_MEDIA_PREV_TRACK = 0xB1, + LL_VK_MEDIA_STOP = 0xB2, + LL_VK_MEDIA_PLAY_PAUSE = 0xB3, + LL_VK_MEDIA_LAUNCH_MAIL = 0xB4, + LL_VK_MEDIA_LAUNCH_MEDIA_SELECT = 0xB5, + LL_VK_MEDIA_LAUNCH_APP1 = 0xB6, + LL_VK_MEDIA_LAUNCH_APP2 = 0xB7, + LL_VK_OEM_1 = 0xBA, + LL_VK_OEM_PLUS = 0xBB, + LL_VK_OEM_COMMA = 0xBC, + LL_VK_OEM_MINUS = 0xBD, + LL_VK_OEM_PERIOD = 0xBE, + LL_VK_OEM_2 = 0xBF, + LL_VK_OEM_3 = 0xC0, + LL_VK_OEM_4 = 0xDB, + LL_VK_OEM_5 = 0xDC, + LL_VK_OEM_6 = 0xDD, + LL_VK_OEM_7 = 0xDE, + LL_VK_OEM_8 = 0xDF, + LL_VK_OEM_102 = 0xE2, + LL_VK_PROCESSKEY = 0xE5, + LL_VK_PACKET = 0xE7, + LL_VK_ATTN = 0xF6, + LL_VK_CRSEL = 0xF7, + LL_VK_EXSEL = 0xF8, + LL_VK_EREOF = 0xF9, + LL_VK_PLAY = 0xFA, + LL_VK_ZOOM = 0xFB, + LL_VK_NONAME = 0xFC, + LL_VK_PA1 = 0xFD, + LL_VK_OEM_CLEAR = 0xFE, }; -std::map< U32, U32 > mSDL2_to_Win; -std::set< U32 > mIgnoreSDL2Keys; +std::map< U32, U32 > mSDL_to_Win; -U32 LLKeyboardSDL::mapSDL2toWin( U32 aSymbol ) +U32 LLKeyboardSDL::mapSDLtoWin( U32 aSymbol ) { - // <FS:ND> Map SDLK_ virtual keys to Windows VK_ virtual keys. - // Text is handled via unicode input (SDL_TEXTINPUT event) and does not need to be translated into VK_ values as those match already. - if( mSDL2_to_Win.empty() ) + // <FS:ND> Map SDLK_ virtual keys to Windows LL_VK_ virtual keys. + // Text is handled via unicode input (SDL_TEXTINPUT event) and does not need to be translated into LL_VK_ values as those match already. + if( mSDL_to_Win.empty() ) { - mSDL2_to_Win[ SDLK_BACKSPACE ] = (U32)WindowsVK::VK_BACK; - mSDL2_to_Win[ SDLK_TAB ] = (U32)WindowsVK::VK_TAB; - mSDL2_to_Win[ 12 ] = (U32)WindowsVK::VK_CLEAR; - mSDL2_to_Win[ SDLK_RETURN ] = (U32)WindowsVK::VK_RETURN; - mSDL2_to_Win[ 19 ] = (U32)WindowsVK::VK_PAUSE; - mSDL2_to_Win[ SDLK_ESCAPE ] = (U32)WindowsVK::VK_ESCAPE; - mSDL2_to_Win[ SDLK_SPACE ] = (U32)WindowsVK::VK_SPACE; - mSDL2_to_Win[ SDLK_QUOTE ] = (U32)WindowsVK::VK_OEM_7; - mSDL2_to_Win[ SDLK_COMMA ] = (U32)WindowsVK::VK_OEM_COMMA; - mSDL2_to_Win[ SDLK_MINUS ] = (U32)WindowsVK::VK_OEM_MINUS; - mSDL2_to_Win[ SDLK_PERIOD ] = (U32)WindowsVK::VK_OEM_PERIOD; - mSDL2_to_Win[ SDLK_SLASH ] = (U32)WindowsVK::VK_OEM_2; + mSDL_to_Win[ SDLK_BACKSPACE ] = (U32)WindowsVK::LL_VK_BACK; + mSDL_to_Win[ SDLK_TAB ] = (U32)WindowsVK::LL_VK_TAB; + mSDL_to_Win[ 12 ] = (U32)WindowsVK::LL_VK_CLEAR; + mSDL_to_Win[ SDLK_RETURN ] = (U32)WindowsVK::LL_VK_RETURN; + mSDL_to_Win[ 19 ] = (U32)WindowsVK::LL_VK_PAUSE; + mSDL_to_Win[ SDLK_ESCAPE ] = (U32)WindowsVK::LL_VK_ESCAPE; + mSDL_to_Win[ SDLK_SPACE ] = (U32)WindowsVK::LL_VK_SPACE; + mSDL_to_Win[ SDLK_APOSTROPHE ] = (U32)WindowsVK::LL_VK_OEM_7; + mSDL_to_Win[ SDLK_COMMA ] = (U32)WindowsVK::LL_VK_OEM_COMMA; + mSDL_to_Win[ SDLK_MINUS ] = (U32)WindowsVK::LL_VK_OEM_MINUS; + mSDL_to_Win[ SDLK_PERIOD ] = (U32)WindowsVK::LL_VK_OEM_PERIOD; + mSDL_to_Win[ SDLK_SLASH ] = (U32)WindowsVK::LL_VK_OEM_2; - mSDL2_to_Win[ SDLK_0 ] = (U32)WindowsVK::VK_0; - mSDL2_to_Win[ SDLK_1 ] = (U32)WindowsVK::VK_1; - mSDL2_to_Win[ SDLK_2 ] = (U32)WindowsVK::VK_2; - mSDL2_to_Win[ SDLK_3 ] = (U32)WindowsVK::VK_3; - mSDL2_to_Win[ SDLK_4 ] = (U32)WindowsVK::VK_4; - mSDL2_to_Win[ SDLK_5 ] = (U32)WindowsVK::VK_5; - mSDL2_to_Win[ SDLK_6 ] = (U32)WindowsVK::VK_6; - mSDL2_to_Win[ SDLK_7 ] = (U32)WindowsVK::VK_7; - mSDL2_to_Win[ SDLK_8 ] = (U32)WindowsVK::VK_8; - mSDL2_to_Win[ SDLK_9 ] = (U32)WindowsVK::VK_9; + mSDL_to_Win[ SDLK_0 ] = (U32)WindowsVK::LL_VK_0; + mSDL_to_Win[ SDLK_1 ] = (U32)WindowsVK::LL_VK_1; + mSDL_to_Win[ SDLK_2 ] = (U32)WindowsVK::LL_VK_2; + mSDL_to_Win[ SDLK_3 ] = (U32)WindowsVK::LL_VK_3; + mSDL_to_Win[ SDLK_4 ] = (U32)WindowsVK::LL_VK_4; + mSDL_to_Win[ SDLK_5 ] = (U32)WindowsVK::LL_VK_5; + mSDL_to_Win[ SDLK_6 ] = (U32)WindowsVK::LL_VK_6; + mSDL_to_Win[ SDLK_7 ] = (U32)WindowsVK::LL_VK_7; + mSDL_to_Win[ SDLK_8 ] = (U32)WindowsVK::LL_VK_8; + mSDL_to_Win[ SDLK_9 ] = (U32)WindowsVK::LL_VK_9; - mSDL2_to_Win[ SDLK_SEMICOLON ] = (U32)WindowsVK::VK_OEM_1; - mSDL2_to_Win[ SDLK_LESS ] = (U32)WindowsVK::VK_OEM_102; - mSDL2_to_Win[ SDLK_EQUALS ] = (U32)WindowsVK::VK_OEM_PLUS; - mSDL2_to_Win[ SDLK_KP_EQUALS ] = (U32)WindowsVK::VK_OEM_PLUS; + mSDL_to_Win[ SDLK_SEMICOLON ] = (U32)WindowsVK::LL_VK_OEM_1; + mSDL_to_Win[ SDLK_LESS ] = (U32)WindowsVK::LL_VK_OEM_102; + mSDL_to_Win[ SDLK_EQUALS ] = (U32)WindowsVK::LL_VK_OEM_PLUS; + mSDL_to_Win[ SDLK_KP_EQUALS ] = (U32)WindowsVK::LL_VK_OEM_PLUS; - mSDL2_to_Win[ SDLK_LEFTBRACKET ] = (U32)WindowsVK::VK_OEM_4; - mSDL2_to_Win[ SDLK_BACKSLASH ] = (U32)WindowsVK::VK_OEM_5; - mSDL2_to_Win[ SDLK_RIGHTBRACKET ] = (U32)WindowsVK::VK_OEM_6; - mSDL2_to_Win[ SDLK_BACKQUOTE ] = (U32)WindowsVK::VK_OEM_8; + mSDL_to_Win[ SDLK_LEFTBRACKET ] = (U32)WindowsVK::LL_VK_OEM_4; + mSDL_to_Win[ SDLK_BACKSLASH ] = (U32)WindowsVK::LL_VK_OEM_5; + mSDL_to_Win[ SDLK_RIGHTBRACKET ] = (U32)WindowsVK::LL_VK_OEM_6; + mSDL_to_Win[ SDLK_GRAVE ] = (U32)WindowsVK::LL_VK_OEM_8; - mSDL2_to_Win[ SDLK_a ] = (U32)WindowsVK::VK_A; - mSDL2_to_Win[ SDLK_b ] = (U32)WindowsVK::VK_B; - mSDL2_to_Win[ SDLK_c ] = (U32)WindowsVK::VK_C; - mSDL2_to_Win[ SDLK_d ] = (U32)WindowsVK::VK_D; - mSDL2_to_Win[ SDLK_e ] = (U32)WindowsVK::VK_E; - mSDL2_to_Win[ SDLK_f ] = (U32)WindowsVK::VK_F; - mSDL2_to_Win[ SDLK_g ] = (U32)WindowsVK::VK_G; - mSDL2_to_Win[ SDLK_h ] = (U32)WindowsVK::VK_H; - mSDL2_to_Win[ SDLK_i ] = (U32)WindowsVK::VK_I; - mSDL2_to_Win[ SDLK_j ] = (U32)WindowsVK::VK_J; - mSDL2_to_Win[ SDLK_k ] = (U32)WindowsVK::VK_K; - mSDL2_to_Win[ SDLK_l ] = (U32)WindowsVK::VK_L; - mSDL2_to_Win[ SDLK_m ] = (U32)WindowsVK::VK_M; - mSDL2_to_Win[ SDLK_n ] = (U32)WindowsVK::VK_N; - mSDL2_to_Win[ SDLK_o ] = (U32)WindowsVK::VK_O; - mSDL2_to_Win[ SDLK_p ] = (U32)WindowsVK::VK_P; - mSDL2_to_Win[ SDLK_q ] = (U32)WindowsVK::VK_Q; - mSDL2_to_Win[ SDLK_r ] = (U32)WindowsVK::VK_R; - mSDL2_to_Win[ SDLK_s ] = (U32)WindowsVK::VK_S; - mSDL2_to_Win[ SDLK_t ] = (U32)WindowsVK::VK_T; - mSDL2_to_Win[ SDLK_u ] = (U32)WindowsVK::VK_U; - mSDL2_to_Win[ SDLK_v ] = (U32)WindowsVK::VK_V; - mSDL2_to_Win[ SDLK_w ] = (U32)WindowsVK::VK_W; - mSDL2_to_Win[ SDLK_x ] = (U32)WindowsVK::VK_X; - mSDL2_to_Win[ SDLK_y ] = (U32)WindowsVK::VK_Y; - mSDL2_to_Win[ SDLK_z ] = (U32)WindowsVK::VK_Z; + mSDL_to_Win[ SDLK_A ] = (U32)WindowsVK::LL_VK_A; + mSDL_to_Win[ SDLK_B ] = (U32)WindowsVK::LL_VK_B; + mSDL_to_Win[ SDLK_C ] = (U32)WindowsVK::LL_VK_C; + mSDL_to_Win[ SDLK_D ] = (U32)WindowsVK::LL_VK_D; + mSDL_to_Win[ SDLK_E ] = (U32)WindowsVK::LL_VK_E; + mSDL_to_Win[ SDLK_F ] = (U32)WindowsVK::LL_VK_F; + mSDL_to_Win[ SDLK_G ] = (U32)WindowsVK::LL_VK_G; + mSDL_to_Win[ SDLK_H ] = (U32)WindowsVK::LL_VK_H; + mSDL_to_Win[ SDLK_I ] = (U32)WindowsVK::LL_VK_I; + mSDL_to_Win[ SDLK_J ] = (U32)WindowsVK::LL_VK_J; + mSDL_to_Win[ SDLK_K ] = (U32)WindowsVK::LL_VK_K; + mSDL_to_Win[ SDLK_L ] = (U32)WindowsVK::LL_VK_L; + mSDL_to_Win[ SDLK_M ] = (U32)WindowsVK::LL_VK_M; + mSDL_to_Win[ SDLK_N ] = (U32)WindowsVK::LL_VK_N; + mSDL_to_Win[ SDLK_O ] = (U32)WindowsVK::LL_VK_O; + mSDL_to_Win[ SDLK_P ] = (U32)WindowsVK::LL_VK_P; + mSDL_to_Win[ SDLK_Q ] = (U32)WindowsVK::LL_VK_Q; + mSDL_to_Win[ SDLK_R ] = (U32)WindowsVK::LL_VK_R; + mSDL_to_Win[ SDLK_S ] = (U32)WindowsVK::LL_VK_S; + mSDL_to_Win[ SDLK_T ] = (U32)WindowsVK::LL_VK_T; + mSDL_to_Win[ SDLK_Y ] = (U32)WindowsVK::LL_VK_U; + mSDL_to_Win[ SDLK_V ] = (U32)WindowsVK::LL_VK_V; + mSDL_to_Win[ SDLK_W ] = (U32)WindowsVK::LL_VK_W; + mSDL_to_Win[ SDLK_X ] = (U32)WindowsVK::LL_VK_X; + mSDL_to_Win[ SDLK_Y ] = (U32)WindowsVK::LL_VK_Y; + mSDL_to_Win[ SDLK_Z ] = (U32)WindowsVK::LL_VK_Z; - mSDL2_to_Win[ SDLK_DELETE ] = (U32)WindowsVK::VK_DELETE; + mSDL_to_Win[ SDLK_DELETE ] = (U32)WindowsVK::LL_VK_DELETE; - mSDL2_to_Win[ SDLK_NUMLOCKCLEAR ] = (U32)WindowsVK::VK_NUMLOCK; - mSDL2_to_Win[ SDLK_SCROLLLOCK ] = (U32)WindowsVK::VK_SCROLL; + mSDL_to_Win[ SDLK_NUMLOCKCLEAR ] = (U32)WindowsVK::LL_VK_NUMLOCK; + mSDL_to_Win[ SDLK_SCROLLLOCK ] = (U32)WindowsVK::LL_VK_SCROLL; - mSDL2_to_Win[ SDLK_HELP ] = (U32)WindowsVK::VK_HELP; - mSDL2_to_Win[ SDLK_PRINTSCREEN ] = (U32)WindowsVK::VK_SNAPSHOT; - mSDL2_to_Win[ SDLK_CANCEL ] = (U32)WindowsVK::VK_CANCEL; - mSDL2_to_Win[ SDLK_APPLICATION ] = (U32)WindowsVK::VK_APPS; + mSDL_to_Win[ SDLK_HELP ] = (U32)WindowsVK::LL_VK_HELP; + mSDL_to_Win[ SDLK_PRINTSCREEN ] = (U32)WindowsVK::LL_VK_SNAPSHOT; + mSDL_to_Win[ SDLK_CANCEL ] = (U32)WindowsVK::LL_VK_CANCEL; + mSDL_to_Win[ SDLK_APPLICATION ] = (U32)WindowsVK::LL_VK_APPS; - mSDL2_to_Win[ SDLK_UNKNOWN ] = (U32)WindowsVK::VK_UNKNOWN; - mSDL2_to_Win[ SDLK_BACKSPACE ] = (U32)WindowsVK::VK_BACK; - mSDL2_to_Win[ SDLK_TAB ] = (U32)WindowsVK::VK_TAB; - mSDL2_to_Win[ SDLK_CLEAR ] = (U32)WindowsVK::VK_CLEAR; - mSDL2_to_Win[ SDLK_RETURN ] = (U32)WindowsVK::VK_RETURN; - mSDL2_to_Win[ SDLK_PAUSE ] = (U32)WindowsVK::VK_PAUSE; - mSDL2_to_Win[ SDLK_ESCAPE ] = (U32)WindowsVK::VK_ESCAPE; - mSDL2_to_Win[ SDLK_DELETE ] = (U32)WindowsVK::VK_DELETE; + mSDL_to_Win[ SDLK_UNKNOWN ] = (U32)WindowsVK::LL_VK_UNKNOWN; + mSDL_to_Win[ SDLK_BACKSPACE ] = (U32)WindowsVK::LL_VK_BACK; + mSDL_to_Win[ SDLK_TAB ] = (U32)WindowsVK::LL_VK_TAB; + mSDL_to_Win[ SDLK_CLEAR ] = (U32)WindowsVK::LL_VK_CLEAR; + mSDL_to_Win[ SDLK_RETURN ] = (U32)WindowsVK::LL_VK_RETURN; + mSDL_to_Win[ SDLK_PAUSE ] = (U32)WindowsVK::LL_VK_PAUSE; + mSDL_to_Win[ SDLK_ESCAPE ] = (U32)WindowsVK::LL_VK_ESCAPE; + mSDL_to_Win[ SDLK_DELETE ] = (U32)WindowsVK::LL_VK_DELETE; - mSDL2_to_Win[ SDLK_KP_PERIOD ] = (U32)WindowsVK::VK_OEM_PERIOD; // VK_DECIMAL? - mSDL2_to_Win[ SDLK_KP_DIVIDE ] = (U32)WindowsVK::VK_DIVIDE; - mSDL2_to_Win[ SDLK_KP_MULTIPLY] = (U32)WindowsVK::VK_MULTIPLY; - mSDL2_to_Win[ SDLK_KP_MINUS ] = (U32)WindowsVK::VK_OEM_MINUS; // VK_SUBSTRACT? - mSDL2_to_Win[ SDLK_KP_PLUS ] = (U32)WindowsVK::VK_OEM_PLUS; // VK_ADD? - mSDL2_to_Win[ SDLK_KP_ENTER ] = (U32)WindowsVK::VK_RETURN; - mSDL2_to_Win[ SDLK_KP_0 ] = (U32)WindowsVK::VK_NUMPAD0; - mSDL2_to_Win[ SDLK_KP_1 ] = (U32)WindowsVK::VK_NUMPAD1; - mSDL2_to_Win[ SDLK_KP_2 ] = (U32)WindowsVK::VK_NUMPAD2; - mSDL2_to_Win[ SDLK_KP_3 ] = (U32)WindowsVK::VK_NUMPAD3; - mSDL2_to_Win[ SDLK_KP_4 ] = (U32)WindowsVK::VK_NUMPAD4; - mSDL2_to_Win[ SDLK_KP_5 ] = (U32)WindowsVK::VK_NUMPAD5; - mSDL2_to_Win[ SDLK_KP_6 ] = (U32)WindowsVK::VK_NUMPAD6; - mSDL2_to_Win[ SDLK_KP_7 ] = (U32)WindowsVK::VK_NUMPAD7; - mSDL2_to_Win[ SDLK_KP_8 ] = (U32)WindowsVK::VK_NUMPAD8; - mSDL2_to_Win[ SDLK_KP_9 ] = (U32)WindowsVK::VK_NUMPAD9; + mSDL_to_Win[ SDLK_KP_PERIOD ] = (U32)WindowsVK::LL_VK_DELETE; // LL_VK_DECIMAL? + mSDL_to_Win[ SDLK_KP_DIVIDE ] = (U32)WindowsVK::LL_VK_DIVIDE; + mSDL_to_Win[ SDLK_KP_MULTIPLY] = (U32)WindowsVK::LL_VK_MULTIPLY; + mSDL_to_Win[ SDLK_KP_MINUS ] = (U32)WindowsVK::LL_VK_OEM_MINUS; // LL_VK_SUBSTRACT? + mSDL_to_Win[ SDLK_KP_PLUS ] = (U32)WindowsVK::LL_VK_OEM_PLUS; // LL_VK_ADD? + mSDL_to_Win[ SDLK_KP_ENTER ] = (U32)WindowsVK::LL_VK_RETURN; + mSDL_to_Win[ SDLK_KP_0 ] = (U32)WindowsVK::LL_VK_INSERT; + mSDL_to_Win[ SDLK_KP_1 ] = (U32)WindowsVK::LL_VK_END; + mSDL_to_Win[ SDLK_KP_2 ] = (U32)WindowsVK::LL_VK_DOWN; + mSDL_to_Win[ SDLK_KP_3 ] = (U32)WindowsVK::LL_VK_NEXT; + mSDL_to_Win[ SDLK_KP_4 ] = (U32)WindowsVK::LL_VK_LEFT; + mSDL_to_Win[ SDLK_KP_5 ] = (U32)WindowsVK::LL_VK_NUMPAD5; + mSDL_to_Win[ SDLK_KP_6 ] = (U32)WindowsVK::LL_VK_RIGHT; + mSDL_to_Win[ SDLK_KP_7 ] = (U32)WindowsVK::LL_VK_HOME; + mSDL_to_Win[ SDLK_KP_8 ] = (U32)WindowsVK::LL_VK_UP; + mSDL_to_Win[ SDLK_KP_9 ] = (U32)WindowsVK::LL_VK_PRIOR; // ? - mSDL2_to_Win[ SDLK_UP ] = (U32)WindowsVK::VK_UP; - mSDL2_to_Win[ SDLK_DOWN ] = (U32)WindowsVK::VK_DOWN; - mSDL2_to_Win[ SDLK_RIGHT ] = (U32)WindowsVK::VK_RIGHT; - mSDL2_to_Win[ SDLK_LEFT ] = (U32)WindowsVK::VK_LEFT; - mSDL2_to_Win[ SDLK_INSERT ] = (U32)WindowsVK::VK_INSERT; - mSDL2_to_Win[ SDLK_HOME ] = (U32)WindowsVK::VK_HOME; - mSDL2_to_Win[ SDLK_END ] = (U32)WindowsVK::VK_END; - mSDL2_to_Win[ SDLK_PAGEUP ] = (U32)WindowsVK::VK_PRIOR; - mSDL2_to_Win[ SDLK_PAGEDOWN ] = (U32)WindowsVK::VK_NEXT; - mSDL2_to_Win[ SDLK_F1 ] = (U32)WindowsVK::VK_F1; - mSDL2_to_Win[ SDLK_F2 ] = (U32)WindowsVK::VK_F2; - mSDL2_to_Win[ SDLK_F3 ] = (U32)WindowsVK::VK_F3; - mSDL2_to_Win[ SDLK_F4 ] = (U32)WindowsVK::VK_F4; - mSDL2_to_Win[ SDLK_F5 ] = (U32)WindowsVK::VK_F5; - mSDL2_to_Win[ SDLK_F6 ] = (U32)WindowsVK::VK_F6; - mSDL2_to_Win[ SDLK_F7 ] = (U32)WindowsVK::VK_F7; - mSDL2_to_Win[ SDLK_F8 ] = (U32)WindowsVK::VK_F8; - mSDL2_to_Win[ SDLK_F9 ] = (U32)WindowsVK::VK_F9; - mSDL2_to_Win[ SDLK_F10 ] = (U32)WindowsVK::VK_F10; - mSDL2_to_Win[ SDLK_F11 ] = (U32)WindowsVK::VK_F11; - mSDL2_to_Win[ SDLK_F12 ] = (U32)WindowsVK::VK_F12; - mSDL2_to_Win[ SDLK_F13 ] = (U32)WindowsVK::VK_F13; - mSDL2_to_Win[ SDLK_F14 ] = (U32)WindowsVK::VK_F14; - mSDL2_to_Win[ SDLK_F15 ] = (U32)WindowsVK::VK_F15; - mSDL2_to_Win[ SDLK_CAPSLOCK ] = (U32)WindowsVK::VK_CAPITAL; - mSDL2_to_Win[ SDLK_RSHIFT ] = (U32)WindowsVK::VK_SHIFT; - mSDL2_to_Win[ SDLK_LSHIFT ] = (U32)WindowsVK::VK_SHIFT; - mSDL2_to_Win[ SDLK_RCTRL ] = (U32)WindowsVK::VK_CONTROL; - mSDL2_to_Win[ SDLK_LCTRL ] = (U32)WindowsVK::VK_CONTROL; - mSDL2_to_Win[ SDLK_RALT ] = (U32)WindowsVK::VK_MENU; - mSDL2_to_Win[ SDLK_LALT ] = (U32)WindowsVK::VK_MENU; + mSDL_to_Win[ SDLK_UP ] = (U32)WindowsVK::LL_VK_UP; + mSDL_to_Win[ SDLK_DOWN ] = (U32)WindowsVK::LL_VK_DOWN; + mSDL_to_Win[ SDLK_RIGHT ] = (U32)WindowsVK::LL_VK_RIGHT; + mSDL_to_Win[ SDLK_LEFT ] = (U32)WindowsVK::LL_VK_LEFT; + mSDL_to_Win[ SDLK_INSERT ] = (U32)WindowsVK::LL_VK_INSERT; + mSDL_to_Win[ SDLK_HOME ] = (U32)WindowsVK::LL_VK_HOME; + mSDL_to_Win[ SDLK_END ] = (U32)WindowsVK::LL_VK_END; + mSDL_to_Win[ SDLK_PAGEUP ] = (U32)WindowsVK::LL_VK_PRIOR; + mSDL_to_Win[ SDLK_PAGEDOWN ] = (U32)WindowsVK::LL_VK_NEXT; + mSDL_to_Win[ SDLK_F1 ] = (U32)WindowsVK::LL_VK_F1; + mSDL_to_Win[ SDLK_F2 ] = (U32)WindowsVK::LL_VK_F2; + mSDL_to_Win[ SDLK_F3 ] = (U32)WindowsVK::LL_VK_F3; + mSDL_to_Win[ SDLK_F4 ] = (U32)WindowsVK::LL_VK_F4; + mSDL_to_Win[ SDLK_F5 ] = (U32)WindowsVK::LL_VK_F5; + mSDL_to_Win[ SDLK_F6 ] = (U32)WindowsVK::LL_VK_F6; + mSDL_to_Win[ SDLK_F7 ] = (U32)WindowsVK::LL_VK_F7; + mSDL_to_Win[ SDLK_F8 ] = (U32)WindowsVK::LL_VK_F8; + mSDL_to_Win[ SDLK_F9 ] = (U32)WindowsVK::LL_VK_F9; + mSDL_to_Win[ SDLK_F10 ] = (U32)WindowsVK::LL_VK_F10; + mSDL_to_Win[ SDLK_F11 ] = (U32)WindowsVK::LL_VK_F11; + mSDL_to_Win[ SDLK_F12 ] = (U32)WindowsVK::LL_VK_F12; + mSDL_to_Win[ SDLK_F13 ] = (U32)WindowsVK::LL_VK_F13; + mSDL_to_Win[ SDLK_F14 ] = (U32)WindowsVK::LL_VK_F14; + mSDL_to_Win[ SDLK_F15 ] = (U32)WindowsVK::LL_VK_F15; + mSDL_to_Win[ SDLK_CAPSLOCK ] = (U32)WindowsVK::LL_VK_CAPITAL; + mSDL_to_Win[ SDLK_RSHIFT ] = (U32)WindowsVK::LL_VK_SHIFT; + mSDL_to_Win[ SDLK_LSHIFT ] = (U32)WindowsVK::LL_VK_SHIFT; + mSDL_to_Win[ SDLK_RCTRL ] = (U32)WindowsVK::LL_VK_CONTROL; + mSDL_to_Win[ SDLK_LCTRL ] = (U32)WindowsVK::LL_VK_CONTROL; + mSDL_to_Win[ SDLK_RALT ] = (U32)WindowsVK::LL_VK_MENU; + mSDL_to_Win[ SDLK_LALT ] = (U32)WindowsVK::LL_VK_MENU; - mSDL2_to_Win[ SDLK_MENU ] = (U32)WindowsVK::VK_MENU; + mSDL_to_Win[ SDLK_MENU ] = (U32)WindowsVK::LL_VK_MENU; - // VK_MODECHANGE ? - // mSDL2_to_Win[ SDLK_MODE ] = (U32)WindowsVK::VK_MODE; + // LL_VK_MODECHANGE ? + // mSDL_to_Win[ SDLK_MODE ] = (U32)WindowsVK::LL_VK_MODE; // ? - // mSDL2_to_Win[ SDLK_SYSREQ ] = (U32)WindowsVK::VK_SYSREQ; - // mSDL2_to_Win[ SDLK_POWER ] = (U32)WindowsVK::VK_POWER; - // mSDL2_to_Win[ SDLK_UNDO ] = (U32)WindowsVK::VK_UNDO; - // mSDL2_to_Win[ SDLK_KP_EQUALS ] = (U32)WindowsVK::VK_EQUALS; - // mSDL2_to_Win[ 311 ] = (U32)WindowsVK::VK_LWIN; - // mSDL2_to_Win[ 312 ] = (U32)WindowsVK::VK_RWIN; - // mSDL2_to_Win[ SDLK_COLON ] = ? + // mSDL_to_Win[ SDLK_SYSREQ ] = (U32)WindowsVK::LL_VK_SYSREQ; + // mSDL_to_Win[ SDLK_POWER ] = (U32)WindowsVK::LL_VK_POWER; + // mSDL_to_Win[ SDLK_UNDO ] = (U32)WindowsVK::LL_VK_UNDO; + // mSDL_to_Win[ SDLK_KP_EQUALS ] = (U32)WindowsVK::LL_VK_EQUALS; + // mSDL_to_Win[ 311 ] = (U32)WindowsVK::LL_VK_LWIN; + // mSDL_to_Win[ 312 ] = (U32)WindowsVK::LL_VK_RWIN; + // mSDL_to_Win[ SDLK_COLON ] = ? } - auto itr = mSDL2_to_Win.find( aSymbol ); - if( itr != mSDL2_to_Win.end() ) + auto itr = mSDL_to_Win.find( aSymbol ); + if( itr != mSDL_to_Win.end() ) return itr->second; return aSymbol; diff --git a/indra/llwindow/llkeyboardsdl.h b/indra/llwindow/llkeyboardsdl.h index 3af098c4d1..2ebe647561 100644 --- a/indra/llwindow/llkeyboardsdl.h +++ b/indra/llwindow/llkeyboardsdl.h @@ -1,7 +1,8 @@ /** - * @author This module has many fathers, and it shows. + * @file llkeyboardsdl.h + * @brief Handler for assignable key bindings * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * @@ -23,38 +24,35 @@ * $/LicenseInfo$ */ -#ifndef LL_LLKEYBOARDSDL2_H -#define LL_LLKEYBOARDSDL2_H +#ifndef LL_LLKEYBOARDSDL_H +#define LL_LLKEYBOARDSDL_H #include "llkeyboard.h" -#if !defined(__i386__) && !defined(__x86_64__) -#define SDL_DISABLE_IMMINTRIN_H -#endif -#include "SDL2/SDL.h" +#include "SDL3/SDL.h" class LLKeyboardSDL : public LLKeyboard { public: LLKeyboardSDL(); - /*virtual*/ ~LLKeyboardSDL() {}; + ~LLKeyboardSDL() = default; - /*virtual*/ bool handleKeyUp(const U32 key, MASK mask); - /*virtual*/ bool handleKeyDown(const U32 key, MASK mask); - /*virtual*/ void resetMaskKeys(); - /*virtual*/ MASK currentMask(bool for_mouse_event); - /*virtual*/ void scanKeyboard(); + bool handleKeyUp(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) override; + bool handleKeyDown(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) override; + void resetMaskKeys() override; + MASK currentMask(bool for_mouse_event) override; + void scanKeyboard() override; protected: - MASK updateModifiers(const U32 mask); + MASK updateModifiers(const MASK mask); void setModifierKeyLevel( KEY key, bool new_state ); - bool translateNumpadKey( const U32 os_key, KEY *translated_key ); - U16 inverseTranslateNumpadKey(const KEY translated_key); + bool translateNumpadKey( const LLKeyboard::NATIVE_KEY_TYPE os_key, KEY *translated_key ); + LLKeyboard::NATIVE_KEY_TYPE inverseTranslateNumpadKey(const KEY translated_key); private: - std::map<U32, KEY> mTranslateNumpadMap; // special map for translating OS keys to numpad keys - std::map<KEY, U32> mInvTranslateNumpadMap; // inverse of the above + std::map<LLKeyboard::NATIVE_KEY_TYPE, KEY> mTranslateNumpadMap; // special map for translating OS keys to numpad keys + std::map<KEY, LLKeyboard::NATIVE_KEY_TYPE> mInvTranslateNumpadMap; // inverse of the above public: - static U32 mapSDL2toWin( U32 ); + static U32 mapSDLtoWin( U32 ); }; #endif diff --git a/indra/llwindow/llkeyboardwin32.cpp b/indra/llwindow/llkeyboardwin32.cpp index 8d6b8d9b93..4ef0b493a5 100644 --- a/indra/llwindow/llkeyboardwin32.cpp +++ b/indra/llwindow/llkeyboardwin32.cpp @@ -197,7 +197,7 @@ MASK LLKeyboardWin32::updateModifiers() // mask is ignored, except for extended flag -- we poll the modifier keys for the other flags -bool LLKeyboardWin32::handleKeyDown(const U16 key, MASK mask) +bool LLKeyboardWin32::handleKeyDown(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) { KEY translated_key; U32 translated_mask; @@ -214,7 +214,7 @@ bool LLKeyboardWin32::handleKeyDown(const U16 key, MASK mask) } // mask is ignored, except for extended flag -- we poll the modifier keys for the other flags -bool LLKeyboardWin32::handleKeyUp(const U16 key, MASK mask) +bool LLKeyboardWin32::handleKeyUp(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) { KEY translated_key; U32 translated_mask; diff --git a/indra/llwindow/llkeyboardwin32.h b/indra/llwindow/llkeyboardwin32.h index d0dfc5cfdd..fa223a2904 100644 --- a/indra/llwindow/llkeyboardwin32.h +++ b/indra/llwindow/llkeyboardwin32.h @@ -37,15 +37,16 @@ class LLKeyboardWin32 : public LLKeyboard { public: LLKeyboardWin32(); - /*virtual*/ ~LLKeyboardWin32() {}; + ~LLKeyboardWin32() = default; - /*virtual*/ bool handleKeyUp(const U16 key, MASK mask); - /*virtual*/ bool handleKeyDown(const U16 key, MASK mask); - /*virtual*/ void resetMaskKeys(); - /*virtual*/ MASK currentMask(bool for_mouse_event); - /*virtual*/ void scanKeyboard(); - bool translateExtendedKey(const U16 os_key, const MASK mask, KEY *translated_key); - U16 inverseTranslateExtendedKey(const KEY translated_key); + bool handleKeyUp(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) override; + bool handleKeyDown(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) override; + void resetMaskKeys() override; + MASK currentMask(bool for_mouse_event) override; + void scanKeyboard() override; + + bool translateExtendedKey(const U16 os_key, const MASK mask, KEY *translated_key); + U16 inverseTranslateExtendedKey(const KEY translated_key); protected: MASK updateModifiers(); diff --git a/indra/llwindow/llmousehandler.cpp b/indra/llwindow/llmousehandler.cpp index eeceab502a..959dde70b4 100644 --- a/indra/llwindow/llmousehandler.cpp +++ b/indra/llwindow/llmousehandler.cpp @@ -24,6 +24,8 @@ * $/LicenseInfo$ */ +#include "linden_common.h" + #include "llmousehandler.h" //virtual diff --git a/indra/llwindow/llopenglview-objc.h b/indra/llwindow/llopenglview-objc.h index 97f4125484..a2c55a2dd9 100644 --- a/indra/llwindow/llopenglview-objc.h +++ b/indra/llwindow/llopenglview-objc.h @@ -42,7 +42,11 @@ unsigned int mMarkedTextLength; bool mMarkedTextAllowed; bool mSimulatedRightClick; - bool mOldResize; + + NSOpenGLPixelFormat *pixelFormat; + NSOpenGLContext *glContext; + + bool mHDRDisplay; } - (id) initWithSamples:(NSUInteger)samples; - (id) initWithSamples:(NSUInteger)samples andVsync:(BOOL)vsync; @@ -50,8 +54,6 @@ - (void)commitCurrentPreedit; -- (void) setOldResize:(bool)oldresize; - // rebuildContext // Destroys and recreates a context with the view's internal format set via setPixelFormat; // Use this in event of needing to rebuild a context for whatever reason, without needing to assign a new pixel format. @@ -68,7 +70,6 @@ - (unsigned long) getVramSize; - (void) allowMarkedTextInput:(bool)allowed; -- (void) viewDidEndLiveResize; @end @@ -88,9 +89,6 @@ @interface LLNSWindow : NSWindow -- (NSPoint)convertToScreenFromLocalPoint:(NSPoint)point relativeToView:(NSView *)view; -- (NSPoint)flipPoint:(NSPoint)aPoint; - @end @interface NSScreen (PointConversion) @@ -100,16 +98,6 @@ */ + (NSScreen *)currentScreenForMouseLocation; -/* - Allows you to convert a point from global coordinates to the current screen coordinates. - */ -- (NSPoint)convertPointToScreenCoordinates:(NSPoint)aPoint; - -/* - Allows to flip the point coordinates, so y is 0 at the top instead of the bottom. x remains the same - */ -- (NSPoint)flipPoint:(NSPoint)aPoint; - @end #endif diff --git a/indra/llwindow/llopenglview-objc.mm b/indra/llwindow/llopenglview-objc.mm index 6177eb4873..547c921736 100644 --- a/indra/llwindow/llopenglview-objc.mm +++ b/indra/llwindow/llopenglview-objc.mm @@ -28,7 +28,10 @@ #import "llwindowmacosx-objc.h" #import "llappdelegate-objc.h" +#import <Carbon/Carbon.h> + extern BOOL gHiDPISupport; +extern BOOL gHDRDisplaySupport; #pragma mark local functions @@ -105,38 +108,24 @@ attributedStringInfo getSegments(NSAttributedString *str) return screen; } +@end -- (NSPoint)convertPointToScreenCoordinates:(NSPoint)aPoint -{ - float normalizedX = fabs(fabs(self.frame.origin.x) - fabs(aPoint.x)); - float normalizedY = aPoint.y - self.frame.origin.y; - - return NSMakePoint(normalizedX, normalizedY); -} +@implementation LLOpenGLView -- (NSPoint)flipPoint:(NSPoint)aPoint +- (NSOpenGLContext*) glContext { - return NSMakePoint(aPoint.x, self.frame.size.height - aPoint.y); + return glContext; } -@end - -@implementation LLOpenGLView - -// Force a high quality update after live resizing -- (void) viewDidEndLiveResize +- (NSOpenGLPixelFormat*) pixelFormat { - if (mOldResize) //Maint-3135 - { - NSSize size = [self frame].size; - callResize(size.width, size.height); - } + return pixelFormat; } - (unsigned long)getVramSize { CGLRendererInfoObj info = 0; - GLint vram_megabytes = 0; + GLint vram_megabytes = 0; int num_renderers = 0; CGLError the_err = CGLQueryRendererInfo (CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay), &info, &num_renderers); if(0 == the_err) @@ -154,51 +143,97 @@ attributedStringInfo getSegments(NSAttributedString *str) vram_megabytes = 256; } - return (unsigned long)vram_megabytes; // return value is in megabytes. + return (unsigned long)vram_megabytes; // return value is in megabytes. +} + +- (void)viewWillMoveToWindow:(nullable NSWindow *)newWindow +{ + if(mHDRDisplay) + { + self.wantsExtendedDynamicRangeOpenGLSurface = YES; + NSLog(@"Wants Extended Dynamic Range OpenGL", nil); + } + /* + else + { + self.wantsExtendedDynamicRangeOpenGLSurface = NO; + NSLog(@"Wants Standard Color Range OpenGL", nil); + } + */ } - (void)viewDidMoveToWindow { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowResized:) name:NSWindowDidResizeNotification - object:[self window]]; + // viewDidMoveToWindow is called both when the view is attached to a window + // and when it is detached (window == nil). + [NSObject cancelPreviousPerformRequestsWithTarget:self + selector:@selector(performDeferredResolutionUpdate) + object:nil]; + + if (![self window]) + { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + return; + } + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowResized:) name:NSWindowDidResizeNotification + object:[self window]]; [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowWillMiniaturize:) name:NSWindowWillMiniaturizeNotification - object:[self window]]; + selector:@selector(windowWillMiniaturize:) name:NSWindowWillMiniaturizeNotification + object:[self window]]; [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification - object:[self window]]; + selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification + object:[self window]]; [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification - object:[self window]]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowDidChangeScreen:) name:NSWindowDidChangeScreenNotification - object:[self window]]; + selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification + object:[self window]]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowDidChangeScreen:) name:NSWindowDidChangeScreenNotification + object:[self window]]; - NSRect wnd_rect = [[self window] frame]; - NSRect dev_rect = [self convertRectToBacking:wnd_rect]; - if (!NSEqualSizes(wnd_rect.size,dev_rect.size)) + if (windowCallbacksReady()) + { + NSRect wnd_rect = [[self window] frame]; + NSRect dev_rect = [self convertRectToBacking:wnd_rect]; + if (!NSEqualSizes(wnd_rect.size,dev_rect.size)) + { + // If window size actually changes, update size immediately + callResize(dev_rect.size.width, dev_rect.size.height); + } + else + { + // Defer size update to let the window settle on its final display + // before checkSettings() reads the live backing size. + callRequestResolutionUpdate(); + } + } + else { - callResize(dev_rect.size.width, dev_rect.size.height); + // Defer so that mCallbacks is the real callbacks object, + // not the null_callbacks used during LLWindowMacOSX construction. + // afterDelay will fire on the next loop iteration. + NSLog(@"viewDidMoveToWindow triggered, but callbacks not ready, postponing resolution update", nil); + [self performSelector:@selector(performDeferredResolutionUpdate) withObject:nil afterDelay:0]; } } -- (void)setOldResize:(bool)oldresize +- (void)performDeferredResolutionUpdate { - mOldResize = oldresize; + if ([self window]) + { + callRequestResolutionUpdate(); + } } - (void)windowResized:(NSNotification *)notification; { - if (!mOldResize) //Maint-3288 - { - NSSize dev_sz = gHiDPISupport ? [self convertSizeToBacking:[self frame].size] : [self frame].size; - callResize(dev_sz.width, dev_sz.height); - } + NSSize dev_sz = [self convertSizeToBacking:[self frame].size]; + callResize(dev_sz.width, dev_sz.height); } - (void)windowWillMiniaturize:(NSNotification *)notification; @@ -218,131 +253,183 @@ attributedStringInfo getSegments(NSAttributedString *str) -(void)windowDidChangeScreen:(NSNotification *)notification; { - callWindowDidChangeScreen(); + callWindowDidChangeScreen(); } - (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [super dealloc]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; } - (id) init { - return [self initWithFrame:[self bounds] withSamples:2 andVsync:TRUE]; + return [self initWithFrame:[self bounds] withSamples:0 andVsync:FALSE]; } - (id) initWithSamples:(NSUInteger)samples { - return [self initWithFrame:[self bounds] withSamples:samples andVsync:TRUE]; + return [self initWithFrame:[self bounds] withSamples:samples andVsync:FALSE]; } - (id) initWithSamples:(NSUInteger)samples andVsync:(BOOL)vsync { - return [self initWithFrame:[self bounds] withSamples:samples andVsync:vsync]; + return [self initWithFrame:[self bounds] withSamples:samples andVsync:vsync]; } +#if LL_DARWIN +// For setView and opengl deprecation +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + - (id) initWithFrame:(NSRect)frame withSamples:(NSUInteger)samples andVsync:(BOOL)vsync { - [self registerForDraggedTypes:[NSArray arrayWithObject:NSURLPboardType]]; - [self initWithFrame:frame]; + NSRect fixedFrame = NSMakeRect(0, 0, NSWidth(frame), NSHeight(frame)); + [self initWithFrame:fixedFrame]; + [self setWantsBestResolutionOpenGLSurface:gHiDPISupport]; + + // Initialize with a default "safe" pixel format that will work with versions dating back to OS X 10.6. + // Any specialized pixel formats, i.e. a core profile pixel format, should be initialized through rebuildContextWithFormat. + // 10.7 and 10.8 don't really care if we're defining a profile or not. If we don't explicitly request a core or legacy profile, it'll always assume a legacy profile (for compatibility reasons). - // Initialize with a default "safe" pixel format that will work with versions dating back to OS X 10.6. - // Any specialized pixel formats, i.e. a core profile pixel format, should be initialized through rebuildContextWithFormat. - // 10.7 and 10.8 don't really care if we're defining a profile or not. If we don't explicitly request a core or legacy profile, it'll always assume a legacy profile (for compatibility reasons). - NSOpenGLPixelFormatAttribute attrs[] = { + NSOpenGLPixelFormatAttribute SDRAttrs[] = { NSOpenGLPFANoRecovery, - NSOpenGLPFADoubleBuffer, - NSOpenGLPFAClosestPolicy, - NSOpenGLPFAAccelerated, - NSOpenGLPFASampleBuffers, 0, - NSOpenGLPFASamples, 0, - NSOpenGLPFAStencilSize, 8, - NSOpenGLPFADepthSize, 24, - NSOpenGLPFAAlphaSize, 8, - NSOpenGLPFAColorSize, 24, - NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core, - 0 + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAClosestPolicy, + NSOpenGLPFAAccelerated, + NSOpenGLPFADepthSize, 24, + NSOpenGLPFAColorSize, 24, + NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core, + 0 }; - NSOpenGLPixelFormat *pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] autorelease]; + NSOpenGLPixelFormatAttribute HDRAttrs[] = { + NSOpenGLPFANoRecovery, + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAClosestPolicy, + NSOpenGLPFAAccelerated, + NSOpenGLPFAColorFloat, + NSOpenGLPFADepthSize, 24, + NSOpenGLPFAColorSize, 48, + NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core, + 0 + }; - if (pixelFormat == nil) - { - NSLog(@"Failed to create pixel format!", nil); - return nil; - } + mHDRDisplay = NO; - NSOpenGLContext *glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; + if(gHDRDisplaySupport) + { + pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:HDRAttrs] autorelease]; + if (pixelFormat == nil) + { + NSLog(@"Failed to create pixel format for HDR Display!", nil); + pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:SDRAttrs] autorelease]; + } + else + { + NSLog(@"pixel format created successfully for HDR Display", nil); + mHDRDisplay = YES; + } + } + else + { + pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:SDRAttrs] autorelease]; + } - if (glContext == nil) - { - NSLog(@"Failed to create OpenGL context!", nil); - return nil; - } + if (pixelFormat == nil) + { + NSLog(@"Failed to create pixel format!", nil); + return nil; + } - [self setPixelFormat:pixelFormat]; + glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; - //for retina support - [self setWantsBestResolutionOpenGLSurface:gHiDPISupport]; + if (glContext == nil) + { + NSLog(@"Failed to create OpenGL context!", nil); + return nil; + } + + [self setPixelFormat:pixelFormat]; + + if(mHDRDisplay) + { + CGColorSpaceRef color_space = [self.window.colorSpace CGColorSpace]; + CGColorSpaceRef color_space_extended = CGColorSpaceCreateExtended(color_space); + NSColorSpace* extended_ns_color_space + = [[NSColorSpace alloc] initWithCGColorSpace:color_space_extended]; + + self.window.colorSpace = extended_ns_color_space; + CGColorSpaceRelease(color_space_extended); + + NSLog(@"Extended color space applied for HDR Display", nil); + } - [self setOpenGLContext:glContext]; + [self setOpenGLContext:glContext]; - [glContext setView:self]; + [glContext setView:self]; - [glContext makeCurrentContext]; + [glContext makeCurrentContext]; - if (vsync) - { - GLint value = 1; - [glContext setValues:&value forParameter:NSOpenGLCPSwapInterval]; - } else { - // supress this error after move to Xcode 7: - // error: null passed to a callee that requires a non-null argument [-Werror,-Wnonnull] - // Tried using ObjC 'nonnull' keyword as per SO article but didn't build - GLint swapInterval=0; - [glContext setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval]; - } + if (vsync) + { + GLint value = 1; + [glContext setValues:&value forParameter:NSOpenGLContextParameterSwapInterval]; + } else { + // supress this error after move to Xcode 7: + // error: null passed to a callee that requires a non-null argument [-Werror,-Wnonnull] + // Tried using ObjC 'nonnull' keyword as per SO article but didn't build + GLint swapInterval=0; + [glContext setValues:&swapInterval forParameter:NSOpenGLContextParameterSwapInterval]; + } - mOldResize = false; + GLint opacity = 1; + [glContext setValues:&opacity forParameter:NSOpenGLCPSurfaceOpacity]; - return self; + [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeURL]]; + + return self; } - (BOOL) rebuildContext { - return [self rebuildContextWithFormat:[self pixelFormat]]; + return [self rebuildContextWithFormat:[self pixelFormat]]; } - (BOOL) rebuildContextWithFormat:(NSOpenGLPixelFormat *)format { - NSOpenGLContext *ctx = [self openGLContext]; + NSOpenGLContext *ctx = [self openGLContext]; - [ctx clearDrawable]; - [ctx initWithFormat:format shareContext:nil]; + [ctx clearDrawable]; + [ctx initWithFormat:format shareContext:nil]; - if (ctx == nil) - { - NSLog(@"Failed to create OpenGL context!", nil); - return false; - } + if (ctx == nil) + { + NSLog(@"Failed to create OpenGL context!", nil); + return false; + } - [self setOpenGLContext:ctx]; - [ctx setView:self]; - [ctx makeCurrentContext]; - return true; + [self setOpenGLContext:ctx]; + [ctx setView:self]; + [ctx makeCurrentContext]; + return true; } +#if LL_DARWIN +#pragma clang diagnostic pop +#endif + - (CGLContextObj)getCGLContextObj { - NSOpenGLContext *ctx = [self openGLContext]; - return (CGLContextObj)[ctx CGLContextObj]; + NSOpenGLContext *ctx = [self openGLContext]; + return (CGLContextObj)[ctx CGLContextObj]; } - (CGLPixelFormatObj*)getCGLPixelFormatObj { - NSOpenGLPixelFormat *fmt = [self pixelFormat]; - return (CGLPixelFormatObj*)[fmt CGLPixelFormatObj]; + NSOpenGLPixelFormat *fmt = [self pixelFormat]; + return (CGLPixelFormatObj*)[fmt CGLPixelFormatObj]; } // Various events can be intercepted by our view, thus not reaching our window. @@ -350,18 +437,14 @@ attributedStringInfo getSegments(NSAttributedString *str) - (void) mouseDown:(NSEvent *)theEvent { - NSPoint mPoint = gHiDPISupport ? [self convertPointToBacking:[theEvent locationInWindow]] : [theEvent locationInWindow]; - mMousePos[0] = mPoint.x; - mMousePos[1] = mPoint.y; - // Apparently people still use this? - if ([theEvent modifierFlags] & NSCommandKeyMask && - !([theEvent modifierFlags] & NSControlKeyMask) && - !([theEvent modifierFlags] & NSShiftKeyMask) && - !([theEvent modifierFlags] & NSAlternateKeyMask) && - !([theEvent modifierFlags] & NSAlphaShiftKeyMask) && - !([theEvent modifierFlags] & NSFunctionKeyMask) && - !([theEvent modifierFlags] & NSHelpKeyMask)) + if ([theEvent modifierFlags] & NSEventModifierFlagCommand && + !([theEvent modifierFlags] & NSEventModifierFlagControl) && + !([theEvent modifierFlags] & NSEventModifierFlagShift) && + !([theEvent modifierFlags] & NSEventModifierFlagOption) && + !([theEvent modifierFlags] & NSEventModifierFlagCapsLock) && + !([theEvent modifierFlags] & NSEventModifierFlagFunction) && + !([theEvent modifierFlags] & NSEventModifierFlagHelp)) { callRightMouseDown(mMousePos, [theEvent modifierFlags]); mSimulatedRightClick = true; @@ -382,7 +465,7 @@ attributedStringInfo getSegments(NSAttributedString *str) callRightMouseUp(mMousePos, [theEvent modifierFlags]); mSimulatedRightClick = false; } else { - NSPoint mPoint = gHiDPISupport ? [self convertPointToBacking:[theEvent locationInWindow]] : [theEvent locationInWindow]; + NSPoint mPoint = [self convertPointToBacking:[theEvent locationInWindow]]; mMousePos[0] = mPoint.x; mMousePos[1] = mPoint.y; callLeftMouseUp(mMousePos, [theEvent modifierFlags]); @@ -391,35 +474,29 @@ attributedStringInfo getSegments(NSAttributedString *str) - (void) rightMouseDown:(NSEvent *)theEvent { - NSPoint mPoint = gHiDPISupport ? [self convertPointToBacking:[theEvent locationInWindow]] : [theEvent locationInWindow]; - mMousePos[0] = mPoint.x; - mMousePos[1] = mPoint.y; - callRightMouseDown(mMousePos, [theEvent modifierFlags]); + callRightMouseDown(mMousePos, [theEvent modifierFlags]); } - (void) rightMouseUp:(NSEvent *)theEvent { - NSPoint mPoint = gHiDPISupport ? [self convertPointToBacking:[theEvent locationInWindow]] : [theEvent locationInWindow]; - mMousePos[0] = mPoint.x; - mMousePos[1] = mPoint.y; - callRightMouseUp(mMousePos, [theEvent modifierFlags]); + callRightMouseUp(mMousePos, [theEvent modifierFlags]); } - (void)mouseMoved:(NSEvent *)theEvent { - NSPoint dev_delta = gHiDPISupport ? [self convertPointToBacking:NSMakePoint([theEvent deltaX], [theEvent deltaY])] : NSMakePoint([theEvent deltaX], [theEvent deltaY]); + NSPoint dev_delta = [self convertPointToBacking:NSMakePoint([theEvent deltaX], [theEvent deltaY])]; - float mouseDeltas[] = { - float(dev_delta.x), - float(dev_delta.y) - }; + float mouseDeltas[] = { + float(dev_delta.x), + float(dev_delta.y) + }; - callDeltaUpdate(mouseDeltas, 0); + callDeltaUpdate(mouseDeltas, 0); - NSPoint mPoint = gHiDPISupport ? [self convertPointToBacking:[theEvent locationInWindow]] : [theEvent locationInWindow]; - mMousePos[0] = mPoint.x; - mMousePos[1] = mPoint.y; - callMouseMoved(mMousePos, 0); + NSPoint mPoint = [self convertPointToBacking:[theEvent locationInWindow]]; + mMousePos[0] = mPoint.x; + mMousePos[1] = mPoint.y; + callMouseMoved(mMousePos, 0); } // NSWindow doesn't trigger mouseMoved when the mouse is being clicked and dragged. @@ -427,66 +504,60 @@ attributedStringInfo getSegments(NSAttributedString *str) - (void) mouseDragged:(NSEvent *)theEvent { - // Trust the deltas supplied by NSEvent. - // The old CoreGraphics APIs we previously relied on are now flagged as obsolete. - // NSEvent isn't obsolete, and provides us with the correct deltas. + // Trust the deltas supplied by NSEvent. + // The old CoreGraphics APIs we previously relied on are now flagged as obsolete. + // NSEvent isn't obsolete, and provides us with the correct deltas. - NSPoint dev_delta = gHiDPISupport ? [self convertPointToBacking:NSMakePoint([theEvent deltaX], [theEvent deltaY])] : NSMakePoint([theEvent deltaX], [theEvent deltaY]); + NSPoint dev_delta = [self convertPointToBacking:NSMakePoint([theEvent deltaX], [theEvent deltaY])]; - float mouseDeltas[] = { - float(dev_delta.x), - float(dev_delta.y) - }; + float mouseDeltas[] = { + float(dev_delta.x), + float(dev_delta.y) + }; - callDeltaUpdate(mouseDeltas, 0); + callDeltaUpdate(mouseDeltas, 0); - NSPoint mPoint = gHiDPISupport ? [self convertPointToBacking:[theEvent locationInWindow]] : [theEvent locationInWindow]; - mMousePos[0] = mPoint.x; - mMousePos[1] = mPoint.y; - callMouseDragged(mMousePos, 0); + NSPoint mPoint = [self convertPointToBacking:[theEvent locationInWindow]]; + mMousePos[0] = mPoint.x; + mMousePos[1] = mPoint.y; + callMouseDragged(mMousePos, 0); } - (void) otherMouseDown:(NSEvent *)theEvent { - NSPoint mPoint = gHiDPISupport ? [self convertPointToBacking:[theEvent locationInWindow]] : [theEvent locationInWindow]; - mMousePos[0] = mPoint.x; - mMousePos[1] = mPoint.y; callOtherMouseDown(mMousePos, [theEvent modifierFlags], [theEvent buttonNumber]); } - (void) otherMouseUp:(NSEvent *)theEvent { - NSPoint mPoint = gHiDPISupport ? [self convertPointToBacking:[theEvent locationInWindow]] : [theEvent locationInWindow]; - mMousePos[0] = mPoint.x; - mMousePos[1] = mPoint.y; callOtherMouseUp(mMousePos, [theEvent modifierFlags], [theEvent buttonNumber]); } - (void) rightMouseDragged:(NSEvent *)theEvent { - [self mouseDragged:theEvent]; + [self mouseDragged:theEvent]; } - (void) otherMouseDragged:(NSEvent *)theEvent { - [self mouseDragged:theEvent]; + [self mouseDragged:theEvent]; } - (void) scrollWheel:(NSEvent *)theEvent { - callScrollMoved(-[theEvent deltaX], -[theEvent deltaY]); + callScrollMoved(-[theEvent deltaX], -[theEvent deltaY]); } - (void) mouseExited:(NSEvent *)theEvent { - callMouseExit(); + callMouseExit(); } - (void) keyUp:(NSEvent *)theEvent { NativeKeyEventData eventData = extractKeyDataFromKeyEvent(theEvent); eventData.mKeyEvent = NativeKeyEventData::KEYUP; - callKeyUp(&eventData, [theEvent keyCode], [theEvent modifierFlags]); + callKeyUp(&eventData, [theEvent keyCode], [theEvent modifierFlags]); } - (void) keyDown:(NSEvent *)theEvent @@ -500,7 +571,7 @@ attributedStringInfo getSegments(NSAttributedString *str) // Because flagsChange event handler misses event when other window is activated, // e.g. OS Window for upload something or Input Window... // mModifiers instance variable is for insertText: or insertText:replacementRange: (by Pell Smit) - mModifiers = [theEvent modifierFlags]; + mModifiers = [theEvent modifierFlags]; NSString *str_no_modifiers = [theEvent charactersIgnoringModifiers]; unichar ch = 0; if (str_no_modifiers.length) @@ -511,7 +582,7 @@ attributedStringInfo getSegments(NSAttributedString *str) if (acceptsText && !mMarkedTextAllowed && - !(mModifiers & (NSControlKeyMask | NSCommandKeyMask)) && // commands don't invoke InputWindow + !(mModifiers & (NSEventModifierFlagControl | NSEventModifierFlagCommand)) && // commands don't invoke InputWindow ![(LLAppDelegate*)[NSApp delegate] romanScript] && ch > ' ' && ch != NSDeleteCharacter && @@ -528,20 +599,20 @@ attributedStringInfo getSegments(NSAttributedString *str) { NativeKeyEventData eventData = extractKeyDataFromModifierEvent(theEvent); - mModifiers = [theEvent modifierFlags]; - callModifier([theEvent modifierFlags]); + mModifiers = [theEvent modifierFlags]; + callModifier([theEvent modifierFlags]); NSInteger mask = 0; switch([theEvent keyCode]) { - case 56: - mask = NSShiftKeyMask; + case kVK_Shift: + mask = NSEventModifierFlagShift; break; - case 58: - mask = NSAlternateKeyMask; + case kVK_Option: + mask = NSEventModifierFlagOption; break; - case 59: - mask = NSControlKeyMask; + case kVK_Control: + mask = NSEventModifierFlagControl; break; default: return; @@ -570,69 +641,69 @@ attributedStringInfo getSegments(NSAttributedString *str) - (BOOL) acceptsFirstResponder { - return YES; + return YES; } - (NSDragOperation) draggingEntered:(id<NSDraggingInfo>)sender { - NSPasteboard *pboard; + NSPasteboard *pboard; NSDragOperation sourceDragMask; - sourceDragMask = [sender draggingSourceOperationMask]; + sourceDragMask = [sender draggingSourceOperationMask]; - pboard = [sender draggingPasteboard]; + pboard = [sender draggingPasteboard]; - if ([[pboard types] containsObject:NSURLPboardType]) - { - if (sourceDragMask & NSDragOperationLink) { - NSURL *fileUrl = [[pboard readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]] options:[NSDictionary dictionary]] objectAtIndex:0]; - mLastDraggedUrl = [[fileUrl absoluteString] UTF8String]; + if ([[pboard types] containsObject:NSPasteboardTypeURL]) + { + if (sourceDragMask & NSDragOperationLink) { + NSURL *fileUrl = [[pboard readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]] options:[NSDictionary dictionary]] objectAtIndex:0]; + mLastDraggedUrl = [[fileUrl absoluteString] UTF8String]; return NSDragOperationLink; } - } - return NSDragOperationNone; + } + return NSDragOperationNone; } - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender { - callHandleDragUpdated(mLastDraggedUrl); + callHandleDragUpdated(mLastDraggedUrl); - return NSDragOperationLink; + return NSDragOperationLink; } - (void) draggingExited:(id<NSDraggingInfo>)sender { - callHandleDragExited(mLastDraggedUrl); + callHandleDragExited(mLastDraggedUrl); } - (BOOL)prepareForDragOperation:(id < NSDraggingInfo >)sender { - return YES; + return YES; } - (BOOL) performDragOperation:(id<NSDraggingInfo>)sender { - callHandleDragDropped(mLastDraggedUrl); - return true; + callHandleDragDropped(mLastDraggedUrl); + return true; } - (BOOL)hasMarkedText { - return mHasMarkedText; + return mHasMarkedText; } - (NSRange)markedRange { - int range[2]; - getPreeditMarkedRange(&range[0], &range[1]); - return NSMakeRange(range[0], range[1]); + int range[2]; + getPreeditMarkedRange(&range[0], &range[1]); + return NSMakeRange(range[0], range[1]); } - (NSRange)selectedRange { - int range[2]; - getPreeditSelectionRange(&range[0], &range[1]); - return NSMakeRange(range[0], range[1]); + int range[2]; + getPreeditSelectionRange(&range[0], &range[1]); + return NSMakeRange(range[0], range[1]); } - (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange @@ -657,7 +728,7 @@ attributedStringInfo getSegments(NSAttributedString *str) }; int string_length = [aString length]; - unichar text[string_length]; + unichar *text = new unichar[string_length]; attributedStringInfo segments; // I used 'respondsToSelector:@selector(string)' // to judge aString is an attributed string or not. @@ -685,6 +756,8 @@ attributedStringInfo getSegments(NSAttributedString *str) // we must clear the marked text when aString is null. [self unmarkText]; } + + delete [] text; } else { if (mHasMarkedText) { @@ -693,6 +766,12 @@ attributedStringInfo getSegments(NSAttributedString *str) } } +#if LL_DARWIN +// For commitEditing deprecation +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + - (void)commitCurrentPreedit { if (mHasMarkedText) @@ -704,23 +783,27 @@ attributedStringInfo getSegments(NSAttributedString *str) } } +#if LL_DARWIN +#pragma clang diagnostic pop +#endif + - (void)unmarkText { - [[self inputContext] discardMarkedText]; - resetPreedit(); - mHasMarkedText = FALSE; + [[self inputContext] discardMarkedText]; + resetPreedit(); + mHasMarkedText = FALSE; } // We don't support attributed strings. - (NSArray *)validAttributesForMarkedText { - return [NSArray array]; + return [NSArray array]; } // See above. - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { - return nil; + return nil; } - (void)insertText:(id)insertString @@ -733,9 +816,9 @@ attributedStringInfo getSegments(NSAttributedString *str) - (void)insertText:(id)aString replacementRange:(NSRange)replacementRange { - // SL-19801 Special workaround for system emoji picker - if ([aString length] == 2) - { + // SL-19801 Special workaround for system emoji picker + if ([aString length] == 2) + { @try { uint32_t b0 = [aString characterAtIndex:0]; @@ -753,7 +836,7 @@ attributedStringInfo getSegments(NSAttributedString *str) NSLog(@"Encountered an unsupported attributed character. Exception: %@ String: %@", e.name, aString); return; } - } + } @try { @@ -783,39 +866,39 @@ attributedStringInfo getSegments(NSAttributedString *str) - (void) insertNewline:(id)sender { - if (!(mModifiers & NSCommandKeyMask) && - !(mModifiers & NSShiftKeyMask) && - !(mModifiers & NSAlternateKeyMask)) - { - callUnicodeCallback(13, 0); - } else { - callUnicodeCallback(13, mModifiers); - } + if (!(mModifiers & NSEventModifierFlagCommand) && + !(mModifiers & NSEventModifierFlagShift) && + !(mModifiers & NSEventModifierFlagOption)) + { + callUnicodeCallback(13, 0); + } else { + callUnicodeCallback(13, mModifiers); + } } - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint { - return NSNotFound; + return NSNotFound; } - (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { - float pos[4] = {0, 0, 0, 0}; - getPreeditLocation(pos, mMarkedTextLength); - return NSMakeRect(pos[0], pos[1], pos[2], pos[3]); + float pos[4] = {0, 0, 0, 0}; + getPreeditLocation(pos, mMarkedTextLength); + return NSMakeRect(pos[0], pos[1], pos[2], pos[3]); } - (void)doCommandBySelector:(SEL)aSelector { - if (aSelector == @selector(insertNewline:)) - { - [self insertNewline:self]; - } + if (aSelector == @selector(insertNewline:)) + { + [self insertNewline:self]; + } } - (BOOL)drawsVerticallyForCharacterAtIndex:(NSUInteger)charIndex { - return NO; + return NO; } - (void) allowMarkedTextInput:(bool)allowed @@ -850,7 +933,7 @@ attributedStringInfo getSegments(NSAttributedString *str) - (void) setGLView:(LLOpenGLView *)view { - glview = view; + glview = view; } - (void)keyDown:(NSEvent *)theEvent @@ -901,45 +984,24 @@ attributedStringInfo getSegments(NSAttributedString *str) - (id) init { - return self; -} - -- (NSPoint)convertToScreenFromLocalPoint:(NSPoint)point relativeToView:(NSView *)view -{ - NSScreen *currentScreen = [NSScreen currentScreenForMouseLocation]; - if(currentScreen) - { - NSPoint windowPoint = [view convertPoint:point toView:nil]; - NSPoint screenPoint = [[view window] convertBaseToScreen:windowPoint]; - NSPoint flippedScreenPoint = [currentScreen flipPoint:screenPoint]; - flippedScreenPoint.y += [currentScreen frame].origin.y; - - return flippedScreenPoint; - } - - return NSZeroPoint; -} - -- (NSPoint)flipPoint:(NSPoint)aPoint -{ - return NSMakePoint(aPoint.x, self.frame.size.height - aPoint.y); + return self; } - (BOOL) becomeFirstResponder { - callFocus(); - return true; + callFocus(); + return true; } - (BOOL) resignFirstResponder { - callFocusLost(); - return true; + callFocusLost(); + return true; } - (void) close { - callQuitHandler(); + callQuitHandler(); } @end diff --git a/indra/llwindow/llsdl.cpp b/indra/llwindow/llsdl.cpp new file mode 100644 index 0000000000..bc38120386 --- /dev/null +++ b/indra/llwindow/llsdl.cpp @@ -0,0 +1,149 @@ +/** + * @file llsdl.cpp + * @brief SDL initialization + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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$ + */ + +#include <initializer_list> +#include <list> + +#include "SDL3/SDL.h" + +#ifndef LL_SDL_APP +#define SDL_MAIN_HANDLED 1 +#include "SDL3/SDL_main.h" +#endif + +#include "llerror.h" +#include "llwindow.h" + +bool gSDLMainHandled = false; + +void sdl_logger(void *userdata, int category, SDL_LogPriority priority, const char *message) +{ + switch (priority) + { + case SDL_LOG_PRIORITY_TRACE: + case SDL_LOG_PRIORITY_VERBOSE: + case SDL_LOG_PRIORITY_DEBUG: + LL_DEBUGS("SDL") << "log='" << message << "'" << LL_ENDL; + break; + case SDL_LOG_PRIORITY_INFO: + LL_INFOS("SDL") << "log='" << message << "'" << LL_ENDL; + break; + case SDL_LOG_PRIORITY_WARN: + case SDL_LOG_PRIORITY_ERROR: + case SDL_LOG_PRIORITY_CRITICAL: + LL_WARNS("SDL") << "log='" << message << "'" << LL_ENDL; + break; + case SDL_LOG_PRIORITY_INVALID: + default: + break; + } +} + +void init_sdl(const std::string& app_name) +{ +#ifndef LL_SDL_APP + if (!gSDLMainHandled) + { + SDL_SetMainReady(); + } +#endif + + SDL_SetLogOutputFunction(&sdl_logger, nullptr); + + const int c_sdl_version = SDL_VERSION; + LL_INFOS() << "Compiled against SDL " + << SDL_VERSIONNUM_MAJOR(c_sdl_version) << "." + << SDL_VERSIONNUM_MINOR(c_sdl_version) << "." + << SDL_VERSIONNUM_MICRO(c_sdl_version) << LL_ENDL; + const int r_sdl_version = SDL_GetVersion(); + LL_INFOS() << "Running with SDL " + << SDL_VERSIONNUM_MAJOR(r_sdl_version) << "." + << SDL_VERSIONNUM_MINOR(r_sdl_version) << "." + << SDL_VERSIONNUM_MICRO(r_sdl_version) << LL_ENDL; + +#if LL_WINDOWS && defined(LL_SDL_WINDOW) + Uint32 style = 0; +#if defined(CS_BYTEALIGNCLIENT) && defined(CS_OWNDC) + style = (CS_BYTEALIGNCLIENT | CS_OWNDC); +#endif + SDL_RegisterApp(app_name.c_str(), style, nullptr); +#endif + +#if LL_SDL_WINDOW + // For linux we SDL_INIT_VIDEO and _AUDIO + std::initializer_list<std::tuple< char const*, char const * > > hintList = + { + {SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR,"0"}, + {SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH,"1"}, + {SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE,"0"}, + {SDL_HINT_MOUSE_RELATIVE_WARP_MOTION,"1"}, + {SDL_HINT_KEYCODE_OPTIONS,"french_numbers,latin_letters"} + }; + + for (auto hint: hintList) + { + SDL_SetHint(std::get<0>(hint), std::get<1>(hint)); + } + + std::initializer_list<std::tuple<uint32_t, char const*, bool>> initList= + { + {SDL_INIT_VIDEO,"SDL_INIT_VIDEO", true}, + {SDL_INIT_JOYSTICK,"SDL_INIT_JOYSTICK", true}, + {SDL_INIT_GAMEPAD,"SDL_INIT_GAMEPAD", true}, + }; +#else + // For non-linux platforms we still SDL_INIT_VIDEO because it is a pre-requisite + // for SDL_INIT_GAMECONTROLLER. + std::initializer_list<std::tuple<uint32_t, char const*, bool>> initList= + { + {SDL_INIT_VIDEO,"SDL_INIT_VIDEO", false}, + }; +#endif // LL_LINUX + // We SDL_INIT_GAMECONTROLLER later in the startup process to make it + // more likely we'll catch initial SDL_CONTROLLERDEVICEADDED events. + + for (auto subSystem : initList) + { + if (!SDL_InitSubSystem(std::get<0>(subSystem))) + { + LL_WARNS() << "SDL_InitSubSystem for " << std::get<1>(subSystem) << " failed " << SDL_GetError() << LL_ENDL; + + if (std::get<2>(subSystem)) + { + OSMessageBox("SDL_Init() failure", "error", OSMB_OK); + return; + } + } + } +} + +void quit_sdl() +{ +#if LL_WINDOWS && defined(LL_SDL_WINDOW) + SDL_UnregisterApp(); +#endif + SDL_Quit(); +} diff --git a/indra/llwindow/llsdl.h b/indra/llwindow/llsdl.h new file mode 100644 index 0000000000..6c9c9eb07c --- /dev/null +++ b/indra/llwindow/llsdl.h @@ -0,0 +1,34 @@ +/** + * @file llsdl.h + * @brief SDL initialization + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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 + +#include "llpreprocessor.h" + +extern bool gSDLMainHandled; + +void init_sdl(const std::string& app_name); +void quit_sdl(); diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp index 4f3cc69c75..b6177abfc7 100644 --- a/indra/llwindow/llwindow.cpp +++ b/indra/llwindow/llwindow.cpp @@ -29,7 +29,7 @@ #if LL_MESA_HEADLESS #include "llwindowmesaheadless.h" -#elif LL_SDL +#elif LL_SDL_WINDOW #include "llwindowsdl.h" #elif LL_WINDOWS #include "llwindowwin32.h" @@ -39,6 +39,9 @@ #include "llerror.h" #include "llkeyboard.h" +#if LL_SDL_WINDOW && !defined(LL_MESA_HEADLESS) +#include "llsdl.h" +#endif #include "llwindowcallbacks.h" @@ -74,14 +77,15 @@ S32 OSMessageBox(const std::string& text, const std::string& caption, U32 type) LL_WARNS() << "OSMessageBox: " << text << LL_ENDL; #if LL_MESA_HEADLESS // !!! *FIX: (?) return OSBTN_OK; +#elif LL_SDL_WINDOW + result = OSMessageBoxSDL(text, caption, type); #elif LL_WINDOWS result = OSMessageBoxWin32(text, caption, type); -#elif LL_DARWIN && !LL_SDL +#elif LL_DARWIN result = OSMessageBoxMacOSX(text, caption, type); -#elif LL_SDL - result = OSMessageBoxSDL(text, caption, type); #else -#error("OSMessageBox not implemented for this platform!") + LL_WARNS() << "OSMessageBox not implemented for this platform!" << LL_ENDL; + return OSBTN_OK; #endif if (was_visible) @@ -103,7 +107,6 @@ LLWindow::LLWindow(LLWindowCallbacks* callbacks, bool fullscreen, U32 flags) mFullscreen(fullscreen), mFullscreenWidth(0), mFullscreenHeight(0), - mFullscreenBits(0), mFullscreenRefresh(0), mSupportedResolutions(NULL), mNumSupportedResolutions(0), @@ -183,12 +186,6 @@ bool LLWindow::dialogColorPicker(F32 *r, F32 *g, F32 *b) return false; } -void *LLWindow::getMediaWindow() -{ - // Default to returning the platform window. - return getPlatformWindow(); -} - bool LLWindow::setSize(LLCoordScreen size) { if (!getMaximized()) @@ -259,23 +256,39 @@ bool LLWindow::copyTextToPrimary(const LLWString &src) // static std::vector<std::string> LLWindow::getDynamicFallbackFontList() { -#if LL_WINDOWS +#if LL_SDL_WINDOW && !LL_MESA_HEADLESS + return LLWindowSDL::getDynamicFallbackFontList(); +#elif LL_WINDOWS return LLWindowWin32::getDynamicFallbackFontList(); -#elif LL_DARWIN && !LL_SDL +#elif LL_DARWIN return LLWindowMacOSX::getDynamicFallbackFontList(); -#elif LL_SDL - return LLWindowSDL::getDynamicFallbackFontList(); #else return std::vector<std::string>(); #endif } // static +LLFontFallbackMatch LLWindow::findFallbackFontForChar(llwchar wch) +{ +#if LL_SDL_WINDOW && !LL_MESA_HEADLESS + return LLWindowSDL::findFallbackFontForChar(wch); +#elif LL_WINDOWS + return LLWindowWin32::findFallbackFontForChar(wch); +#elif LL_DARWIN + return LLWindowMacOSX::findFallbackFontForChar(wch); +#else + return LLFontFallbackMatch(); +#endif +} + +// static std::vector<std::string> LLWindow::getDisplaysResolutionList() { -#if LL_WINDOWS +#if LL_SDL_WINDOW && !LL_MESA_HEADLESS + return LLWindowSDL::getDisplaysResolutionList(); +#elif LL_WINDOWS return LLWindowWin32::getDisplaysResolutionList(); -#elif LL_DARWIN && !LL_SDL +#elif LL_DARWIN return LLWindowMacOSX::getDisplaysResolutionList(); #else return std::vector<std::string>(); @@ -342,14 +355,17 @@ bool LLSplashScreen::isVisible() // static LLSplashScreen *LLSplashScreen::create() { -#if LL_MESA_HEADLESS || LL_SDL // !!! *FIX: (?) - return 0; +#if LL_MESA_HEADLESS + return nullptr; +#elif LL_SDL_WINDOW + return new LLSplashScreenSDL; #elif LL_WINDOWS return new LLSplashScreenWin32; -#elif LL_DARWIN && !LL_SDL +#elif LL_DARWIN return new LLSplashScreenMacOSX; #else -#error("LLSplashScreen not implemented on this platform!") + LL_WARNS() << ("LLSplashScreen not implemented on this platform!") << LL_ENDL; + return nullptr; #endif } @@ -359,9 +375,11 @@ void LLSplashScreen::show() { if (!gSplashScreenp) { -#if LL_WINDOWS && !LL_MESA_HEADLESS +#if LL_SDL_WINDOW && !LL_MESA_HEADLESS + gSplashScreenp = new LLSplashScreenSDL; +#elif LL_WINDOWS && !LL_MESA_HEADLESS gSplashScreenp = new LLSplashScreenWin32; -#elif LL_DARWIN && !LL_SDL +#elif LL_DARWIN gSplashScreenp = new LLSplashScreenMacOSX; #endif if (gSplashScreenp) @@ -411,7 +429,11 @@ LLWindow* LLWindowManager::createWindow( U32 max_cores, F32 max_gl_version) { - LLWindow* new_window = nullptr; + LLWindow* new_window; + +#if LL_SDL_WINDOW && !defined(LL_MESA_HEADLESS) + init_sdl(name); +#endif if (use_gl) { @@ -419,7 +441,7 @@ LLWindow* LLWindowManager::createWindow( new_window = new LLWindowMesaHeadless(callbacks, title, name, x, y, width, height, flags, fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth); -#elif LL_SDL +#elif LL_SDL_WINDOW new_window = new LLWindowSDL(callbacks, title, name, x, y, width, height, flags, fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth, fsaa_samples); @@ -462,6 +484,9 @@ bool LLWindowManager::destroyWindow(LLWindow* window) window->close(); sWindowList.erase(window); +#if LL_SDL_WINDOW && !defined(LL_MESA_HEADLESS) + quit_sdl(); +#endif delete window; diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h index 5e06e665f3..3f06bf1a50 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -38,6 +38,13 @@ class LLSplashScreen; class LLPreeditor; class LLWindowCallbacks; +// Result of an OS font-fallback query; empty mPath means no font was found. +struct LLFontFallbackMatch +{ + std::string mPath; + S32 mFaceIndex = 0; +}; + // Refer to llwindow_test in test/common/llwindow for usage example class LLWindow : public LLInstanceTracker<LLWindow> @@ -71,7 +78,12 @@ public: virtual void restore() = 0; bool getFullscreen() { return mFullscreen; }; virtual bool getPosition(LLCoordScreen *position) = 0; + + // Returns the platform-defined window size in physical (backing) pixels. + // On macOS: backing content view size (excludes title bar). + // On Windows: outer window frame size (includes title bar and borders). virtual bool getSize(LLCoordScreen *size) = 0; + virtual bool getSize(LLCoordWindow *size) = 0; virtual bool setPosition(LLCoordScreen position) = 0; bool setSize(LLCoordScreen size); @@ -92,9 +104,10 @@ public: virtual bool setCursorPosition(LLCoordWindow position) = 0; virtual bool getCursorPosition(LLCoordWindow *position) = 0; -#if LL_WINDOWS +#if LL_WINDOWS && !LL_SDL_WINDOW virtual bool getCursorDelta(LLCoordCommon* delta) = 0; #endif + virtual bool isWrapMouse() const = 0; virtual void showCursor() = 0; virtual void hideCursor() = 0; virtual bool isCursorHidden() = 0; @@ -147,7 +160,6 @@ public: virtual void swapBuffers() = 0; virtual void bringToFront() = 0; virtual void focusClient() { }; // this may not have meaning or be required on other platforms, therefore, it's not abstract - virtual void setOldResize(bool oldresize) { }; // handy coordinate space conversion routines // NB: screen to window and vice verse won't work on width/height coordinate pairs, // as the conversion must take into account left AND right border widths, etc. @@ -174,9 +186,6 @@ public: // return a platform-specific window reference (HWND on Windows, WindowRef on the Mac, Gtk window on Linux) virtual void *getPlatformWindow() = 0; -// return the platform-specific window reference we use to initialize llmozlib (HWND on Windows, WindowRef on the Mac, Gtk window on Linux) - virtual void *getMediaWindow(); - // control platform's Language Text Input mechanisms. virtual void allowLanguageTextInput(LLPreeditor *preeditor, bool b) {} virtual void setLanguageTextInput( const LLCoordGL & pos ) {}; @@ -186,6 +195,9 @@ public: static std::vector<std::string> getDynamicFallbackFontList(); + // Ask the OS for a font file covering the given codepoint (lazy fallback). + static LLFontFallbackMatch findFallbackFontForChar(llwchar wch); + // Provide native key event data virtual LLSD getNativeKeyData() { return LLSD::emptyMap(); } @@ -205,6 +217,8 @@ public: }; virtual S32 getRefreshRate() { return mRefreshRate; } + + virtual void initWatchdog() {} // windows runs window as a thread and it needs a watchdog protected: LLWindow(LLWindowCallbacks* callbacks, bool fullscreen, U32 flags); virtual ~LLWindow(); @@ -223,7 +237,6 @@ protected: bool mFullscreen; S32 mFullscreenWidth; S32 mFullscreenHeight; - S32 mFullscreenBits; S32 mFullscreenRefresh; LLWindowResolution* mSupportedResolutions; S32 mNumSupportedResolutions; diff --git a/indra/llwindow/llwindowcallbacks.cpp b/indra/llwindow/llwindowcallbacks.cpp index 195f68e08b..b16d0ffce8 100644 --- a/indra/llwindow/llwindowcallbacks.cpp +++ b/indra/llwindow/llwindowcallbacks.cpp @@ -68,7 +68,25 @@ void LLWindowCallbacks::handleMouseLeave(LLWindow *window) return; } -bool LLWindowCallbacks::handleCloseRequest(LLWindow *window) +void LLWindowCallbacks::handlePreCloseRequest() +{ +} + +void LLWindowCallbacks::handleCloseRequestCanceled() +{ +} + +void LLWindowCallbacks::handleSuspendRequest() +{ +} + +bool LLWindowCallbacks::handleCloseRequest(LLWindow *window, bool from_user) +{ + //allow the window to close + return true; +} + +bool LLWindowCallbacks::handleSessionExit(LLWindow* window) { //allow the window to close return true; @@ -138,6 +156,10 @@ void LLWindowCallbacks::handleResize(LLWindow *window, const S32 width, const S3 { } +void LLWindowCallbacks::handleRequestResolutionUpdate(LLWindow* window) +{ +} + void LLWindowCallbacks::handleFocus(LLWindow *window) { LL_WARNS("COCOA") << "Called handleFocus proto" << LL_ENDL; @@ -184,7 +206,7 @@ bool LLWindowCallbacks::handleTimerEvent(LLWindow *window) return false; } -bool LLWindowCallbacks::handleDeviceChange(LLWindow *window) +bool LLWindowCallbacks::handleDeviceChange(LLWindow *window, const std::string& change_type) { return false; } diff --git a/indra/llwindow/llwindowcallbacks.h b/indra/llwindow/llwindowcallbacks.h index d812f93524..1fcf2974b5 100644 --- a/indra/llwindow/llwindowcallbacks.h +++ b/indra/llwindow/llwindowcallbacks.h @@ -41,8 +41,13 @@ public: virtual bool handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask); virtual bool handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask); virtual void handleMouseLeave(LLWindow *window); + // Called before close request is processed (ex: to create marker file in case OS is about to kill app). + virtual void handlePreCloseRequest(); + virtual void handleCloseRequestCanceled(); + virtual void handleSuspendRequest(); // return true to allow window to close, which will then cause handleQuit to be called - virtual bool handleCloseRequest(LLWindow *window); + virtual bool handleCloseRequest(LLWindow *window, bool from_user); + virtual bool handleSessionExit(LLWindow* window); // window is about to be destroyed, clean up your business virtual void handleQuit(LLWindow *window); virtual bool handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask); @@ -58,6 +63,7 @@ public: virtual void handleScrollWheel(LLWindow *window, S32 clicks); virtual void handleScrollHWheel(LLWindow *window, S32 clicks); virtual void handleResize(LLWindow *window, S32 width, S32 height); + virtual void handleRequestResolutionUpdate(LLWindow* window); virtual void handleFocus(LLWindow *window); virtual void handleFocusLost(LLWindow *window); virtual void handleMenuSelect(LLWindow *window, S32 menu_item); @@ -67,7 +73,7 @@ public: virtual void handleWindowUnblock(LLWindow *window); // window coming back after taking over CPU for a while virtual void handleDataCopy(LLWindow *window, S32 data_type, void *data); virtual bool handleTimerEvent(LLWindow *window); - virtual bool handleDeviceChange(LLWindow *window); + virtual bool handleDeviceChange(LLWindow *window, const std::string& change_type); virtual bool handleDPIChanged(LLWindow *window, F32 ui_scale_factor, S32 window_width, S32 window_height); virtual bool handleDisplayChanged(); virtual bool handleWindowDidChangeScreen(LLWindow *window); diff --git a/indra/llwindow/llwindowheadless.h b/indra/llwindow/llwindowheadless.h index 5696b69a59..8b7b0ef55c 100644 --- a/indra/llwindow/llwindowheadless.h +++ b/indra/llwindow/llwindowheadless.h @@ -60,9 +60,10 @@ public: /*virtual*/ void toggleVSync(bool enable_vsync) override { } /*virtual*/ bool setCursorPosition(LLCoordWindow position) override {return false;} /*virtual*/ bool getCursorPosition(LLCoordWindow *position) override {return false;} -#if LL_WINDOWS +#if LL_WINDOWS && !LL_SDL_WINDOW /*virtual*/ bool getCursorDelta(LLCoordCommon* delta) override { return false; } #endif + /*virtual*/ bool isWrapMouse() const override { return true; } /*virtual*/ void showCursor() override {} /*virtual*/ void hideCursor() override {} /*virtual*/ void showCursorFromMouseMove() override {} diff --git a/indra/llwindow/llwindowmacosx-objc.h b/indra/llwindow/llwindowmacosx-objc.h index d9d8bfce1f..a9698bc0e1 100644 --- a/indra/llwindow/llwindowmacosx-objc.h +++ b/indra/llwindow/llwindowmacosx-objc.h @@ -78,6 +78,8 @@ void initMainLoop(); void cleanupViewer(); void handleUrl(const char* url); void dispatchUrl(std::string url); +void startWatchdog(std::string_view state); +void stopWatchdog(); /* Defined in llwindowmacosx-objc.mm: */ int createNSApp(int argc, const char **argv); @@ -100,7 +102,6 @@ bool isCGCursorVisible(); void hideNSCursorTillMove(bool hide); void requestUserAttention(); long showAlert(std::string title, std::string text, int type); -void setResizeMode(bool oldresize, void* glview); NSWindowRef createNSWindow(int x, int y, int width, int height); @@ -111,16 +112,14 @@ void glSwapBuffers(void* context); CGLContextObj getCGLContextObj(GLViewRef view); unsigned long getVramSize(GLViewRef view); float getDeviceUnitSize(GLViewRef view); -CGPoint getContentViewBoundsPosition(NSWindowRef window); -CGSize getContentViewBoundsSize(NSWindowRef window); -CGSize getDeviceContentViewSize(NSWindowRef window, GLViewRef view); +CGRect getContentViewRect(NSWindowRef window); +CGRect getBackingViewRect(NSWindowRef window, GLViewRef view); void getWindowSize(NSWindowRef window, float* size); void setWindowSize(NSWindowRef window, int width, int height); void getCursorPos(NSWindowRef window, float* pos); void makeWindowOrderFront(NSWindowRef window); void convertScreenToWindow(NSWindowRef window, float *coord); void convertWindowToScreen(NSWindowRef window, float *coord); -void convertScreenToView(NSWindowRef window, float *coord); void convertRectToScreen(NSWindowRef window, float *coord); void convertRectFromScreen(NSWindowRef window, float *coord); void setWindowPos(NSWindowRef window, float* pos); @@ -141,6 +140,7 @@ void callLeftMouseDown(float *pos, unsigned int mask); void callLeftMouseUp(float *pos, unsigned int mask); void callDoubleClick(float *pos, unsigned int mask); void callResize(unsigned int width, unsigned int height); +void callRequestResolutionUpdate(); void callMouseMoved(float *pos, unsigned int mask); void callMouseDragged(float *pos, unsigned int mask); void callScrollMoved(float deltaX, float deltaY); @@ -158,6 +158,7 @@ void callFocusLost(); void callModifier(unsigned int mask); void callQuitHandler(); void commitCurrentPreedit(GLViewRef glView); +bool windowCallbacksReady(); #include <string> void callHandleDragEntered(std::string url); diff --git a/indra/llwindow/llwindowmacosx-objc.mm b/indra/llwindow/llwindowmacosx-objc.mm index 2e75d309ea..85208a5ac3 100644 --- a/indra/llwindow/llwindowmacosx-objc.mm +++ b/indra/llwindow/llwindowmacosx-objc.mm @@ -41,15 +41,15 @@ int createNSApp(int argc, const char *argv[]) { - return NSApplicationMain(argc, argv); + return NSApplicationMain(argc, argv); } void setupCocoa() { - static bool inited = false; - - if(!inited) - { + static bool inited = false; + + if(!inited) + { @autoreleasepool { // The following prevents the Cocoa command line parser from trying to open 'unknown' arguements as documents. // ie. running './secondlife -set Language fr' would cause a pop-up saying can't open document 'fr' @@ -57,8 +57,8 @@ void setupCocoa() [[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"]; } - inited = true; - } + inited = true; + } } bool copyToPBoard(const unsigned short *str, unsigned int len) @@ -66,7 +66,7 @@ bool copyToPBoard(const unsigned short *str, unsigned int len) @autoreleasepool { NSPasteboard *pboard = [NSPasteboard generalPasteboard]; [pboard clearContents]; - + NSArray *contentsToPaste = [[[NSArray alloc] initWithObjects:[NSString stringWithCharacters:str length:len], nil] autorelease]; return [pboard writeObjects:contentsToPaste]; } @@ -74,8 +74,8 @@ bool copyToPBoard(const unsigned short *str, unsigned int len) bool pasteBoardAvailable() { - NSArray *classArray = [NSArray arrayWithObject:[NSString class]]; - return [[NSPasteboard generalPasteboard] canReadObjectForClasses:classArray options:[NSDictionary dictionary]]; + NSArray *classArray = [NSArray arrayWithObject:[NSString class]]; + return [[NSPasteboard generalPasteboard] canReadObjectForClasses:classArray options:[NSDictionary dictionary]]; } unsigned short *copyFromPBoard() @@ -111,100 +111,110 @@ CursorRef createImageCursor(const char *fullpath, int hotspotX, int hotspotY) hotSpot:NSMakePoint(hotspotX, hotspotY) ] retain]; } - - return (CursorRef)cursor; + + return (CursorRef)cursor; } void setArrowCursor() { - NSCursor *cursor = [NSCursor arrowCursor]; - [NSCursor unhide]; - [cursor set]; + NSCursor *cursor = [NSCursor arrowCursor]; + [NSCursor unhide]; + [cursor set]; } void setIBeamCursor() { - NSCursor *cursor = [NSCursor IBeamCursor]; - [cursor set]; + NSCursor *cursor = [NSCursor IBeamCursor]; + [cursor set]; } void setPointingHandCursor() { - NSCursor *cursor = [NSCursor pointingHandCursor]; - [cursor set]; + NSCursor *cursor = [NSCursor pointingHandCursor]; + [cursor set]; } void setCopyCursor() { - NSCursor *cursor = [NSCursor dragCopyCursor]; - [cursor set]; + NSCursor *cursor = [NSCursor dragCopyCursor]; + [cursor set]; } void setCrossCursor() { - NSCursor *cursor = [NSCursor crosshairCursor]; - [cursor set]; + NSCursor *cursor = [NSCursor crosshairCursor]; + [cursor set]; } void setNotAllowedCursor() { - NSCursor *cursor = [NSCursor operationNotAllowedCursor]; - [cursor set]; + NSCursor *cursor = [NSCursor operationNotAllowedCursor]; + [cursor set]; } void hideNSCursor() { - [NSCursor hide]; + [NSCursor hide]; } void showNSCursor() { - [NSCursor unhide]; + [NSCursor unhide]; } +#if LL_DARWIN +// For CGCursorIsVisible no replacement in modern API +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + bool isCGCursorVisible() { return CGCursorIsVisible(); } +#if LL_DARWIN +#pragma clang diagnostic pop +#endif + void hideNSCursorTillMove(bool hide) { - [NSCursor setHiddenUntilMouseMoves:hide]; + [NSCursor setHiddenUntilMouseMoves:hide]; } // This is currently unused, since we want all our cursors to persist for the life of the app, but I've included it for completeness. OSErr releaseImageCursor(CursorRef ref) { - if( ref != NULL ) - { + if( ref != NULL ) + { @autoreleasepool { NSCursor *cursor = (NSCursor*)ref; [cursor autorelease]; } - } - else - { - return paramErr; - } - - return noErr; + } + else + { + return paramErr; + } + + return noErr; } OSErr setImageCursor(CursorRef ref) { - if( ref != NULL ) - { + if( ref != NULL ) + { @autoreleasepool { NSCursor *cursor = (NSCursor*)ref; [cursor set]; } - } - else - { - return paramErr; - } - - return noErr; + } + else + { + return paramErr; + } + + return noErr; } // Now for some unholy juggling between generic pointers and casting them to Obj-C objects! @@ -212,186 +222,155 @@ OSErr setImageCursor(CursorRef ref) NSWindowRef createNSWindow(int x, int y, int width, int height) { - LLNSWindow *window = [[LLNSWindow alloc]initWithContentRect:NSMakeRect(x, y, width, height) - styleMask:NSTitledWindowMask | NSResizableWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSTexturedBackgroundWindowMask backing:NSBackingStoreBuffered defer:NO]; - [window makeKeyAndOrderFront:nil]; - [window setAcceptsMouseMovedEvents:TRUE]; + LLNSWindow *window = [[LLNSWindow alloc]initWithContentRect:NSMakeRect(x, y, width, height) + styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskFullSizeContentView + backing:NSBackingStoreBuffered defer:YES]; + [window makeKeyAndOrderFront:nil]; + [window setAcceptsMouseMovedEvents:TRUE]; [window setRestorable:FALSE]; // Viewer manages state from own settings - return window; + return window; } GLViewRef createOpenGLView(NSWindowRef window, unsigned int samples, bool vsync) { - LLOpenGLView *glview = [[LLOpenGLView alloc]initWithFrame:[(LLNSWindow*)window frame] withSamples:samples andVsync:vsync]; - [(LLNSWindow*)window setContentView:glview]; - return glview; -} - -void setResizeMode(bool oldresize, void* glview) -{ - [(LLOpenGLView *)glview setOldResize:oldresize]; + LLOpenGLView *glview = [[LLOpenGLView alloc]initWithFrame:[(LLNSWindow*)window frame] withSamples:samples andVsync:vsync]; + [(LLNSWindow*)window setContentView:glview]; + return glview; } void glSwapBuffers(void* context) { - [(NSOpenGLContext*)context flushBuffer]; + [(NSOpenGLContext*)context flushBuffer]; } CGLContextObj getCGLContextObj(GLViewRef view) { - return [(LLOpenGLView *)view getCGLContextObj]; + return [(LLOpenGLView *)view getCGLContextObj]; } CGLPixelFormatObj* getCGLPixelFormatObj(NSWindowRef window) { - LLOpenGLView *glview = [(LLNSWindow*)window contentView]; - return [glview getCGLPixelFormatObj]; + LLOpenGLView *glview = [(LLNSWindow*)window contentView]; + return [glview getCGLPixelFormatObj]; } unsigned long getVramSize(GLViewRef view) { - return [(LLOpenGLView *)view getVramSize]; + return [(LLOpenGLView *)view getVramSize]; } float getDeviceUnitSize(GLViewRef view) { - return [(LLOpenGLView*)view convertSizeToBacking:NSMakeSize(1, 1)].width; + return [(LLOpenGLView*)view convertSizeToBacking:NSMakeSize(1, 1)].width; } -CGPoint getContentViewBoundsPosition(NSWindowRef window) +CGRect getContentViewRect(NSWindowRef window) { - return [[(LLNSWindow*)window contentView] bounds].origin; + return [[(LLNSWindow*)window contentView] bounds]; } -CGSize getContentViewBoundsSize(NSWindowRef window) +CGRect getBackingViewRect(NSWindowRef window, GLViewRef view) { - return [[(LLNSWindow*)window contentView] bounds].size; -} - -CGSize getDeviceContentViewSize(NSWindowRef window, GLViewRef view) -{ - return [(NSOpenGLView*)view convertRectToBacking:[[(LLNSWindow*)window contentView] bounds]].size; + return [(NSOpenGLView*)view convertRectToBacking:[[(LLNSWindow*)window contentView] bounds]]; } void getWindowSize(NSWindowRef window, float* size) { - NSRect frame = [(LLNSWindow*)window frame]; - size[0] = frame.origin.x; - size[1] = frame.origin.y; - size[2] = frame.size.width; - size[3] = frame.size.height; + NSRect frame = [(LLNSWindow*)window frame]; + size[0] = frame.origin.x; + size[1] = frame.origin.y; + size[2] = frame.size.width; + size[3] = frame.size.height; } void setWindowSize(NSWindowRef window, int width, int height) { - NSRect frame = [(LLNSWindow*)window frame]; - frame.size.width = width; - frame.size.height = height; - [(LLNSWindow*)window setFrame:frame display:TRUE]; + NSRect frame = [(LLNSWindow*)window frame]; + frame.size.width = width; + frame.size.height = height; + [(LLNSWindow*)window setFrame:frame display:TRUE]; } void setWindowPos(NSWindowRef window, float* pos) { - NSPoint point; - point.x = pos[0]; - point.y = pos[1]; - [(LLNSWindow*)window setFrameOrigin:point]; + NSPoint point; + point.x = pos[0]; + point.y = pos[1]; + [(LLNSWindow*)window setFrameOrigin:point]; } void getCursorPos(NSWindowRef window, float* pos) { - NSPoint mLoc; - mLoc = [(LLNSWindow*)window mouseLocationOutsideOfEventStream]; - pos[0] = mLoc.x; - pos[1] = mLoc.y; + NSPoint mLoc; + mLoc = [(LLNSWindow*)window mouseLocationOutsideOfEventStream]; + pos[0] = mLoc.x; + pos[1] = mLoc.y; } void makeWindowOrderFront(NSWindowRef window) { - [(LLNSWindow*)window makeKeyAndOrderFront:nil]; + [(LLNSWindow*)window makeKeyAndOrderFront:nil]; } void convertScreenToWindow(NSWindowRef window, float *coord) { - NSRect point; - point.origin.x = coord[0]; - point.origin.y = coord[1]; - point = [(LLNSWindow*)window convertRectFromScreen:point]; - coord[0] = point.origin.x; - coord[1] = point.origin.y; + NSRect point = NSMakeRect(coord[0], coord[1], 0,0); + point = [(LLNSWindow*)window convertRectFromScreen:point]; + coord[0] = point.origin.x; + coord[1] = point.origin.y; } void convertRectToScreen(NSWindowRef window, float *coord) { - NSRect point; - point.origin.x = coord[0]; - point.origin.y = coord[1]; - point.size.width = coord[2]; - point.size.height = coord[3]; - - point = [(LLNSWindow*)window convertRectToScreen:point]; - - coord[0] = point.origin.x; - coord[1] = point.origin.y; - coord[2] = point.size.width; - coord[3] = point.size.height; + NSRect rect = NSMakeRect(coord[0], coord[1], coord[2], coord[3]);; + rect = [(LLNSWindow*)window convertRectToScreen:rect]; + + coord[0] = rect.origin.x; + coord[1] = rect.origin.y; + coord[2] = rect.size.width; + coord[3] = rect.size.height; } void convertRectFromScreen(NSWindowRef window, float *coord) { - NSRect point; - point.origin.x = coord[0]; - point.origin.y = coord[1]; - point.size.width = coord[2]; - point.size.height = coord[3]; - - point = [(LLNSWindow*)window convertRectFromScreen:point]; - - coord[0] = point.origin.x; - coord[1] = point.origin.y; - coord[2] = point.size.width; - coord[3] = point.size.height; -} + NSRect point = NSMakeRect(coord[0], coord[1], coord[2], coord[3]); + point = [(LLNSWindow*)window convertRectFromScreen:point]; -void convertScreenToView(NSWindowRef window, float *coord) -{ - NSRect point; - point.origin.x = coord[0]; - point.origin.y = coord[1]; - point.origin = [(LLNSWindow*)window convertScreenToBase:point.origin]; - point.origin = [[(LLNSWindow*)window contentView] convertPoint:point.origin fromView:nil]; + coord[0] = point.origin.x; + coord[1] = point.origin.y; + coord[2] = point.size.width; + coord[3] = point.size.height; } void convertWindowToScreen(NSWindowRef window, float *coord) { - NSPoint point; - point.x = coord[0]; - point.y = coord[1]; - point = [(LLNSWindow*)window convertToScreenFromLocalPoint:point relativeToView:[(LLNSWindow*)window contentView]]; - coord[0] = point.x; - coord[1] = point.y; + NSRect rect = NSMakeRect(coord[0], coord[1], 0, 0); + rect = [(LLNSWindow*)window convertRectToScreen:rect]; + + coord[0] = rect.origin.x; + coord[1] = [[NSScreen screens][0] frame].size.height - rect.origin.y; } void closeWindow(NSWindowRef window) { - [(LLNSWindow*)window close]; - [(LLNSWindow*)window release]; + [(LLNSWindow*)window close]; + [(LLNSWindow*)window release]; } void removeGLView(GLViewRef view) { - [(LLOpenGLView*)view clearGLContext]; - [(LLOpenGLView*)view removeFromSuperview]; + [(LLOpenGLView*)view clearGLContext]; + [(LLOpenGLView*)view removeFromSuperview]; } void setupInputWindow(NSWindowRef window, GLViewRef glview) { - [[(LLAppDelegate*)[NSApp delegate] inputView] setGLView:(LLOpenGLView*)glview]; + [[(LLAppDelegate*)[NSApp delegate] inputView] setGLView:(LLOpenGLView*)glview]; } void commitCurrentPreedit(GLViewRef glView) { - [(LLOpenGLView*)glView commitCurrentPreedit]; + [(LLOpenGLView*)glView commitCurrentPreedit]; } void allowDirectMarkedTextInput(bool allow, GLViewRef glView) @@ -401,20 +380,20 @@ void allowDirectMarkedTextInput(bool allow, GLViewRef glView) NSWindowRef getMainAppWindow() { - LLNSWindow *winRef = [(LLAppDelegate*)[[NSApplication sharedApplication] delegate] window]; - - [winRef setAcceptsMouseMovedEvents:TRUE]; - return winRef; + LLNSWindow *winRef = [(LLAppDelegate*)[[NSApplication sharedApplication] delegate] window]; + + [winRef setAcceptsMouseMovedEvents:TRUE]; + return winRef; } void makeFirstResponder(NSWindowRef window, GLViewRef view) { - [(LLNSWindow*)window makeFirstResponder:(LLOpenGLView*)view]; + [(LLNSWindow*)window makeFirstResponder:(LLOpenGLView*)view]; } void requestUserAttention() { - [[NSApplication sharedApplication] requestUserAttention:NSInformationalRequest]; + [[NSApplication sharedApplication] requestUserAttention:NSInformationalRequest]; } long showAlert(std::string text, std::string title, int type) @@ -422,7 +401,7 @@ long showAlert(std::string text, std::string title, int type) long ret = 0; @autoreleasepool { NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - + [alert setMessageText:[NSString stringWithCString:title.c_str() encoding:[NSString defaultCStringEncoding]]]; [alert setInformativeText:[NSString stringWithCString:text.c_str() encoding:[NSString defaultCStringEncoding]]]; if (type == 0) @@ -439,7 +418,7 @@ long showAlert(std::string text, std::string title, int type) } ret = [alert runModal]; } - + if (ret == NSAlertFirstButtonReturn) { if (type == 1) @@ -459,7 +438,7 @@ long showAlert(std::string text, std::string title, int type) ret = 1; } } - + return ret; } @@ -472,5 +451,5 @@ long showAlert(std::string text, std::string title, int type) unsigned int getModifiers() { - return [NSEvent modifierFlags]; + return [NSEvent modifierFlags]; } diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index c97e014e46..c0cc6596a2 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -38,21 +38,21 @@ #include "lldir.h" #include "indra_constants.h" - #include <OpenGL/OpenGL.h> #include <Carbon/Carbon.h> #include <CoreServices/CoreServices.h> #include <CoreGraphics/CGDisplayConfiguration.h> #include <IOKit/IOCFPlugIn.h> -#include <IOKit/IOKitLib.h> +#include "llwindowmacosx_iokit.h" #include <IOKit/IOMessage.h> #include <IOKit/hid/IOHIDUsageTables.h> #include <IOKit/hid/IOHIDLib.h> #include <IOKit/usb/IOUSBLib.h> extern bool gDebugWindowProc; -bool gHiDPISupport = true; +bool gHiDPISupport = false; +bool gHDRDisplaySupport = false; const S32 BITS_PER_PIXEL = 32; const S32 MAX_NUM_RESOLUTIONS = 32; @@ -166,6 +166,7 @@ LLWindowMacOSX::LLWindowMacOSX(LLWindowCallbacks* callbacks, // Route them to a dummy callback structure until the end of constructor. LLWindowCallbacks null_callbacks; mCallbacks = &null_callbacks; + mIsConstructing = true; // Voodoo for calling cocoa from carbon (see llwindowmacosx-objc.mm). setupCocoa(); @@ -236,10 +237,11 @@ LLWindowMacOSX::LLWindowMacOSX(LLWindowCallbacks* callbacks, allowLanguageTextInput(NULL, false); } + setUseMultGL(sUseMultGL); + mCallbacks = callbacks; + mIsConstructing = false; stop_glerror(); - - } // These functions are used as wrappers for our internal event handling callbacks. @@ -413,6 +415,14 @@ void callResize(unsigned int width, unsigned int height) } } +void callRequestResolutionUpdate() +{ + if (gWindowImplementation && gWindowImplementation->getCallbacks()) + { + gWindowImplementation->getCallbacks()->handleRequestResolutionUpdate(gWindowImplementation); + } +} + void callMouseMoved(float *pos, MASK mask) { if (!gWindowImplementation) @@ -611,7 +621,7 @@ void callQuitHandler() { if (gWindowImplementation && gWindowImplementation->getCallbacks()) { - if(gWindowImplementation->getCallbacks()->handleCloseRequest(gWindowImplementation)) + if(gWindowImplementation->getCallbacks()->handleCloseRequest(gWindowImplementation, true)) { gWindowImplementation->getCallbacks()->handleQuit(gWindowImplementation); } @@ -706,6 +716,11 @@ void getPreeditLocation(float *location, unsigned int length) } } +bool windowCallbacksReady() +{ + return gWindowImplementation && !gWindowImplementation->isConstructing(); +} + void LLWindowMacOSX::updateMouseDeltas(float* deltas) { if (mCursorDecoupled) @@ -759,7 +774,7 @@ bool LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits kCGLPFAMultisample, kCGLPFASampleBuffers, static_cast<CGLPixelFormatAttribute>((mFSAASamples > 0 ? 1 : 0)), kCGLPFASamples, static_cast<CGLPixelFormatAttribute>(mFSAASamples), - kCGLPFAStencilSize, static_cast<CGLPixelFormatAttribute>(8), + //kCGLPFAStencilSize, static_cast<CGLPixelFormatAttribute>(8), kCGLPFADepthSize, static_cast<CGLPixelFormatAttribute>(24), kCGLPFAAlphaSize, static_cast<CGLPixelFormatAttribute>(8), kCGLPFAColorSize, static_cast<CGLPixelFormatAttribute>(24), @@ -937,9 +952,10 @@ bool LLWindowMacOSX::getVisible() if(mFullscreen) { result = true; - }if (mWindow) + } + if (mWindow) { - result = true; + result = true; } return(result); @@ -986,7 +1002,7 @@ bool LLWindowMacOSX::getPosition(LLCoordScreen *position) } else if(mWindow) { - const CGPoint & pos = getContentViewBoundsPosition(mWindow); + CGPoint pos = getContentViewRect(mWindow).origin; position->mX = pos.x; position->mY = pos.y; @@ -1013,7 +1029,7 @@ bool LLWindowMacOSX::getSize(LLCoordScreen *size) } else if(mWindow) { - const CGSize & sz = gHiDPISupport ? getDeviceContentViewSize(mWindow, mGLView) : getContentViewBoundsSize(mWindow); + CGSize sz = getBackingViewRect(mWindow, mGLView).size; size->mX = sz.width; size->mY = sz.height; @@ -1039,7 +1055,7 @@ bool LLWindowMacOSX::getSize(LLCoordWindow *size) } else if(mWindow) { - const CGSize & sz = gHiDPISupport ? getDeviceContentViewSize(mWindow, mGLView) : getContentViewBoundsSize(mWindow); + CGSize sz = getBackingViewRect(mWindow, mGLView).size; size->mX = sz.width; size->mY = sz.height; @@ -1225,6 +1241,12 @@ void LLWindowMacOSX::setMouseClipping( bool b ) adjustCursorDecouple(); } +#if LL_DARWIN +// For CGSetLocalEventsSuppressionInterval there is no replacement in modern API +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + bool LLWindowMacOSX::setCursorPosition(const LLCoordWindow position) { bool result = false; @@ -1254,14 +1276,15 @@ bool LLWindowMacOSX::setCursorPosition(const LLCoordWindow position) // trigger mouse move callback LLCoordGL gl_pos; convertCoords(position, &gl_pos); - float scale = getSystemUISize(); - gl_pos.mX *= scale; - gl_pos.mY *= scale; mCallbacks->handleMouseMove(this, gl_pos, (MASK)0); return result; } +#if LL_DARWIN +#pragma clang diagnostic pop +#endif + bool LLWindowMacOSX::getCursorPosition(LLCoordWindow *position) { float cursor_point[2]; @@ -1486,8 +1509,9 @@ bool LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordWindow* to) convertScreenToWindow(mWindow, mouse_point); - to->mX = mouse_point[0]; - to->mY = mouse_point[1]; + float scale_factor = getSystemUISize(); + to->mX = mouse_point[0] * scale_factor; + to->mY = mouse_point[1] * scale_factor; return true; } @@ -1499,9 +1523,9 @@ bool LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordScreen *to) if(mWindow) { float mouse_point[2]; - - mouse_point[0] = from.mX; - mouse_point[1] = from.mY; + float scale_factor = getSystemUISize(); + mouse_point[0] = from.mX / scale_factor; + mouse_point[1] = from.mY / scale_factor; convertWindowToScreen(mWindow, mouse_point); @@ -2372,7 +2396,7 @@ bool LLWindowMacOSX::getInputDevices(U32 device_type_filter, io_iterator_t io_iter = 0; // create an IO object iterator - result = IOServiceGetMatchingServices( kIOMasterPortDefault, device_dict_ref, &io_iter ); + result = IOServiceGetMatchingServices( kLLIOMainPort, device_dict_ref, &io_iter ); if ( kIOReturnSuccess != result ) { LL_WARNS("Joystick") << "IOServiceGetMatchingServices failed" << LL_ENDL; @@ -2626,6 +2650,12 @@ std::vector<std::string> LLWindowMacOSX::getDynamicFallbackFontList() return std::vector<std::string>(); } +LLFontFallbackMatch LLWindowMacOSX::findFallbackFontForChar(llwchar wch) +{ + // Not implemented on macOS; would use CoreText (CTFontCreateForString). + return LLFontFallbackMatch(); +} + // static MASK LLWindowMacOSX::modifiersToMask(S16 modifiers) { @@ -2638,7 +2668,7 @@ MASK LLWindowMacOSX::modifiersToMask(S16 modifiers) F32 LLWindowMacOSX::getSystemUISize() { - return gHiDPISupport ? ::getDeviceUnitSize(mGLView) : LLWindow::getSystemUISize(); + return ::getDeviceUnitSize(mGLView); } #if LL_OS_DRAGDROP_ENABLED diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h index 7de1a40d93..a9475c84cc 100644 --- a/indra/llwindow/llwindowmacosx.h +++ b/indra/llwindow/llwindowmacosx.h @@ -36,11 +36,6 @@ #include <ApplicationServices/ApplicationServices.h> #include <OpenGL/OpenGL.h> -// AssertMacros.h does bad things. -#include "fix_macros.h" -#undef verify -#undef require - class LLWindowMacOSX : public LLWindow { public: @@ -55,7 +50,11 @@ public: void restore() override; bool getFullscreen(); bool getPosition(LLCoordScreen *position) override; + + // Content view size in backing pixels (excludes title bar). + // Note: on Windows, getSize(LLCoordScreen) returns the outer window frame size (includes title bar and borders). bool getSize(LLCoordScreen *size) override; + bool getSize(LLCoordWindow *size) override; bool setPosition(LLCoordScreen position) override; bool setSizeImpl(LLCoordScreen size) override; @@ -63,6 +62,7 @@ public: bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL) override; bool setCursorPosition(LLCoordWindow position) override; bool getCursorPosition(LLCoordWindow *position) override; + bool isWrapMouse() const override { return !mCursorDecoupled; }; void showCursor() override; void hideCursor() override; void showCursorFromMouseMove() override; @@ -121,6 +121,7 @@ public: static std::vector<std::string> getDisplaysResolutionList(); static std::vector<std::string> getDynamicFallbackFontList(); + static LLFontFallbackMatch findFallbackFontForChar(llwchar wch); // Provide native key event data LLSD getNativeKeyData() override; @@ -150,6 +151,10 @@ public: // enable or disable multithreaded GL static void setUseMultGL(bool use_mult_gl); + // During construction null_callbacks are used, + // this is a way to determine when callbacks are ready. + bool isConstructing() const { return mIsConstructing; } + protected: LLWindowMacOSX(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags, @@ -174,9 +179,6 @@ protected: bool shouldPostQuit() { return mPostQuit; } - //Satisfy MAINT-3135 and MAINT-3288 with a flag. - /*virtual */ void setOldResize(bool oldresize) override {setResizeMode(oldresize, mGLView); } - private: void restoreGLContext(); @@ -229,6 +231,7 @@ protected: bool mMinimized; U32 mFSAASamples; bool mForceRebuild; + bool mIsConstructing = true; S32 mDragOverrideCursor; diff --git a/indra/llwindow/llwindowmacosx_iokit.h b/indra/llwindow/llwindowmacosx_iokit.h new file mode 100644 index 0000000000..a6be2c86ef --- /dev/null +++ b/indra/llwindow/llwindowmacosx_iokit.h @@ -0,0 +1,35 @@ +/** + * @file llwindowmacosx_iokit.h + * @brief IOKit compatibility for macOS deployment target differences + * + * $LicenseInfo:firstyear=2025&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2025, 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 +#include <IOKit/IOKitLib.h> + +// kIOMainPortDefault is the macOS 12+ rename of kIOMasterPortDefault. +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 120000 +static const mach_port_t kLLIOMainPort = kIOMainPortDefault; +#else +static const mach_port_t kLLIOMainPort = kIOMasterPortDefault; +#endif diff --git a/indra/llwindow/llwindowmesaheadless.h b/indra/llwindow/llwindowmesaheadless.h index 0bf8c46a30..47cd6320b1 100644 --- a/indra/llwindow/llwindowmesaheadless.h +++ b/indra/llwindow/llwindowmesaheadless.h @@ -30,70 +30,75 @@ #if LL_MESA_HEADLESS #include "llwindow.h" -#include "GL/glu.h" #include "GL/osmesa.h" class LLWindowMesaHeadless : public LLWindow { public: - /*virtual*/ void show() {}; - /*virtual*/ void hide() {}; - /*virtual*/ void close() {}; - /*virtual*/ bool getVisible() {return false;}; - /*virtual*/ bool getMinimized() {return false;}; - /*virtual*/ bool getMaximized() {return false;}; - /*virtual*/ bool maximize() {return false;}; - /*virtual*/ void minimize() {}; - /*virtual*/ void restore() {}; - /*virtual*/ bool getFullscreen() {return false;}; - /*virtual*/ bool getPosition(LLCoordScreen *position) {return false;}; - /*virtual*/ bool getSize(LLCoordScreen *size) {return false;}; - /*virtual*/ bool getSize(LLCoordWindow *size) {return false;}; - /*virtual*/ bool setPosition(LLCoordScreen position) {return false;}; - /*virtual*/ bool setSizeImpl(LLCoordScreen size) {return false;}; - /*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, const LLCoordScreen * const posp = NULL) {return false;}; - /*virtual*/ bool setCursorPosition(LLCoordWindow position) {return false;}; - /*virtual*/ bool getCursorPosition(LLCoordWindow *position) {return false;}; - /*virtual*/ void showCursor() {}; - /*virtual*/ void hideCursor() {}; - /*virtual*/ void showCursorFromMouseMove() {}; - /*virtual*/ void hideCursorUntilMouseMove() {}; - /*virtual*/ bool isCursorHidden() {return false;}; - /*virtual*/ void updateCursor() {}; - //virtual ECursorType getCursor() { return mCurrentCursor; }; - /*virtual*/ void captureMouse() {}; - /*virtual*/ void releaseMouse() {}; - /*virtual*/ void setMouseClipping( bool b ) {}; - /*virtual*/ bool isClipboardTextAvailable() {return false; }; - /*virtual*/ bool pasteTextFromClipboard(LLWString &dst) {return false; }; - /*virtual*/ bool copyTextToClipboard(const LLWString &src) {return false; }; - /*virtual*/ void flashIcon(F32 seconds) {}; - /*virtual*/ F32 getGamma() {return 1.0f; }; - /*virtual*/ bool setGamma(const F32 gamma) {return false; }; // Set the gamma - /*virtual*/ bool restoreGamma() {return false; }; // Restore original gamma table (before updating gamma) - /*virtual*/ void setFSAASamples(const U32 fsaa_samples) { /* FSAA not supported yet on Mesa headless.*/ } - /*virtual*/ U32 getFSAASamples() { return 0; } - //virtual ESwapMethod getSwapMethod() { return mSwapMethod; } - /*virtual*/ void gatherInput() {}; - /*virtual*/ void delayInputProcessing() {}; - /*virtual*/ void swapBuffers(); - /*virtual*/ void restoreGLContext() {}; + void show() override {}; + void hide() override {}; + void close() override {}; + bool getVisible() override {return false;}; + bool getMinimized() override {return false;}; + bool getMaximized() override {return false;}; + bool maximize() override {return false;}; + void minimize() override {}; + void restore() override {}; + bool getFullscreen() {return false;}; + bool getPosition(LLCoordScreen *position) override {return false;}; + bool getSize(LLCoordScreen *size) override {return false;}; + bool getSize(LLCoordWindow *size) override {return false;}; + bool setPosition(LLCoordScreen position) override {return false;}; + bool setSizeImpl(LLCoordScreen size) override {return false;}; + bool switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, const LLCoordScreen * const posp = NULL) override {return false;}; + bool setCursorPosition(LLCoordWindow position) override {return false;}; + bool getCursorPosition(LLCoordWindow *position) override {return false;}; + bool isWrapMouse() const override { return true; } + void showCursor() override {}; + void hideCursor() override {}; + void showCursorFromMouseMove() override {}; + void hideCursorUntilMouseMove() override {}; + bool isCursorHidden() override {return false;}; + void updateCursor() override {}; + //virtual ECursorType getCursor() override { return mCurrentCursor; }; + void captureMouse() override {}; + void releaseMouse() override {}; + void setMouseClipping( bool b ) override {}; + bool isClipboardTextAvailable() override {return false; }; + bool pasteTextFromClipboard(LLWString &dst) override {return false; }; + bool copyTextToClipboard(const LLWString &src) override {return false; }; + void flashIcon(F32 seconds) override {}; + F32 getGamma() override {return 1.0f; }; + bool setGamma(const F32 gamma) override {return false; }; // Set the gamma + bool restoreGamma() override {return false; }; // Restore original gamma table (before updating gamma) + void setFSAASamples(const U32 fsaa_samples) override { /* FSAA not supported yet on Mesa headless.*/ } + U32 getFSAASamples() override { return 0; } + //virtual ESwapMethod getSwapMethod() override { return mSwapMethod; } + void gatherInput() override {}; + void delayInputProcessing() override {}; + void swapBuffers() override; + + void* createSharedContext()override{return nullptr;}; + void makeContextCurrent(void* context)override{}; + void destroySharedContext(void* context)override{}; + void toggleVSync(bool enable_vsync) override {}; + bool setSizeImpl(LLCoordWindow size) override { return false; }; // handy coordinate space conversion routines - /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordWindow *to) { return false; }; - /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordScreen *to) { return false; }; - /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordGL *to) { return false; }; - /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordWindow *to) { return false; }; - /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordGL *to) { return false; }; - /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordScreen *to) { return false; }; + bool convertCoords(LLCoordScreen from, LLCoordWindow *to) override { return false; }; + bool convertCoords(LLCoordWindow from, LLCoordScreen *to) override { return false; }; + bool convertCoords(LLCoordWindow from, LLCoordGL *to) override { return false; }; + bool convertCoords(LLCoordGL from, LLCoordWindow *to) override { return false; }; + bool convertCoords(LLCoordScreen from, LLCoordGL *to) override { return false; }; + bool convertCoords(LLCoordGL from, LLCoordScreen *to) override { return false; }; - /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) { return NULL; }; - /*virtual*/ F32 getNativeAspectRatio() { return 1.0f; }; - /*virtual*/ F32 getPixelAspectRatio() { return 1.0f; }; - /*virtual*/ void setNativeAspectRatio(F32 ratio) {} + LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override { return NULL; }; + F32 getNativeAspectRatio() override { return 1.0f; }; + F32 getPixelAspectRatio() override { return 1.0f; }; + void setNativeAspectRatio(F32 ratio) override {} - /*virtual*/ void *getPlatformWindow() { return 0; }; - /*virtual*/ void bringToFront() {}; + void *getPlatformWindow() override { return 0; }; + void bringToFront() override {}; LLWindowMesaHeadless(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, @@ -112,9 +117,9 @@ public: LLSplashScreenMesaHeadless() {}; virtual ~LLSplashScreenMesaHeadless() {}; - /*virtual*/ void showImpl() {}; - /*virtual*/ void updateImpl(const std::string& mesg) {}; - /*virtual*/ void hideImpl() {}; + void showImpl() override {}; + void updateImpl(const std::string& mesg) override {}; + void hideImpl() override {}; }; diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index 69332e36b6..c98913e8b5 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -37,7 +37,10 @@ #include "llstring.h" #include "lldir.h" #include "llfindlocale.h" +#include "llpreeditor.h" +#include "llsdl.h" +#if LL_LINUX || __FreeBSD__ #ifdef LL_GLIB #include <glib.h> #endif @@ -46,108 +49,48 @@ extern "C" { # include "fontconfig/fontconfig.h" } -#if LL_LINUX || __FreeBSD__ // not necessarily available on random SDL platforms, so #if LL_LINUX // for execv(), waitpid(), fork() -# include <unistd.h> -# include <sys/types.h> -# include <sys/wait.h> -# define GLX_GLXEXT_PROTOTYPES 1 -# include <GL/glx.h> -# include <stdio.h> -#endif // LL_LINUX +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <stdio.h> -extern bool gDebugWindowProc; - -const S32 MAX_NUM_RESOLUTIONS = 200; +#if LL_X11 +LLWindowSDL::X11_DATA LLWindowSDL::sX11Data = {}; +#endif +#if LL_WAYLAND +LLWindowSDL::WAYLAND_DATA LLWindowSDL::sWaylandData = {}; +#endif +#endif // LL_LINUX #if LL_DARWIN - #include <OpenGL/OpenGL.h> -#include <CoreGraphics/CGDirectDisplay.h> +#include <Carbon/Carbon.h> #include <CoreServices/CoreServices.h> - -bool gHiDPISupport = true; - -namespace -{ - struct NativeKeyEventData { - enum EventType { - KEYUNKNOWN, - KEYUP, - KEYDOWN, - KEYCHAR - }; - - EventType mKeyEvent = KEYUNKNOWN; - uint32_t mEventType = 0; - uint32_t mEventModifiers = 0; - uint32_t mEventKeyCode = 0; - uint32_t mEventChars = 0; - uint32_t mEventUnmodChars = 0; - bool mEventRepeat = false; - } *mRawKeyEvent = NULL; -} -// -// LLWindowMacOSX -// +#include <CoreGraphics/CGDisplayConfiguration.h> bool LLWindowSDL::sUseMultGL = false; - #endif -bool hasHIDPI = 0; +bool gHiDPISupport = true; + +const S32 MAX_NUM_RESOLUTIONS = 200; +const S32 DEFAULT_REFRESH_RATE = 60; // // LLWindowSDL // -#if LL_X11 -# include <X11/Xutil.h> -#endif //LL_X11 +#if LL_WINDOWS && !LL_MESA_HEADLESS +#pragma comment(lib, "dinput8") +#endif // TOFU HACK -- (*exactly* the same hack as LLWindowMacOSX for a similar // set of reasons): Stash a pointer to the LLWindowSDL object here and // maintain in the constructor and destructor. This assumes that there will // be only one object of this class at any time. Currently this is true. -static LLWindowSDL *gWindowImplementation = NULL; - - -void maybe_lock_display(void) -{ - if (gWindowImplementation && gWindowImplementation->Lock_Display) { - gWindowImplementation->Lock_Display(); - } -} - - -void maybe_unlock_display(void) -{ - if (gWindowImplementation && gWindowImplementation->Unlock_Display) { - gWindowImplementation->Unlock_Display(); - } -} - - -#if LL_X11 -// static -Window LLWindowSDL::get_SDL_XWindowID(void) -{ - if (gWindowImplementation) { - return gWindowImplementation->mSDL_XWindowID; - } - return None; -} - -//static -Display* LLWindowSDL::get_SDL_Display(void) -{ - if (gWindowImplementation) { - return gWindowImplementation->mSDL_Display; - } - return NULL; -} -#endif // LL_X11 +static LLWindowSDL *gWindowImplementation = nullptr; LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, @@ -156,36 +99,16 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, bool enable_vsync, bool use_gl, bool ignore_pixel_depth, U32 fsaa_samples) : LLWindow(callbacks, fullscreen, flags), - Lock_Display(NULL), - Unlock_Display(NULL), mGamma(1.0f) + mGamma(1.0f), mFlashing(false) { + SDL_GL_LoadLibrary(nullptr); + // Initialize the keyboard gKeyboard = new LLKeyboardSDL(); gKeyboard->setCallbacks(callbacks); - // Note that we can't set up key-repeat until after SDL has init'd video - -#if LL_DARWIN - hasHIDPI = gHiDPISupport; -#endif - - // Ignore use_gl for now, only used for drones on PC - mWindow = NULL; - mContext = {}; - mNeedsResize = false; - mOverrideAspectRatio = 0.f; - mGrabbyKeyFlags = 0; - mReallyCapturedCount = 0; - mHaveInputFocus = -1; - mIsMinimized = -1; - mFSAASamples = fsaa_samples; - -#if LL_X11 - mSDL_XWindowID = None; - mSDL_Display = nullptr; -#endif // LL_X11 // Assume 4:3 aspect ratio until we know better - mOriginalAspectRatio = 1024.0 / 768.0; + mNativeAspectRatio = 1024.f / 768.f; if (title.empty()) mWindowTitle = "Second Life"; @@ -195,6 +118,7 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, // Create the GL context and set it up for windowed or fullscreen, as appropriate. if(createContext(x, y, width, height, 32, fullscreen, enable_vsync)) { + gGLManager.initWGL(); gGLManager.initGL(); //start with arrow cursor @@ -206,11 +130,6 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, // Stash an object pointer for OSMessageBox() gWindowImplementation = this; - - mFlashing = false; - - mKeyVirtualKey = 0; - mKeyModifiers = KMOD_NONE; } static SDL_Surface *Load_BMP_Resource(const char *basename) @@ -239,7 +158,7 @@ void LLWindowSDL::tryFindFullscreenSize( int &width, int &height ) LL_INFOS() << "createContext: setting up fullscreen " << width << "x" << height << LL_ENDL; // If the requested width or height is 0, find the best default for the monitor. - if((width == 0) || (height == 0)) + if(width == 0 || height == 0) { // Scan through the list of modes, looking for one which has: // height between 700 and 800 @@ -247,23 +166,22 @@ void LLWindowSDL::tryFindFullscreenSize( int &width, int &height ) S32 resolutionCount = 0; LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount); - if(resolutionList != NULL) + if(resolutionList != nullptr) { F32 closestAspect = 0; U32 closestHeight = 0; U32 closestWidth = 0; - int i; - LL_INFOS() << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << LL_ENDL; + LL_INFOS() << "createContext: searching for a display mode, original aspect is " << mNativeAspectRatio << LL_ENDL; - for(i=0; i < resolutionCount; i++) + for(S32 i=0; i < resolutionCount; i++) { F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight; LL_INFOS() << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << LL_ENDL; if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) && - (fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio))) + (fabs(aspect - mNativeAspectRatio) < fabs(closestAspect - mNativeAspectRatio))) { LL_INFOS() << " (new closest mode) " << LL_ENDL; @@ -279,7 +197,7 @@ void LLWindowSDL::tryFindFullscreenSize( int &width, int &height ) } } - if((width == 0) || (height == 0)) + if(width == 0 || height == 0) { // Mode search failed for some reason. Use the old-school default. width = 1024; @@ -289,58 +207,10 @@ void LLWindowSDL::tryFindFullscreenSize( int &width, int &height ) bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, bool fullscreen, bool enable_vsync) { - //bool glneedsinit = false; - - LL_INFOS() << "createContext, fullscreen=" << fullscreen << - " size=" << width << "x" << height << LL_ENDL; + LL_INFOS() << "createContext, fullscreen=" << fullscreen << " size=" << width << "x" << height << LL_ENDL; // captures don't survive contexts mGrabbyKeyFlags = 0; - mReallyCapturedCount = 0; - - std::initializer_list<std::tuple< char const*, char const * > > hintList = - { - {SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR,"0"}, - {SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH,"1"}, - {SDL_HINT_IME_INTERNAL_EDITING,"1"} - }; - - for( auto hint: hintList ) - { - SDL_SetHint( std::get<0>(hint), std::get<1>(hint)); - } - - std::initializer_list<std::tuple<uint32_t, char const*, bool>> initList= - { {SDL_INIT_VIDEO,"SDL_INIT_VIDEO", true}, - {SDL_INIT_AUDIO,"SDL_INIT_AUDIO", false}, - {SDL_INIT_GAMECONTROLLER,"SDL_INIT_GAMECONTROLLER", false}, - {SDL_INIT_SENSOR,"SDL_INIT_SENSOR", false} - }; - - for( auto subSystem : initList) - { - if( SDL_InitSubSystem( std::get<0>(subSystem) ) < 0 ) - { - LL_WARNS() << "SDL_InitSubSystem for " << std::get<1>(subSystem) << " failed " << SDL_GetError() << LL_ENDL; - - if( std::get<2>(subSystem)) - setupFailure("SDL_Init() failure", "error", OSMB_OK); - - } - } - - SDL_version c_sdl_version; - SDL_VERSION(&c_sdl_version); - LL_INFOS() << "Compiled against SDL " - << int(c_sdl_version.major) << "." - << int(c_sdl_version.minor) << "." - << int(c_sdl_version.patch) << LL_ENDL; - SDL_version r_sdl_version; - SDL_GetVersion(&r_sdl_version); - LL_INFOS() << " Running against SDL " - << int(r_sdl_version.major) << "." - << int(r_sdl_version.minor) << "." - << int(r_sdl_version.patch) << LL_ENDL; if (width == 0) width = 1024; @@ -353,18 +223,6 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b mFullscreen = fullscreen; - int sdlflags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; - - if( mFullscreen ) - { - sdlflags |= SDL_WINDOW_FULLSCREEN; - tryFindFullscreenSize( width, height ); - } - - if(hasHIDPI) sdlflags = sdlflags | SDL_WINDOW_ALLOW_HIGHDPI; - - mSDLFlags = sdlflags; - // Setup default backing colors GLint redBits{8}, greenBits{8}, blueBits{8}, alphaBits{8}; GLint depthBits{24}, stencilBits{8}; @@ -375,33 +233,55 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, alphaBits); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, depthBits); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, stencilBits); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); + SDL_GLContextFlag context_flags{}; + if(LLRender::sGLCoreProfile) + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); #if LL_DARWIN - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + context_flags |= SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG; #else - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6); #endif - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + } - U32 context_flags = 0; if (gDebugGL) { context_flags |= SDL_GL_CONTEXT_DEBUG_FLAG; } + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, context_flags); SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); - // Create the window - mWindow = SDL_CreateWindow(mWindowTitle.c_str(), x, y, width, height, mSDLFlags); + if(mFullscreen) + { + tryFindFullscreenSize(width, height); + } + + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, mWindowTitle.c_str()); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, x); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, y); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, width); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, height); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, true); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, mFullscreen); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIDDEN_BOOLEAN, true); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN, gHiDPISupport); + + mWindow = SDL_CreateWindowWithProperties(props); if (mWindow == nullptr) { LL_WARNS() << "Window creation failure. SDL: " << SDL_GetError() << LL_ENDL; setupFailure("Window creation error", "Error", OSMB_OK); + SDL_DestroyProperties(props); return false; } + SDL_DestroyProperties(props); // Free properties once window is created // Create the context mContext = SDL_GL_CreateContext(mWindow); @@ -412,56 +292,92 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b return false; } - if (SDL_GL_MakeCurrent(mWindow, mContext) != 0) + if (!SDL_GL_MakeCurrent(mWindow, mContext)) { LL_WARNS() << "Failed to make context current. SDL: " << SDL_GetError() << LL_ENDL; setupFailure("GL Context failed to set current failure", "Error", OSMB_OK); return false; } - if(mFullscreen) + const SDL_DisplayMode* displayMode = SDL_GetCurrentDisplayMode(SDL_GetDisplayForWindow(mWindow)); + if(displayMode) { - if (mWindow) + mRefreshRate = ll_round(displayMode->refresh_rate); + mNativeAspectRatio = ((F32)displayMode->w) / ((F32)displayMode->h); + if(mFullscreen) { - mFullscreen = true; - /* - mFullscreenWidth = mSurface->w; - mFullscreenHeight = mSurface->h; - mFullscreenBits = mSurface->format->BitsPerPixel; - */ - SDL_GetWindowSize(mWindow, &mFullscreenWidth, &mFullscreenHeight); - mFullscreenRefresh = -1; + mFullscreenWidth = displayMode->w; + mFullscreenHeight = displayMode->h; + mFullscreenRefresh = ll_round(displayMode->refresh_rate); LL_INFOS() << "Running at " << mFullscreenWidth - << "x" << mFullscreenHeight - << "x" << mFullscreenBits - << " @ " << mFullscreenRefresh - << LL_ENDL; + << "x" << mFullscreenHeight + << " @ " << mFullscreenRefresh + << LL_ENDL; } - else + } + else + { + LL_WARNS() << "Failed to get current display mode and refresh rate" << LL_ENDL; + mRefreshRate = 0; + mNativeAspectRatio = 1024.f / 768.f; + if(mFullscreen) // Fallback to window size { - LL_WARNS() << "createContext: fullscreen creation failure. SDL: " << SDL_GetError() << LL_ENDL; - // No fullscreen support - mFullscreen = false; - mFullscreenWidth = -1; - mFullscreenHeight = -1; - mFullscreenBits = -1; + SDL_GetWindowSize(mWindow, &mFullscreenWidth, &mFullscreenHeight); mFullscreenRefresh = -1; + } + } + + if(mRefreshRate == 0) // We failed to get a valid refresh rate above + { + mRefreshRate = DEFAULT_REFRESH_RATE; + } + + /* Grab the window manager specific information */ +#if LL_LINUX || __FreeBSD__ + if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "x11") == 0) + { + LL_INFOS() << "Running under X11" << LL_ENDL; + mServerProtocol = X11; + + gGLManager.mIsX11 = true; + +#if LL_X11 + sX11Data.xdisplay = (Display *)SDL_GetPointerProperty(SDL_GetWindowProperties(mWindow), SDL_PROP_WINDOW_X11_DISPLAY_POINTER, nullptr); + sX11Data.xwindow = (Window)SDL_GetNumberProperty(SDL_GetWindowProperties(mWindow), SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0); + sX11Data.xscreen = (int)SDL_GetNumberProperty(SDL_GetWindowProperties(mWindow), SDL_PROP_WINDOW_X11_SCREEN_NUMBER, -1); + if (sX11Data.xdisplay && sX11Data.xwindow) + { - std::string error = llformat("Unable to run fullscreen at %d x %d.\nRunning in window.", width, height); - OSMessageBox(error, "Error", OSMB_OK); - return false; } +#endif + + gGLManager.initGLX(); } - else + else if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0) { - if (!mWindow) + LL_INFOS() << "Running under Wayland" << LL_ENDL; + mServerProtocol = Wayland; + + gGLManager.mIsWayland = true; + +#if LL_WAYLAND + sWaylandData.display = (struct wl_display *)SDL_GetPointerProperty(SDL_GetWindowProperties(mWindow), SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, nullptr); + sWaylandData.surface = (struct wl_surface *)SDL_GetPointerProperty(SDL_GetWindowProperties(mWindow), SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr); + if (sWaylandData.display && sWaylandData.surface) { - LL_WARNS() << "createContext: window creation failure. SDL: " << SDL_GetError() << LL_ENDL; - setupFailure("Window creation error", "Error", OSMB_OK); - return false; + } +#endif + + gGLManager.initEGL(); + + // If set (XWayland) remove DISPLAY, this will prompt dullahan to also use Wayland + if(getenv("DISPLAY")) + { + unsetenv("DISPLAY"); } } +#endif SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &redBits); SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &greenBits); @@ -479,13 +395,8 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b LL_INFOS() << " Stencil Bits " << S32(stencilBits) << LL_ENDL; GLint colorBits = redBits + greenBits + blueBits + alphaBits; - // fixme: actually, it's REALLY important for picking that we get at - // least 8 bits each of red,green,blue. Alpha we can be a bit more - // relaxed about if we have to. if (colorBits < 32) { -#if 0 - close(); setupFailure( "Second Life requires True Color (32-bit) to run in a window.\n" "Please go to Control Panels -> Display -> Settings and\n" @@ -495,7 +406,6 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b "Error", OSMB_OK); return false; -#endif } LL_PROFILER_GPU_CONTEXT; @@ -503,104 +413,87 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b // Enable vertical sync toggleVSync(enable_vsync); +#if LL_DARWIN + setUseMultGL(sUseMultGL); + + // Get vram via CGL on macos + gGLManager.mVRAM = getVramSize(); +#endif + // Set the application icon. SDL_Surface* bmpsurface = Load_BMP_Resource("ll_icon.BMP"); if (bmpsurface) { SDL_SetWindowIcon(mWindow, bmpsurface); - SDL_FreeSurface(bmpsurface); - bmpsurface = NULL; + SDL_DestroySurface(bmpsurface); + bmpsurface = nullptr; } -#if LL_X11 - /* Grab the window manager specific information */ - SDL_SysWMinfo info; - SDL_VERSION(&info.version); - if ( SDL_GetWindowWMInfo(mWindow, &info) ) - { - /* Save the information for later use */ - if ( info.subsystem == SDL_SYSWM_X11 ) - { - mSDL_Display = info.info.x11.display; - mSDL_XWindowID = info.info.x11.window; - } - else - { - LL_WARNS() << "We're not running under X11? Wild." - << LL_ENDL; - } - } - else - { - LL_WARNS() << "We're not running under any known WM. Wild." - << LL_ENDL; - } -#endif // LL_X11 + SDL_StartTextInput(mWindow); + return true; +} -# if LL_X11 - PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC queryInteger; - queryInteger = (PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC)glXGetProcAddressARB((const GLubyte *)"glXQueryCurrentRendererIntegerMESA"); - unsigned int vram_megabytes = 0; - queryInteger(GLX_RENDERER_VIDEO_MEMORY_MESA, &vram_megabytes); - if (!vram_megabytes) - { - glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, (int *)&vram_megabytes); - vram_megabytes /= 1024; - } - if (!vram_megabytes) - { - glGetIntegerv(GL_VBO_FREE_MEMORY_ATI, (int *)&vram_megabytes); - vram_megabytes /= 1024; - } - gGLManager.mVRAM = vram_megabytes; -#elif LL_DARWIN - CGLRendererInfoObj info = 0; - GLint vram_megabytes = 0; - int num_renderers = 0; - auto err = CGLQueryRendererInfo(CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay), &info, &num_renderers); - if (!err) +void* LLWindowSDL::createSharedContext() +{ + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, "Second Life OSR Utility"); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, 1); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, 1); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, false); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIDDEN_BOOLEAN, true); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN, false); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_UTILITY_BOOLEAN, true); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN, gHiDPISupport); + + auto osr_window = SDL_CreateWindowWithProperties(props); + if (osr_window == nullptr) { - CGLDescribeRenderer(info, 0, kCGLRPVideoMemoryMegabytes, &vram_megabytes); - CGLDestroyRendererInfo(info); + SDL_DestroyProperties(props); + return nullptr; } - else + SDL_DestroyProperties(props); // Free properties once window is created + SDL_GLContext pContext = SDL_GL_CreateContext(osr_window); + + // Hack to ensure main window context is bound + SDL_GL_MakeCurrent(mWindow, mContext); + + if (!pContext) { - vram_megabytes = 256; + LL_WARNS() << "Creating shared OpenGL context failed!" << LL_ENDL; + SDL_DestroyWindow(osr_window); + return nullptr; } - gGLManager.mVRAM = vram_megabytes; -# endif - SDL_StartTextInput(); - //make sure multisampling is disabled by default -#if GL_VERSION_1_3 - glDisable(GL_MULTISAMPLE_ARB); -#endif - // Don't need to get the current gamma, since there's a call that restores it to the system defaults. - return true; -} - -void* LLWindowSDL::createSharedContext() -{ - SDL_GLContext pContext = SDL_GL_CreateContext(mWindow); - if (pContext) { - LL_DEBUGS() << "Creating shared OpenGL context successful!" << LL_ENDL; - return (void*)pContext; + LLMutexLock osr_lock(&mOSRMutex); + mOSRContexts.emplace(pContext, osr_window); } - LL_WARNS() << "Creating shared OpenGL context failed!" << LL_ENDL; - return nullptr; + LL_DEBUGS() << "Creating shared OpenGL context successful!" << LL_ENDL; + return (void*)pContext; } void LLWindowSDL::makeContextCurrent(void* contextPtr) { - SDL_GL_MakeCurrent(mWindow, contextPtr); + LLMutexLock osr_lock(&mOSRMutex); + auto it = mOSRContexts.find((SDL_GLContext)contextPtr); + if(it != mOSRContexts.end()) + { + SDL_GL_MakeCurrent((SDL_Window*)it->second, (SDL_GLContext)it->first); + } LL_PROFILER_GPU_CONTEXT; } void LLWindowSDL::destroySharedContext(void* contextPtr) { - SDL_GL_DeleteContext(contextPtr); + LLMutexLock osr_lock(&mOSRMutex); + auto it = mOSRContexts.find((SDL_GLContext)contextPtr); + if(it != mOSRContexts.end()) + { + SDL_GL_DestroyContext((SDL_GLContext)it->first); + mDeadOSRWindows.push_back((SDL_Window*)it->second); + } } void LLWindowSDL::toggleVSync(bool enable_vsync) @@ -609,13 +502,11 @@ void LLWindowSDL::toggleVSync(bool enable_vsync) { LL_INFOS("Window") << "Disabling vertical sync" << LL_ENDL; SDL_GL_SetSwapInterval(0); - SDL_SetHintWithPriority(SDL_HINT_RENDER_VSYNC,"0",SDL_HINT_OVERRIDE); } else { LL_INFOS("Window") << "Enabling vertical sync" << LL_ENDL; SDL_GL_SetSwapInterval(1); - SDL_SetHintWithPriority(SDL_HINT_RENDER_VSYNC,"1",SDL_HINT_OVERRIDE); } } @@ -633,6 +524,7 @@ bool LLWindowSDL::switchContext(bool fullscreen, const LLCoordScreen &size, bool result = createContext(0, 0, size.mX, size.mY, 32, fullscreen, enable_vsync); if (result) { + gGLManager.initWGL(); gGLManager.initGL(); //start with arrow cursor @@ -650,27 +542,29 @@ void LLWindowSDL::destroyContext() { LL_INFOS() << "destroyContext begins" << LL_ENDL; + { + LLMutexLock osr_lock(&mOSRMutex); + for(SDL_Window* pWindow : mDeadOSRWindows) + { + SDL_DestroyWindow(pWindow); + } + mDeadOSRWindows.clear(); + } + // Stop unicode input - SDL_StopTextInput(); + SDL_StopTextInput(mWindow); // Clean up remaining GL state before blowing away window LL_INFOS() << "shutdownGL begins" << LL_ENDL; gGLManager.shutdownGL(); -#if LL_X11 - mSDL_Display = NULL; - mSDL_XWindowID = None; - Lock_Display = NULL; - Unlock_Display = NULL; -#endif // LL_X11 - LL_INFOS() << "Destroying SDL cursors" << LL_ENDL; quitCursors(); if (mContext) { LL_INFOS() << "Destroying SDL GL Context" << LL_ENDL; - SDL_GL_DeleteContext(mContext); + SDL_GL_DestroyContext(mContext); mContext = nullptr; } else @@ -689,21 +583,15 @@ void LLWindowSDL::destroyContext() LL_INFOS() << "SDL Window already destroyed" << LL_ENDL; } LL_INFOS() << "destroyContext end" << LL_ENDL; - - LL_INFOS() << "SDL_QuitSS/VID begins" << LL_ENDL; - SDL_QuitSubSystem(SDL_INIT_VIDEO); // *FIX: this might be risky... } LLWindowSDL::~LLWindowSDL() { destroyContext(); - if(mSupportedResolutions != NULL) - { - delete []mSupportedResolutions; - } + delete[] mSupportedResolutions; - gWindowImplementation = NULL; + gWindowImplementation = nullptr; } @@ -712,6 +600,7 @@ void LLWindowSDL::show() if (mWindow) { SDL_ShowWindow(mWindow); + SDL_RaiseWindow(mWindow); } } @@ -723,7 +612,6 @@ void LLWindowSDL::hide() } } -//virtual void LLWindowSDL::minimize() { if (mWindow) @@ -732,7 +620,6 @@ void LLWindowSDL::minimize() } } -//virtual void LLWindowSDL::restore() { if (mWindow) @@ -745,12 +632,6 @@ void LLWindowSDL::restore() // Usually called from LLWindowManager::destroyWindow() void LLWindowSDL::close() { - // Is window is already closed? - // if (!mWindow) - // { - // return; - // } - // Make sure cursor is visible and we haven't mangled the clipping state. setMouseClipping(false); showCursor(); @@ -760,18 +641,18 @@ void LLWindowSDL::close() bool LLWindowSDL::isValid() { - return (mWindow != NULL); + return mWindow != nullptr; } bool LLWindowSDL::getVisible() { - bool result = false; + bool result = true; if (mWindow) { - Uint32 flags = SDL_GetWindowFlags(mWindow); - if (flags & SDL_WINDOW_SHOWN) + SDL_WindowFlags flags = SDL_GetWindowFlags(mWindow); + if (flags & SDL_WINDOW_HIDDEN) { - result = true; + result = false; } } return result; @@ -782,7 +663,7 @@ bool LLWindowSDL::getMinimized() bool result = false; if (mWindow) { - Uint32 flags = SDL_GetWindowFlags(mWindow); + SDL_WindowFlags flags = SDL_GetWindowFlags(mWindow); if (flags & SDL_WINDOW_MINIMIZED) { result = true; @@ -796,7 +677,7 @@ bool LLWindowSDL::getMaximized() bool result = false; if (mWindow) { - Uint32 flags = SDL_GetWindowFlags(mWindow); + SDL_WindowFlags flags = SDL_GetWindowFlags(mWindow); if (flags & SDL_WINDOW_MAXIMIZED) { result = true; @@ -816,11 +697,6 @@ bool LLWindowSDL::maximize() return false; } -bool LLWindowSDL::getFullscreen() -{ - return mFullscreen; -} - bool LLWindowSDL::getPosition(LLCoordScreen *position) { if (mWindow) @@ -835,30 +711,22 @@ bool LLWindowSDL::getSize(LLCoordScreen *size) { if (mWindow) { - /* - if(hasHIDPI) - SDL_GL_GetDrawableSize(mWindow, &size->mX, &size->mY); - else - */ SDL_GetWindowSize(mWindow, &size->mX, &size->mY); - return (true); + return true; } - return (false); + return false; } bool LLWindowSDL::getSize(LLCoordWindow *size) { if (mWindow) { - if(hasHIDPI) - SDL_GL_GetDrawableSize(mWindow, &size->mX, &size->mY); - else - SDL_GetWindowSize(mWindow, &size->mX, &size->mY); - return (true); + SDL_GetWindowSizeInPixels(mWindow, &size->mX, &size->mY); + return true; } - return (false); + return false; } bool LLWindowSDL::setPosition(const LLCoordScreen position) @@ -877,21 +745,12 @@ template< typename T > bool setSizeImpl( const T& newSize, SDL_Window *pWin ) if( !pWin ) return false; - auto nFlags = SDL_GetWindowFlags( pWin ); - - if( nFlags & SDL_WINDOW_MAXIMIZED ) - SDL_RestoreWindow( pWin ); + SDL_WindowFlags winFlags = SDL_GetWindowFlags(pWin); + if( winFlags & SDL_WINDOW_MAXIMIZED) + SDL_RestoreWindow(pWin); - SDL_SetWindowSize( pWin, newSize.mX, newSize.mY ); - SDL_Event event; - event.type = SDL_WINDOWEVENT; - event.window.event = SDL_WINDOWEVENT_RESIZED; - event.window.windowID = SDL_GetWindowID( pWin ); - event.window.data1 = newSize.mX; - event.window.data2 = newSize.mY; - SDL_PushEvent( &event ); - + SDL_SetWindowSize(pWin, newSize.mX, newSize.mY); return true; } @@ -932,12 +791,6 @@ F32 LLWindowSDL::getGamma() bool LLWindowSDL::restoreGamma() { - if (mWindow) - { - Uint16 ramp[256]; - SDL_CalculateGammaRamp(1.f, ramp); - SDL_SetWindowGammaRamp(mWindow, ramp, ramp, ramp); - } return true; } @@ -945,14 +798,10 @@ bool LLWindowSDL::setGamma(const F32 gamma) { if (mWindow) { - Uint16 ramp[256]; - mGamma = gamma; - if (mGamma == 0) mGamma = 0.1f; + if (mGamma == 0) + mGamma = 0.1f; mGamma = 1.f / mGamma; - - SDL_CalculateGammaRamp(mGamma, ramp); - SDL_SetWindowGammaRamp(mWindow, ramp, ramp, ramp); } return true; } @@ -965,7 +814,10 @@ bool LLWindowSDL::isCursorHidden() // Constrains the mouse to the window. void LLWindowSDL::setMouseClipping(bool b) { - //SDL_WM_GrabInput(b ? SDL_GRAB_ON : SDL_GRAB_OFF); + if (mWindow) + { + SDL_SetWindowMouseGrab(mWindow, b); + } } // virtual @@ -981,29 +833,15 @@ void LLWindowSDL::setMinSize(U32 min_width, U32 min_height, bool enforce_immedia bool LLWindowSDL::setCursorPosition(const LLCoordWindow position) { - bool result = true; - LLCoordScreen screen_pos; - - if (!convertCoords(position, &screen_pos)) - { - return false; - } - - //LL_INFOS() << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << LL_ENDL; - - // do the actual forced cursor move. - + /* if (mFullscreen) { - SDL_WarpMouseGlobal(screen_pos.mX, screen_pos.mY); - return result; + SDL_WarpMouseGlobal((F32)position.mX, (F32)position.mY); + return true; } - - SDL_WarpMouseInWindow(mWindow, screen_pos.mX, screen_pos.mY); - - //LL_INFOS() << llformat("llcw %d,%d -> scr %d,%d", position.mX, position.mY, screen_pos.mX, screen_pos.mY) << LL_ENDL; - - return result; + */ + SDL_WarpMouseInWindow(mWindow, (F32)position.mX, (F32)position.mY); + return true; } bool LLWindowSDL::getCursorPosition(LLCoordWindow *position) @@ -1011,46 +849,34 @@ bool LLWindowSDL::getCursorPosition(LLCoordWindow *position) //Point cursor_point; LLCoordScreen screen_pos; - int x, y; + float x, y; SDL_GetMouseState(&x, &y); - screen_pos.mX = x; - screen_pos.mY = y; + screen_pos.mX = (S32)x; + screen_pos.mY = (S32)y; return convertCoords(screen_pos, position); } -F32 LLWindowSDL::getSystemUISize() -{ - if(hasHIDPI) return 2.0f; - else return 1.f; -} - F32 LLWindowSDL::getNativeAspectRatio() { - // MBW -- there are a couple of bad assumptions here. One is that the display list won't include - // ridiculous resolutions nobody would ever use. The other is that the list is in order. - - // New assumptions: - // - pixels are square (the only reasonable choice, really) - // - The user runs their display at a native resolution, so the resolution of the display - // when the app is launched has an aspect ratio that matches the monitor. - - //RN: actually, the assumption that there are no ridiculous resolutions (above the display's native capabilities) has - // been born out in my experience. - // Pixels are often not square (just ask the people who run their LCDs at 1024x768 or 800x600 when running fullscreen, like me) - // The ordering of display list is a blind assumption though, so we should check for max values - // Things might be different on the Mac though, so I'll defer to MBW - - // The constructor for this class grabs the aspect ratio of the monitor before doing any resolution - // switching, and stashes it in mOriginalAspectRatio. Here, we just return it. - if (mOverrideAspectRatio > 0.f) { return mOverrideAspectRatio; } + else if (mNativeAspectRatio > 0.f) + { + // we grabbed this value at startup, based on the user's desktop settings + return mNativeAspectRatio; + } - return mOriginalAspectRatio; + // RN: this hack presumes that the largest supported resolution is monitor-limited + // and that pixels in that mode are square, therefore defining the native aspect ratio + // of the monitor...this seems to work to a close approximation for most CRTs/LCDs + S32 num_resolutions; + LLWindowResolution* resolutions = getSupportedResolutions(num_resolutions); + + return ((F32)resolutions[num_resolutions - 1].mWidth / (F32)resolutions[num_resolutions - 1].mHeight); } F32 LLWindowSDL::getPixelAspectRatio() @@ -1073,61 +899,21 @@ F32 LLWindowSDL::getPixelAspectRatio() // dialogs are still usable in fullscreen. void LLWindowSDL::beforeDialog() { - bool running_x11 = false; -#if LL_X11 - running_x11 = (mSDL_XWindowID != None); -#endif //LL_X11 - LL_INFOS() << "LLWindowSDL::beforeDialog()" << LL_ENDL; if (SDLReallyCaptureInput(false)) // must ungrab input so popup works! { - if (mFullscreen) - { - // need to temporarily go non-fullscreen; bless SDL - // for providing a SDL_WM_ToggleFullScreen() - though - // it only works in X11 - if (running_x11 && mWindow) - { - SDL_SetWindowFullscreen( mWindow, 0 ); - } - } - } - -#if LL_X11 - if (mSDL_Display) - { - // Everything that we/SDL asked for should happen before we - // potentially hand control over to GTK. - maybe_lock_display(); - XSync(mSDL_Display, False); - maybe_unlock_display(); + if (mFullscreen && mWindow ) + SDL_SetWindowFullscreen( mWindow, 0 ); } -#endif // LL_X11 - - maybe_lock_display(); } void LLWindowSDL::afterDialog() { - bool running_x11 = false; -#if LL_X11 - running_x11 = (mSDL_XWindowID != None); -#endif //LL_X11 - LL_INFOS() << "LLWindowSDL::afterDialog()" << LL_ENDL; - maybe_unlock_display(); - - if (mFullscreen) - { - // need to restore fullscreen mode after dialog - only works - // in X11 - if (running_x11 && mWindow) - { - SDL_SetWindowFullscreen( mWindow, 0 ); - } - } + if (mFullscreen && mWindow ) + SDL_SetWindowFullscreen( mWindow, 0 ); } void LLWindowSDL::flashIcon(F32 seconds) @@ -1144,9 +930,18 @@ void LLWindowSDL::flashIcon(F32 seconds) mFlashing = true; } +void LLWindowSDL::maybeStopFlashIcon() +{ + if (mFlashing && mFlashTimer.hasExpired()) + { + mFlashing = false; + SDL_FlashWindow( mWindow, SDL_FLASH_CANCEL ); + } +} + bool LLWindowSDL::isClipboardTextAvailable() { - return SDL_HasClipboardText() == SDL_TRUE; + return SDL_HasClipboardText(); } bool LLWindowSDL::pasteTextFromClipboard(LLWString &dst) @@ -1167,12 +962,12 @@ bool LLWindowSDL::pasteTextFromClipboard(LLWString &dst) bool LLWindowSDL::copyTextToClipboard(const LLWString& text) { const std::string utf8 = wstring_to_utf8str(text); - return SDL_SetClipboardText(utf8.c_str()) == 0; // success == 0 + return SDL_SetClipboardText(utf8.c_str()); } bool LLWindowSDL::isPrimaryTextAvailable() { - return SDL_HasPrimarySelectionText() == SDL_TRUE; + return SDL_HasPrimarySelectionText(); } bool LLWindowSDL::pasteTextFromPrimary(LLWString &dst) @@ -1193,7 +988,7 @@ bool LLWindowSDL::pasteTextFromPrimary(LLWString &dst) bool LLWindowSDL::copyTextToPrimary(const LLWString& text) { const std::string utf8 = wstring_to_utf8str(text); - return SDL_SetPrimarySelectionText(utf8.c_str()) == 0; // success == 0 + return SDL_SetPrimarySelectionText(utf8.c_str()); } LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_resolutions) @@ -1203,32 +998,29 @@ LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_reso mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS]; mNumSupportedResolutions = 0; - // <FS:ND> Use display no from mWindow/mSurface here? - int max = SDL_GetNumDisplayModes(0); - max = llclamp( max, 0, MAX_NUM_RESOLUTIONS ); - - for( int i =0; i < max; ++i ) - { - SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 }; - if (SDL_GetDisplayMode( 0 , i, &mode) != 0) - { - continue; - } - - int w = mode.w; - int h = mode.h; - if ((w >= 800) && (h >= 600)) - { - // make sure we don't add the same resolution multiple times! - if ( (mNumSupportedResolutions == 0) || - ((mSupportedResolutions[mNumSupportedResolutions-1].mWidth != w) && - (mSupportedResolutions[mNumSupportedResolutions-1].mHeight != h)) ) + SDL_DisplayID display = SDL_GetPrimaryDisplay(); + int num_modes = 0; + SDL_DisplayMode **modes = SDL_GetFullscreenDisplayModes(display, &num_modes); + num_modes = llclamp(num_modes, 0, MAX_NUM_RESOLUTIONS); + if (modes) { + for (int i = 0; i < num_modes; ++i) { + SDL_DisplayMode *mode = modes[i]; + int w = mode->w; + int h = mode->h; + if ((w >= 800) && (h >= 600)) { - mSupportedResolutions[mNumSupportedResolutions].mWidth = w; - mSupportedResolutions[mNumSupportedResolutions].mHeight = h; - mNumSupportedResolutions++; + // make sure we don't add the same resolution multiple times! + if ( (mNumSupportedResolutions == 0) || + ((mSupportedResolutions[mNumSupportedResolutions-1].mWidth != w) && + (mSupportedResolutions[mNumSupportedResolutions-1].mHeight != h)) ) + { + mSupportedResolutions[mNumSupportedResolutions].mWidth = w; + mSupportedResolutions[mNumSupportedResolutions].mHeight = h; + mNumSupportedResolutions++; + } } } + SDL_free(modes); } } @@ -1236,15 +1028,42 @@ LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_reso return mSupportedResolutions; } +//static +std::vector<std::string> LLWindowSDL::getDisplaysResolutionList() +{ + std::vector<std::string> ret; + if (gWindowImplementation) + { + S32 resolutionCount = 0; + LLWindowResolution* resolutionList = gWindowImplementation->getSupportedResolutions(resolutionCount); + if (resolutionList != nullptr) + { + for (S32 i = 0; i < resolutionCount; i++) + { + const LLWindowResolution& resolution = resolutionList[i]; + ret.push_back(std::to_string(resolution.mWidth) + "x" + std::to_string(resolution.mHeight)); + } + } + } + return ret; +} + + bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to) { if (!to) return false; - to->mX = from.mX; - int h; - SDL_GetWindowSize(mWindow, nullptr, &h); - to->mY = h - from.mY - 1; + if (!mWindow) + return false; + + S32 height; + SDL_GetWindowSizeInPixels(mWindow, nullptr, &height); + + float pixel_scale = SDL_GetWindowPixelDensity(mWindow); + + to->mX = from.mX / pixel_scale; + to->mY = (height - from.mY - 1) / pixel_scale; return true; } @@ -1254,10 +1073,15 @@ bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to) if (!to) return false; - to->mX = from.mX; - int h; - SDL_GetWindowSize(mWindow, nullptr, &h); - to->mY = h - from.mY - 1; + if (!mWindow) + return false; + S32 height; + SDL_GetWindowSize(mWindow, nullptr, &height); + + float pixel_scale = SDL_GetWindowPixelDensity(mWindow); + + to->mX = from.mX * pixel_scale; + to->mY = (height - from.mY - 1) * pixel_scale; return true; } @@ -1270,7 +1094,7 @@ bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to) // In the fullscreen case, window and screen coordinates are the same. to->mX = from.mX; to->mY = from.mY; - return (true); + return true; } bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to) @@ -1281,26 +1105,26 @@ bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to) // In the fullscreen case, window and screen coordinates are the same. to->mX = from.mX; to->mY = from.mY; - return (true); + return true; } bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordGL *to) { LLCoordWindow window_coord; - return(convertCoords(from, &window_coord) && convertCoords(window_coord, to)); + return convertCoords(from, &window_coord) && convertCoords(window_coord, to); } bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordScreen *to) { LLCoordWindow window_coord; - return(convertCoords(from, &window_coord) && convertCoords(window_coord, to)); + return convertCoords(from, &window_coord) && convertCoords(window_coord, to); } void LLWindowSDL::setupFailure(const std::string& text, const std::string& caption, U32 type) { - destroyContext(); + close(); OSMessageBox(text, caption, type); } @@ -1331,13 +1155,13 @@ bool LLWindowSDL::SDLReallyCaptureInput(bool capture) } bool newGrab = wantGrab; - if (!mFullscreen) /* only bother if we're windowed anyway */ { int result; - if (wantGrab == true) + if (wantGrab) { - result = SDL_CaptureMouse(SDL_TRUE); + // LL_INFOS() << "X11 POINTER GRABBY" << LL_ENDL; + result = SDL_SetWindowMouseGrab(mWindow, true); if (0 == result) newGrab = true; else @@ -1346,9 +1170,10 @@ bool LLWindowSDL::SDLReallyCaptureInput(bool capture) else { newGrab = false; - result = SDL_CaptureMouse(SDL_FALSE); + result = SDL_SetWindowMouseGrab(mWindow, false); } } + // pretend we got what we wanted, when really we don't care. // return boolean success for whether we ended up in the desired state return capture == newGrab; @@ -1395,354 +1220,260 @@ U32 LLWindowSDL::SDLCheckGrabbyKeys(U32 keysym, bool gain) return mGrabbyKeyFlags; } - -void check_vm_bloat() +// virtual +void LLWindowSDL::processMiscNativeEvents() { -#if LL_LINUX - // watch our own VM and RSS sizes, warn if we bloated rapidly - static const std::string STATS_FILE = "/proc/self/stat"; - FILE *fp = fopen(STATS_FILE.c_str(), "r"); - if (fp) { - static long long last_vm_size = 0; - static long long last_rss_size = 0; - const long long significant_vm_difference = 250 * 1024*1024; - const long long significant_rss_difference = 50 * 1024*1024; - long long this_vm_size = 0; - long long this_rss_size = 0; - - ssize_t res; - size_t dummy; - char *ptr = NULL; - for (int i=0; i<22; ++i) // parse past the values we don't want + LLMutexLock osr_lock(&mOSRMutex); + for(SDL_Window* pWindow : mDeadOSRWindows) { - res = getdelim(&ptr, &dummy, ' ', fp); - if (-1 == res) - { - LL_WARNS() << "Unable to parse " << STATS_FILE << LL_ENDL; - goto finally; - } - free(ptr); - ptr = NULL; + SDL_DestroyWindow(pWindow); } - // 23rd space-delimited entry is vsize - res = getdelim(&ptr, &dummy, ' ', fp); - llassert(ptr); - if (-1 == res) + mDeadOSRWindows.clear(); + } +} + +void LLWindowSDL::gatherInput() +{ + // This is for the case where SDL is not driving the main event loop + if(!gSDLMainHandled) + { + SDL_Event event; + + // Handle all outstanding SDL events + while (SDL_PollEvent(&event)) { - LL_WARNS() << "Unable to parse " << STATS_FILE << LL_ENDL; - goto finally; + handleEvent(event); } - this_vm_size = atoll(ptr); - free(ptr); - ptr = NULL; - // 24th space-delimited entry is RSS - res = getdelim(&ptr, &dummy, ' ', fp); - llassert(ptr); - if (-1 == res) + } + + updateCursor(); + + // This is a good time to stop flashing the icon if our mFlashTimer has + // expired. + if (mFlashing && mFlashTimer.hasExpired()) + { + SDL_FlashWindow(mWindow, SDL_FLASH_CANCEL); + mFlashing = false; + } +} + +SDL_AppResult LLWindowSDL::handleEvent(const SDL_Event& event) +{ + switch(event.type) + { + case SDL_EVENT_MOUSE_MOTION: { - LL_WARNS() << "Unable to parse " << STATS_FILE << LL_ENDL; - goto finally; + LLCoordWindow winCoord(llfloor(event.motion.x), llfloor(event.motion.y)); + LLCoordGL openGlCoord; + convertCoords(winCoord, &openGlCoord); + mCallbacks->handleMouseMove(this, openGlCoord, gKeyboard->currentMask(true)); + break; } - this_rss_size = getpagesize() * atoll(ptr); - free(ptr); - ptr = NULL; - LL_INFOS() << "VM SIZE IS NOW " << (this_vm_size/(1024*1024)) << " MB, RSS SIZE IS NOW " << (this_rss_size/(1024*1024)) << " MB" << LL_ENDL; - - if (llabs(last_vm_size - this_vm_size) > - significant_vm_difference) + case SDL_EVENT_MOUSE_WHEEL: { - if (this_vm_size > last_vm_size) + if(event.wheel.integer_y != 0) { - LL_WARNS() << "VM size grew by " << (this_vm_size - last_vm_size)/(1024*1024) << " MB in last frame" << LL_ENDL; + mCallbacks->handleScrollWheel(this, -event.wheel.integer_y); } - else + if (event.wheel.integer_x != 0) { - LL_INFOS() << "VM size shrank by " << (last_vm_size - this_vm_size)/(1024*1024) << " MB in last frame" << LL_ENDL; + mCallbacks->handleScrollHWheel(this, -event.wheel.integer_x); } + break; } - if (llabs(last_rss_size - this_rss_size) > - significant_rss_difference) + case SDL_EVENT_MOUSE_BUTTON_DOWN: { - if (this_rss_size > last_rss_size) + LLCoordWindow winCoord(llfloor(event.button.x), llfloor(event.button.y)); + LLCoordGL openGlCoord; + convertCoords(winCoord, &openGlCoord); + MASK mask = gKeyboard->currentMask(true); + + if (event.button.button == SDL_BUTTON_LEFT) // left { - LL_WARNS() << "RSS size grew by " << (this_rss_size - last_rss_size)/(1024*1024) << " MB in last frame" << LL_ENDL; + if (event.button.clicks == 2) + mCallbacks->handleDoubleClick(this, openGlCoord, mask); + else + mCallbacks->handleMouseDown(this, openGlCoord, mask); + } + else if (event.button.button == SDL_BUTTON_RIGHT) + { + mCallbacks->handleRightMouseDown(this, openGlCoord, mask); + } + else if (event.button.button == SDL_BUTTON_MIDDLE) // middle + { + mCallbacks->handleMiddleMouseDown(this, openGlCoord, mask); } else { - LL_INFOS() << "RSS size shrank by " << (last_rss_size - this_rss_size)/(1024*1024) << " MB in last frame" << LL_ENDL; + mCallbacks->handleOtherMouseDown(this, openGlCoord, mask, event.button.button); } - } - last_rss_size = this_rss_size; - last_vm_size = this_vm_size; - -finally: - if (NULL != ptr) - { - free(ptr); - ptr = NULL; + break; } - fclose(fp); - } -#endif // LL_LINUX -} - - -// virtual -void LLWindowSDL::processMiscNativeEvents() -{ -#if LL_GLIB - // Pump until we've nothing left to do or passed 1/15th of a - // second pumping for this frame. - static LLTimer pump_timer; - pump_timer.reset(); - pump_timer.setTimerExpirySec(1.0f / 15.0f); - do - { - g_main_context_iteration(g_main_context_default(), false); - } while( g_main_context_pending(g_main_context_default()) && !pump_timer.hasExpired()); -#endif - // hack - doesn't belong here - but this is just for debugging - if (getenv("LL_DEBUG_BLOAT")) - { - check_vm_bloat(); - } -} - -void LLWindowSDL::gatherInput() -{ - SDL_Event event; - - // Handle all outstanding SDL events - while (SDL_PollEvent(&event)) - { - switch (event.type) + case SDL_EVENT_MOUSE_BUTTON_UP: { - case SDL_MOUSEWHEEL: + LLCoordWindow winCoord(llfloor(event.button.x), llfloor(event.button.y)); + LLCoordGL openGlCoord; + convertCoords(winCoord, &openGlCoord); + MASK mask = gKeyboard->currentMask(true); + + if (event.button.button == SDL_BUTTON_LEFT) // left { - if( event.wheel.y != 0 ) - { - mCallbacks->handleScrollWheel(this, -event.wheel.y); - } - if (event.wheel.x != 0) - { - mCallbacks->handleScrollHWheel(this, -event.wheel.x); - } - break; + mCallbacks->handleMouseUp(this, openGlCoord, mask); } - - case SDL_MOUSEMOTION: + else if (event.button.button == SDL_BUTTON_RIGHT) // right { - LLCoordWindow winCoord(event.motion.x, event.motion.y); - LLCoordGL openGlCoord; - convertCoords(winCoord, &openGlCoord); - - openGlCoord.mX = openGlCoord.mX * getSystemUISize(); - openGlCoord.mY = openGlCoord.mY * getSystemUISize(); - - //LL_INFOS() << "SDL_MOUSEMOTION " << event.button.x << " " << event.button.y << " gl " << openGlCoord.mX << " " << openGlCoord.mY << LL_ENDL; - - MASK mask = gKeyboard->currentMask(true); - mCallbacks->handleMouseMove(this, openGlCoord, mask); - break; + mCallbacks->handleRightMouseUp(this, openGlCoord, mask); } - - case SDL_TEXTINPUT: + else if (event.button.button == SDL_BUTTON_MIDDLE) // middle { - auto string = utf8str_to_utf16str( event.text.text ); - mKeyModifiers = gKeyboard->currentMask( false ); - mInputType = "textinput"; - for( auto key: string ) - { - mKeyVirtualKey = key; - - if( (MASK_CONTROL|MASK_ALT)&mKeyModifiers ) - gKeyboard->handleKeyDown(mKeyVirtualKey, mKeyModifiers ); - else - handleUnicodeUTF16( key, mKeyModifiers ); - } - break; + mCallbacks->handleMiddleMouseUp(this, openGlCoord, mask); + } + else + { + mCallbacks->handleOtherMouseUp(this, openGlCoord, mask, event.button.button); } - case SDL_KEYDOWN: - mKeyVirtualKey = event.key.keysym.sym; - mKeyModifiers = event.key.keysym.mod; - mInputType = "keydown"; - - // treat all possible Enter/Return keys the same - if (mKeyVirtualKey == SDLK_RETURN2 || mKeyVirtualKey == SDLK_KP_ENTER) - { - mKeyVirtualKey = SDLK_RETURN; - } - - gKeyboard->handleKeyDown(mKeyVirtualKey, mKeyModifiers ); - - // <FS:ND> Slightly hacky :| To make the viewer honor enter (eg to accept form input) we've to not only send handleKeyDown but also send a - // invoke handleUnicodeUTF16 in case the user hits return. - // Note that we cannot blindly use handleUnicodeUTF16 for each SDL_KEYDOWN. Doing so will create bogus keyboard input (like % for cursor left). - if( mKeyVirtualKey == SDLK_RETURN ) - { - // fix return key not working when capslock, scrolllock or numlock are enabled - mKeyModifiers &= (~(KMOD_NUM | KMOD_CAPS | KMOD_MODE | KMOD_SCROLL)); - handleUnicodeUTF16( mKeyVirtualKey, mKeyModifiers ); - } - - // part of the fix for SL-13243 - if (SDLCheckGrabbyKeys(event.key.keysym.sym, true) != 0) - SDLReallyCaptureInput(true); - - break; - - case SDL_KEYUP: - mKeyVirtualKey = event.key.keysym.sym; - mKeyModifiers = event.key.keysym.mod; - mInputType = "keyup"; + break; + } - // treat all possible Enter/Return keys the same - if (mKeyVirtualKey == SDLK_RETURN2 || mKeyVirtualKey == SDLK_KP_ENTER) - { - mKeyVirtualKey = SDLK_RETURN; - } + case SDL_EVENT_KEY_DOWN: + { + mKeyVirtualKey = event.key.key; + mKeyModifiers = event.key.mod; - if (SDLCheckGrabbyKeys(mKeyVirtualKey, false) == 0) - SDLReallyCaptureInput(false); // part of the fix for SL-13243 + if (mKeyVirtualKey == SDLK_RETURN2 || mKeyVirtualKey == SDLK_KP_ENTER) + { + mKeyVirtualKey = SDLK_RETURN; + } - gKeyboard->handleKeyUp(mKeyVirtualKey,mKeyModifiers); - break; + gKeyboard->handleKeyDown(mKeyVirtualKey, mKeyModifiers); - case SDL_MOUSEBUTTONDOWN: + if (mKeyVirtualKey == SDLK_RETURN) { - LLCoordWindow winCoord(event.button.x, event.button.y); - LLCoordGL openGlCoord; - convertCoords(winCoord, &openGlCoord); - - openGlCoord.mX *= getSystemUISize(); - openGlCoord.mY *= getSystemUISize(); + // fix return key not working when capslock, scrolllock or numlock are enabled + mKeyModifiers &= (~(SDL_KMOD_NUM | SDL_KMOD_CAPS | SDL_KMOD_MODE | SDL_KMOD_SCROLL)); + mCallbacks->handleUnicodeChar(mKeyVirtualKey, gKeyboard->currentMask(false)); + } - MASK mask = gKeyboard->currentMask(true); + // part of the fix for SL-13243 + if (SDLCheckGrabbyKeys(mKeyVirtualKey, true) != 0) + SDLReallyCaptureInput(true); + break; + } - if (event.button.button == SDL_BUTTON_LEFT) // left - { - if (event.button.clicks >= 2) - mCallbacks->handleDoubleClick(this, openGlCoord, mask); - else - mCallbacks->handleMouseDown(this, openGlCoord, mask); - } - else if (event.button.button == SDL_BUTTON_RIGHT) // right - { - mCallbacks->handleRightMouseDown(this, openGlCoord, mask); - } - else if (event.button.button == SDL_BUTTON_MIDDLE) // middle - { - mCallbacks->handleMiddleMouseDown(this, openGlCoord, mask); - } - else - { - mCallbacks->handleOtherMouseDown(this, openGlCoord, mask, event.button.button); - } + case SDL_EVENT_KEY_UP: + { + mKeyVirtualKey = event.key.key; + mKeyModifiers = event.key.mod; - break; + if (mKeyVirtualKey == SDLK_RETURN2 || mKeyVirtualKey == SDLK_KP_ENTER) + { + mKeyVirtualKey = SDLK_RETURN; } - case SDL_MOUSEBUTTONUP: - { - LLCoordWindow winCoord(event.button.x, event.button.y); - LLCoordGL openGlCoord; - convertCoords(winCoord, &openGlCoord); + gKeyboard->handleKeyUp(mKeyVirtualKey, mKeyModifiers); + if (SDLCheckGrabbyKeys(mKeyVirtualKey, false) == 0) + SDLReallyCaptureInput(false); // part of the fix for SL-13243 + break; + } - openGlCoord.mX *= getSystemUISize(); - openGlCoord.mY *= getSystemUISize(); + case SDL_EVENT_TEXT_INPUT: + { + auto string = utf8str_to_wstring(event.text.text); + MASK current_mask = gKeyboard->currentMask(false); + for (auto key : string) + { + mKeyVirtualKey = key; + mCallbacks->handleUnicodeChar(key, current_mask); + } + break; + } - MASK mask = gKeyboard->currentMask(true); + case SDL_EVENT_WINDOW_EXPOSED: + { + mCallbacks->handlePaint(this, 0, 0, 0, 0); + break; + } - if (event.button.button == SDL_BUTTON_LEFT) // left - { - mCallbacks->handleMouseUp(this, openGlCoord, mask); - } - else if (event.button.button == SDL_BUTTON_RIGHT) // right - { - mCallbacks->handleRightMouseUp(this, openGlCoord, mask); - } - else if (event.button.button == SDL_BUTTON_MIDDLE) // middle - { - mCallbacks->handleMiddleMouseUp(this, openGlCoord, mask); - } - else - { - mCallbacks->handleOtherMouseUp(this, openGlCoord, mask, event.button.button); - } + case SDL_EVENT_WINDOW_RESIZED: + { + LL_INFOS() << "Handling a resize event: " << event.window.data1 << "x" << event.window.data2 << LL_ENDL; + F32 pixel_density = SDL_GetWindowPixelDensity(mWindow); + S32 width = llmax(event.window.data1, (S32)mMinWindowWidth) * pixel_density; + S32 height = llmax(event.window.data2, (S32)mMinWindowHeight) * pixel_density; - break; + mCallbacks->handleResize(this, width, height); + break; + } + case SDL_EVENT_WINDOW_MOUSE_ENTER: + break; + case SDL_EVENT_WINDOW_MOUSE_LEAVE: + mCallbacks->handleMouseLeave(this); + break; + case SDL_EVENT_WINDOW_FOCUS_GAINED: + //SDL_SetWindowKeyboardGrab(mWindow, true); + mCallbacks->handleFocus(this); + break; + case SDL_EVENT_WINDOW_FOCUS_LOST: + mCallbacks->handleFocusLost(this); + //SDL_SetWindowKeyboardGrab(mWindow, false); + break; + case SDL_EVENT_WINDOW_RESTORED: + mCallbacks->handleActivate(this, true); + break; + case SDL_EVENT_WINDOW_MAXIMIZED: + mCallbacks->handleActivate(this, true); + break; + case SDL_EVENT_WINDOW_MINIMIZED: + mCallbacks->handleActivate(this, false); + break; + case SDL_EVENT_WINDOW_DISPLAY_CHANGED: + { + // Update refresh rate when changing monitors + const SDL_DisplayMode* displayMode = SDL_GetCurrentDisplayMode(event.window.data1); + if(displayMode) + { + mRefreshRate = ll_round(displayMode->refresh_rate); + mNativeAspectRatio = ((F32)displayMode->w) / ((F32)displayMode->h); } - - case SDL_WINDOWEVENT: + mCallbacks->handleDisplayChanged(); + break; + } + case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: + { + S32 w, h = 0; + SDL_GetWindowSizeInPixels(mWindow, &w, &h); + mCallbacks->handleDPIChanged(this, getSystemUISize(), w, h); + break; + } + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: + { + if(mCallbacks->handleCloseRequest(this, true)) { - switch(event.window.event) - { - //case SDL_WINDOWEVENT_SIZE_CHANGED: <FS:ND> SDL_WINDOWEVENT_SIZE_CHANGED is followed by SDL_WINDOWEVENT_RESIZED, so handling one shall be enough - case SDL_WINDOWEVENT_RESIZED: - { - LL_INFOS() << "Handling a resize event: " << event.window.data1 << "x" << event.window.data2 << LL_ENDL; - S32 width = llmax(event.window.data1, (S32)mMinWindowWidth); - S32 height = llmax(event.window.data2, (S32)mMinWindowHeight); - - mCallbacks->handleResize(this, width * getSystemUISize(), height * getSystemUISize()); - break; - } - case SDL_WINDOWEVENT_LEAVE: - mCallbacks->handleMouseLeave(this); - break; - case SDL_WINDOWEVENT_FOCUS_GAINED: - mCallbacks->handleFocus(this); - break; - case SDL_WINDOWEVENT_FOCUS_LOST: - mCallbacks->handleFocusLost(this); - break; - case SDL_WINDOWEVENT_EXPOSED: - case SDL_WINDOWEVENT_SHOWN: - case SDL_WINDOWEVENT_HIDDEN: - case SDL_WINDOWEVENT_MINIMIZED: - case SDL_WINDOWEVENT_MAXIMIZED: - case SDL_WINDOWEVENT_RESTORED: - { - Uint32 flags = SDL_GetWindowFlags(mWindow); - bool minimized = (flags & SDL_WINDOW_MINIMIZED); - bool hidden = (flags & SDL_WINDOW_HIDDEN); - - mCallbacks->handleActivate(this, !minimized || !hidden); - LL_INFOS() << "SDL deiconification state switched to " << minimized << LL_ENDL; - break; - } - } - break; + // Get the app to initiate cleanup. + mCallbacks->handleQuit(this); + // The app is responsible for calling destroyWindow when done with GL } - case SDL_QUIT: - if(mCallbacks->handleCloseRequest(this)) - { - // Get the app to initiate cleanup. - mCallbacks->handleQuit(this); - // The app is responsible for calling destroyWindow when done with GL - } - break; - default: - //LL_INFOS() << "Unhandled SDL event type " << event.type << LL_ENDL; - break; + break; } + default: + break; } - updateCursor(); + return SDL_APP_CONTINUE; +} - // This is a good time to stop flashing the icon if our mFlashTimer has - // expired. - if (mFlashing && mFlashTimer.hasExpired()) - { - SDL_FlashWindow(mWindow, SDL_FLASH_CANCEL); - mFlashing = false; - } +// static +SDL_AppResult LLWindowSDL::handleEvents(const SDL_Event& event) +{ + if(!gWindowImplementation) return SDL_APP_CONTINUE; + + return gWindowImplementation->handleEvent(event); } static SDL_Cursor *makeSDLCursorFromBMP(const char *filename, int hotx, int hoty) @@ -1757,20 +1488,31 @@ static SDL_Cursor *makeSDLCursorFromBMP(const char *filename, int hotx, int hoty SDL_Surface *cursurface; LL_DEBUGS() << "Loaded cursor file " << filename << " " << bmpsurface->w << "x" << bmpsurface->h << LL_ENDL; - cursurface = SDL_CreateRGBSurface (SDL_SWSURFACE, - bmpsurface->w, + SDL_PixelFormat pix_format = SDL_GetPixelFormatForMasks(32, + SDL_Swap32LE(0xFFU), + SDL_Swap32LE(0xFF00U), + SDL_Swap32LE(0xFF0000U), + SDL_Swap32LE(0xFF000000U)); + if (pix_format == SDL_PIXELFORMAT_UNKNOWN) + { + return nullptr; + } + + const SDL_PixelFormatDetails* pix_format_details = SDL_GetPixelFormatDetails(pix_format); + if(!pix_format_details) + { + return nullptr; + } + + cursurface = SDL_CreateSurface(bmpsurface->w, bmpsurface->h, - 32, - SDL_SwapLE32(0xFFU), - SDL_SwapLE32(0xFF00U), - SDL_SwapLE32(0xFF0000U), - SDL_SwapLE32(0xFF000000U)); - SDL_FillRect(cursurface, NULL, SDL_SwapLE32(0x00000000U)); + pix_format); + SDL_FillSurfaceRect(cursurface, nullptr, SDL_Swap32LE(0x00000000U)); // Blit the cursor pixel data onto a 32-bit RGBA surface so we // only have to cope with processing one type of pixel format. - if (0 == SDL_BlitSurface(bmpsurface, NULL, - cursurface, NULL)) + if (SDL_BlitSurface(bmpsurface, nullptr, + cursurface, nullptr)) { // n.b. we already checked that width is a multiple of 8. const int bitmap_bytes = (cursurface->w * cursurface->h) / 8; @@ -1787,7 +1529,7 @@ static SDL_Cursor *makeSDLCursorFromBMP(const char *filename, int hotx, int hoty U8 *pixelp = ((U8*)cursurface->pixels) + cursurface->pitch * i - + j*cursurface->format->BytesPerPixel; + + j*pix_format_details->bytes_per_pixel; U8 srcred = pixelp[0]; U8 srcgreen = pixelp[1]; U8 srcblue = pixelp[2]; @@ -1810,8 +1552,8 @@ static SDL_Cursor *makeSDLCursorFromBMP(const char *filename, int hotx, int hoty } else { LL_WARNS() << "CURSOR BLIT FAILURE, cursurface: " << cursurface << LL_ENDL; } - SDL_FreeSurface(cursurface); - SDL_FreeSurface(bmpsurface); + SDL_DestroySurface(cursurface); + SDL_DestroySurface(bmpsurface); } else { LL_WARNS() << "CURSOR LOAD FAILURE " << filename << LL_ENDL; } @@ -1844,28 +1586,28 @@ void LLWindowSDL::updateCursor() void LLWindowSDL::initCursors() { - int i; // Blank the cursor pointer array for those we may miss. - for (i=0; i<UI_CURSOR_COUNT; ++i) + for (int i=0; i<UI_CURSOR_COUNT; ++i) { mSDLCursors[i] = nullptr; } + // Pre-make an SDL cursor for each of the known cursor types. // We hardcode the hotspots - to avoid that we'd have to write // a .cur file loader. // NOTE: SDL doesn't load RLE-compressed BMP files. - mSDLCursors[UI_CURSOR_ARROW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); + mSDLCursors[UI_CURSOR_ARROW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT); mSDLCursors[UI_CURSOR_WAIT] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT); - mSDLCursors[UI_CURSOR_HAND] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); - mSDLCursors[UI_CURSOR_IBEAM] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); + mSDLCursors[UI_CURSOR_HAND] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER); + mSDLCursors[UI_CURSOR_IBEAM] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_TEXT); mSDLCursors[UI_CURSOR_CROSS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR); - mSDLCursors[UI_CURSOR_SIZENWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE); - mSDLCursors[UI_CURSOR_SIZENESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW); - mSDLCursors[UI_CURSOR_SIZEWE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); - mSDLCursors[UI_CURSOR_SIZENS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); - mSDLCursors[UI_CURSOR_SIZEALL] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); - mSDLCursors[UI_CURSOR_NO] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO); - mSDLCursors[UI_CURSOR_WORKING] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAITARROW); + mSDLCursors[UI_CURSOR_SIZENWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NWSE_RESIZE); + mSDLCursors[UI_CURSOR_SIZENESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NESW_RESIZE); + mSDLCursors[UI_CURSOR_SIZEWE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_EW_RESIZE); + mSDLCursors[UI_CURSOR_SIZENS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NS_RESIZE); + mSDLCursors[UI_CURSOR_SIZEALL] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE); + mSDLCursors[UI_CURSOR_NO] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NOT_ALLOWED); + mSDLCursors[UI_CURSOR_WORKING] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_PROGRESS); mSDLCursors[UI_CURSOR_TOOLGRAB] = makeSDLCursorFromBMP("lltoolgrab.BMP",2,13); mSDLCursors[UI_CURSOR_TOOLLAND] = makeSDLCursorFromBMP("lltoolland.BMP",1,6); mSDLCursors[UI_CURSOR_TOOLFOCUS] = makeSDLCursorFromBMP("lltoolfocus.BMP",8,5); @@ -1902,23 +1644,24 @@ void LLWindowSDL::initCursors() void LLWindowSDL::quitCursors() { - int i; if (mWindow) { - for (i=0; i<UI_CURSOR_COUNT; ++i) + for (int i=0; i<UI_CURSOR_COUNT; ++i) { if (mSDLCursors[i]) { - SDL_FreeCursor(mSDLCursors[i]); - mSDLCursors[i] = NULL; + SDL_DestroyCursor(mSDLCursors[i]); + mSDLCursors[i] = nullptr; } } - } else { + } + else + { // SDL doesn't refcount cursors, so if the window has // already been destroyed then the cursors have gone with it. LL_INFOS() << "Skipping quitCursors: mWindow already gone." << LL_ENDL; - for (i=0; i<UI_CURSOR_COUNT; ++i) - mSDLCursors[i] = NULL; + for (int i=0; i<UI_CURSOR_COUNT; ++i) + mSDLCursors[i] = nullptr; } } @@ -1947,7 +1690,7 @@ void LLWindowSDL::hideCursor() // LL_INFOS() << "hideCursor: hiding" << LL_ENDL; mCursorHidden = true; mHideCursorPermanent = true; - SDL_ShowCursor(SDL_DISABLE); + SDL_HideCursor(); } else { @@ -1962,7 +1705,7 @@ void LLWindowSDL::showCursor() // LL_INFOS() << "showCursor: showing" << LL_ENDL; mCursorHidden = false; mHideCursorPermanent = false; - SDL_ShowCursor(SDL_ENABLE); + SDL_ShowCursor(); } else { @@ -2038,37 +1781,28 @@ S32 OSMessageBoxSDL(const std::string& text, const std::string& caption, U32 typ break; } + if(gWindowImplementation != nullptr) + gWindowImplementation->beforeDialog(); + int btn{0}; - if( 0 == SDL_ShowMessageBox( &oData, &btn ) ) + if(SDL_ShowMessageBox( &oData, &btn )) + { + if(gWindowImplementation != nullptr) + gWindowImplementation->afterDialog(); return btn; + } + + if(gWindowImplementation != nullptr) + gWindowImplementation->afterDialog(); + return OSBTN_CANCEL; } bool LLWindowSDL::dialogColorPicker( F32 *r, F32 *g, F32 *b) { - return (false); + return false; } -#if LL_DARWIN -LLSD LLWindowSDL::getNativeKeyData() -{ - LLSD result = LLSD::emptyMap(); - - if(mRawKeyEvent) - { - result["event_type"] = LLSD::Integer(mRawKeyEvent->mEventType); - result["event_modifiers"] = LLSD::Integer(mRawKeyEvent->mEventModifiers); - result["event_keycode"] = LLSD::Integer(mRawKeyEvent->mEventKeyCode); - result["event_chars"] = (mRawKeyEvent->mEventChars) ? LLSD(LLSD::Integer(mRawKeyEvent->mEventChars)) : LLSD(); - result["event_umodchars"] = (mRawKeyEvent->mEventUnmodChars) ? LLSD(LLSD::Integer(mRawKeyEvent->mEventUnmodChars)) : LLSD(); - result["event_isrepeat"] = LLSD::Boolean(mRawKeyEvent->mEventRepeat); - } - - LL_DEBUGS() << "native key data is: " << result << LL_ENDL; - - return result; -} -#else /* Make the raw keyboard data available - used to poke through to LLQtWebKit so that Qt/Webkit has access to the virtual keycodes etc. that it needs @@ -2083,23 +1817,21 @@ LLSD LLWindowSDL::getNativeKeyData() // what a plugin under GDK under Qt under SL under SDL under X11 considers // a 'native' modifier mask. this has been sort of reverse-engineered... they *appear* // to match GDK consts, but that may be co-incidence. - modifiers |= (mKeyModifiers & KMOD_LSHIFT) ? 0x0001 : 0; - modifiers |= (mKeyModifiers & KMOD_RSHIFT) ? 0x0001 : 0;// munge these into the same shift - modifiers |= (mKeyModifiers & KMOD_CAPS) ? 0x0002 : 0; - modifiers |= (mKeyModifiers & KMOD_LCTRL) ? 0x0004 : 0; - modifiers |= (mKeyModifiers & KMOD_RCTRL) ? 0x0004 : 0;// munge these into the same ctrl - modifiers |= (mKeyModifiers & KMOD_LALT) ? 0x0008 : 0;// untested - modifiers |= (mKeyModifiers & KMOD_RALT) ? 0x0008 : 0;// untested + modifiers |= (mKeyModifiers & SDL_KMOD_LSHIFT) ? 0x0001 : 0; + modifiers |= (mKeyModifiers & SDL_KMOD_RSHIFT) ? 0x0001 : 0;// munge these into the same shift + modifiers |= (mKeyModifiers & SDL_KMOD_CAPS) ? 0x0002 : 0; + modifiers |= (mKeyModifiers & SDL_KMOD_LCTRL) ? 0x0004 : 0; + modifiers |= (mKeyModifiers & SDL_KMOD_RCTRL) ? 0x0004 : 0;// munge these into the same ctrl + modifiers |= (mKeyModifiers & SDL_KMOD_LALT) ? 0x0008 : 0;// untested + modifiers |= (mKeyModifiers & SDL_KMOD_RALT) ? 0x0008 : 0;// untested // *todo: test ALTs - I don't have a case for testing these. Do you? // *todo: NUM? - I don't care enough right now (and it's not a GDK modifier). result["virtual_key"] = (S32)mKeyVirtualKey; - result["virtual_key_win"] = (S32)LLKeyboardSDL::mapSDL2toWin( mKeyVirtualKey ); + result["virtual_key_win"] = (S32)LLKeyboardSDL::mapSDLtoWin( mKeyVirtualKey ); result["modifiers"] = (S32)modifiers; - result["input_type"] = mInputType; return result; } -#endif // LL_DARWIN // Open a URL with the user's default web browser. // Must begin with protocol identifier. @@ -2124,7 +1856,7 @@ void LLWindowSDL::spawnWebBrowser(const std::string& escaped_url, bool async) LL_INFOS() << "spawn_web_browser: " << escaped_url << LL_ENDL; - if (SDL_OpenURL(escaped_url.c_str()) != 0) + if (!SDL_OpenURL(escaped_url.c_str())) { LL_WARNS() << "spawn_web_browser failed with error: " << SDL_GetError() << LL_ENDL; } @@ -2132,9 +1864,18 @@ void LLWindowSDL::spawnWebBrowser(const std::string& escaped_url, bool async) LL_INFOS() << "spawn_web_browser returning." << LL_ENDL; } -void *LLWindowSDL::getPlatformWindow() +void* LLWindowSDL::getPlatformWindow() { - return NULL; + void* ret = nullptr; + if (mWindow) + { +#if LL_WINDOWS + ret = SDL_GetPointerProperty(SDL_GetWindowProperties(mWindow), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr); +#elif LL_DARWIN + ret = SDL_GetPointerProperty(SDL_GetWindowProperties(mWindow), SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr); +#endif + } + return ret; } void LLWindowSDL::bringToFront() @@ -2151,102 +1892,64 @@ void LLWindowSDL::bringToFront() //static std::vector<std::string> LLWindowSDL::getDynamicFallbackFontList() { - // Use libfontconfig to find us a nice ordered list of fallback fonts - // specific to this system. - std::string final_fallback("/usr/share/fonts/truetype/kochi/kochi-gothic.ttf"); - const int max_font_count_cutoff = 40; // fonts are expensive in the current system, don't enumerate an arbitrary number of them - // Our 'ideal' font properties which define the sorting results. - // slant=0 means Roman, index=0 means the first face in a font file - // (the one we actually use), weight=80 means medium weight, - // spacing=0 means proportional spacing. - std::string sort_order("slant=0:index=0:weight=80:spacing=0"); - // elide_unicode_coverage removes fonts from the list whose unicode - // range is covered by fonts earlier in the list. This usually - // removes ~90% of the fonts as redundant (which is great because - // the font list can be huge), but might unnecessarily reduce the - // renderable range if for some reason our FreeType actually fails - // to use some of the fonts we want it to. - const bool elide_unicode_coverage = true; - std::vector<std::string> rtns; - FcFontSet *fs = NULL; - FcPattern *sortpat = NULL; - - LL_INFOS() << "Getting system font list from FontConfig..." << LL_ENDL; + // Lazy-loaded fonts, seeded with fonts.xml + return std::vector<std::string>(); +} - // If the user has a system-wide language preference, then favor - // fonts from that language group. This doesn't affect the types - // of languages that can be displayed, but ensures that their - // preferred language is rendered from a single consistent font where - // possible. - FL_Locale *locale = NULL; - FL_Success success = FL_FindLocale(&locale, FL_MESSAGES); - if (success != 0) +LLFontFallbackMatch LLWindowSDL::findFallbackFontForChar(llwchar wch) +{ + LLFontFallbackMatch result; +#if LL_LINUX || __FreeBSD__ + // FcInit() is idempotent, but this runs per missing codepoint, so only + // attempt initialization once. + static bool fc_ready = FcInit(); + if (!fc_ready) { - if (success >= 2 && locale->lang) // confident! - { - LL_INFOS("AppInit") << "Language " << locale->lang << LL_ENDL; - LL_INFOS("AppInit") << "Location " << locale->country << LL_ENDL; - LL_INFOS("AppInit") << "Variant " << locale->variant << LL_ENDL; - - LL_INFOS() << "Preferring fonts of language: " - << locale->lang - << LL_ENDL; - sort_order = "lang=" + std::string(locale->lang) + ":" - + sort_order; - } + LL_WARNS_ONCE() << "FontConfig failed to initialize." << LL_ENDL; + return result; } - FL_FreeLocale(&locale); - if (!FcInit()) + // Ask FontConfig for the best font covering this codepoint. + FcCharSet* charset = FcCharSetCreate(); + FcPattern* pat = FcPatternCreate(); + if (!charset || !pat) { - LL_WARNS() << "FontConfig failed to initialize." << LL_ENDL; - rtns.push_back(final_fallback); - return rtns; + if (charset) FcCharSetDestroy(charset); + if (pat) FcPatternDestroy(pat); + return result; } - sortpat = FcNameParse((FcChar8*) sort_order.c_str()); - if (sortpat) - { - // Sort the list of system fonts from most-to-least-desirable. - FcResult result; - fs = FcFontSort(NULL, sortpat, elide_unicode_coverage, - NULL, &result); - FcPatternDestroy(sortpat); - } + FcCharSetAddChar(charset, (FcChar32)wch); + + FcPatternAddCharSet(pat, FC_CHARSET, charset); + FcPatternAddBool(pat, FC_SCALABLE, FcTrue); + + FcConfigSubstitute(nullptr, pat, FcMatchPattern); + FcDefaultSubstitute(pat); - int found_font_count = 0; - if (fs) + FcResult fc_result; + FcPattern* match = FcFontMatch(nullptr, pat, &fc_result); + if (match) { - // Get the full pathnames to the fonts, where available, - // which is what we really want. - found_font_count = fs->nfont; - for (int i=0; i<fs->nfont; ++i) + FcChar8* filename = nullptr; + if (FcResultMatch == FcPatternGetString(match, FC_FILE, 0, &filename) && filename) { - FcChar8 *filename; - if (FcResultMatch == FcPatternGetString(fs->fonts[i], - FC_FILE, 0, - &filename) - && filename) + result.mPath = (const char*)filename; + + // .ttc/.otc collections carry several faces; get the right one. + int index = 0; + if (FcResultMatch == FcPatternGetInteger(match, FC_INDEX, 0, &index)) { - rtns.push_back(std::string((const char*)filename)); - if (rtns.size() >= max_font_count_cutoff) - break; // hit limit + result.mFaceIndex = index; } } - FcFontSetDestroy (fs); + FcPatternDestroy(match); } - LL_DEBUGS() << "Using font list: " << LL_ENDL; - for (std::vector<std::string>::iterator it = rtns.begin(); - it != rtns.end(); - ++it) - { - LL_DEBUGS() << " file: " << *it << LL_ENDL; - } - LL_INFOS() << "Using " << rtns.size() << "/" << found_font_count << " system fonts." << LL_ENDL; - - rtns.push_back(final_fallback); - return rtns; + FcPatternDestroy(pat); + FcCharSetDestroy(charset); +#endif + return result; } void LLWindowSDL::setLanguageTextInput(const LLCoordGL& position) @@ -2260,5 +1963,78 @@ void LLWindowSDL::setLanguageTextInput(const LLCoordGL& position) r.w = 500; r.h = 16; - SDL_SetTextInputRect(&r); + SDL_SetTextInputArea(mWindow, &r, 0); +} + +F32 LLWindowSDL::getSystemUISize() +{ + if(mWindow) + { + F32 scale = SDL_GetWindowDisplayScale(mWindow); + if (scale > 0.0f) + { + return scale; + } + } + return 1.f; } + +#if LL_DARWIN +// static +U64 LLWindowSDL::getVramSize() +{ + CGLRendererInfoObj info = 0; + GLint vram_megabytes = 0; + int num_renderers = 0; + CGLError the_err = CGLQueryRendererInfo (CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay), &info, &num_renderers); + if(0 == the_err) + { + // The name, uses, and other platform definitions of gGLManager.mVRAM suggest that this is supposed to be total vram in MB, + // rather than, say, just the texture memory. The two exceptions are: + // 1. LLAppViewer::getViewerInfo() puts the value in a field labeled "TEXTURE_MEMORY" + // 2. For years, this present function used kCGLRPTextureMemoryMegabytes + // Now we use kCGLRPVideoMemoryMegabytes to bring it in line with everything else (except thatone label). + CGLDescribeRenderer (info, 0, kCGLRPVideoMemoryMegabytes, &vram_megabytes); + CGLDestroyRendererInfo (info); + } + else + { + vram_megabytes = 256; + } + + return (U64)vram_megabytes; // return value is in megabytes. +} + +//static +void LLWindowSDL::setUseMultGL(bool use_mult_gl) +{ + bool was_enabled = sUseMultGL; + + sUseMultGL = use_mult_gl; + + if (gGLManager.mInited) + { + CGLContextObj ctx = CGLGetCurrentContext(); + //enable multi-threaded OpenGL (whether or not sUseMultGL actually changed) + if (sUseMultGL) + { + CGLError cgl_err = CGLEnable( ctx, kCGLCEMPEngine); + if (cgl_err != kCGLNoError ) + { + LL_INFOS("GLInit") << "Multi-threaded OpenGL not available." << LL_ENDL; + sUseMultGL = false; + } + else + { + LL_INFOS("GLInit") << "Multi-threaded OpenGL enabled." << LL_ENDL; + } + } + else if (was_enabled) + { + CGLDisable( ctx, kCGLCEMPEngine); + LL_INFOS("GLInit") << "Multi-threaded OpenGL disabled." << LL_ENDL; + } + } +} +#endif + diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h index 144216f658..7156ad66b7 100644 --- a/indra/llwindow/llwindowsdl.h +++ b/indra/llwindow/llwindowsdl.h @@ -24,37 +24,35 @@ * $/LicenseInfo$ */ -#ifndef LL_LLWINDOWSDL2_H -#define LL_LLWINDOWSDL2_H +#ifndef LL_LLWINDOWSDL_H +#define LL_LLWINDOWSDL_H // Simple Directmedia Layer (http://libsdl.org/) implementation of LLWindow class #include "llwindow.h" #include "lltimer.h" +#include "llmutex.h" -#if !defined(__i386__) && !defined(__x86_64__) -#define SDL_DISABLE_IMMINTRIN_H +#include "SDL3/SDL.h" +#include "SDL3/SDL_endian.h" + +#ifdef LL_WAYLAND +#include <wayland-client-protocol.h> #endif -#include "SDL2/SDL.h" -#include "SDL2/SDL_video.h" -#include "SDL2/SDL_endian.h" #if LL_X11 -// get X11-specific headers for use in low-level stuff like copy-and-paste support -#include "SDL2/SDL_syswm.h" +#include <X11/Xlib.h> +#include <X11/Xutil.h> #endif -// AssertMacros.h does bad things. -#include "fix_macros.h" -#undef verify -#undef require - +class LLPreeditor; -class LLWindowSDL : public LLWindow { +class LLWindowSDL final : public LLWindow +{ public: void show() override; - void hide() override; + void restore() override; void close() override; @@ -65,23 +63,16 @@ public: bool getMaximized() override; bool maximize() override; - void minimize() override; - void restore() override; - - bool getFullscreen(); - bool getPosition(LLCoordScreen *position) override; bool getSize(LLCoordScreen *size) override; - bool getSize(LLCoordWindow *size) override; bool setPosition(LLCoordScreen position) override; bool setSizeImpl(LLCoordScreen size) override; - bool setSizeImpl(LLCoordWindow size) override; bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, @@ -90,86 +81,67 @@ public: bool setCursorPosition(LLCoordWindow position) override; bool getCursorPosition(LLCoordWindow *position) override; - + bool isWrapMouse() const override { return true; } void showCursor() override; - void hideCursor() override; + bool isCursorHidden() override; void showCursorFromMouseMove() override; - void hideCursorUntilMouseMove() override; - bool isCursorHidden() override; - void updateCursor() override; void captureMouse() override; - void releaseMouse() override; void setMouseClipping(bool b) override; - void setMinSize(U32 min_width, U32 min_height, bool enforce_immediately = true) override; + void setMinSize(U32 min_width, U32 min_height, bool enforce_immediately = true) override; bool isClipboardTextAvailable() override; - bool pasteTextFromClipboard(LLWString &dst) override; - bool copyTextToClipboard(const LLWString &src) override; bool isPrimaryTextAvailable() override; - bool pasteTextFromPrimary(LLWString &dst) override; - bool copyTextToPrimary(const LLWString &src) override; void flashIcon(F32 seconds) override; + void maybeStopFlashIcon(); F32 getGamma() override; - bool setGamma(const F32 gamma) override; // Set the gamma - U32 getFSAASamples() override; + bool restoreGamma() override; // Restore original gamma table (before updating gamma) + U32 getFSAASamples() override; void setFSAASamples(const U32 samples) override; - bool restoreGamma() override; // Restore original gamma table (before updating gamma) - ESwapMethod getSwapMethod() override { return mSwapMethod; } - void processMiscNativeEvents() override; void gatherInput() override; - void swapBuffers() override; + SDL_AppResult handleEvent(const SDL_Event& event); + static SDL_AppResult handleEvents(const SDL_Event& event); - void restoreGLContext() {}; + void swapBuffers() override; void delayInputProcessing() override {}; // handy coordinate space conversion routines bool convertCoords(LLCoordScreen from, LLCoordWindow *to) override; - bool convertCoords(LLCoordWindow from, LLCoordScreen *to) override; - bool convertCoords(LLCoordWindow from, LLCoordGL *to) override; - bool convertCoords(LLCoordGL from, LLCoordWindow *to) override; - bool convertCoords(LLCoordScreen from, LLCoordGL *to) override; - bool convertCoords(LLCoordGL from, LLCoordScreen *to) override; LLWindowResolution *getSupportedResolutions(S32 &num_resolutions) override; F32 getNativeAspectRatio() override; - F32 getPixelAspectRatio() override; - void setNativeAspectRatio(F32 ratio) override { mOverrideAspectRatio = ratio; } - F32 getSystemUISize() override; - void beforeDialog() override; - void afterDialog() override; bool dialogColorPicker(F32 *r, F32 *g, F32 *b) override; @@ -185,38 +157,24 @@ public: void setTitle(const std::string title) override; static std::vector<std::string> getDynamicFallbackFontList(); + static LLFontFallbackMatch findFallbackFontForChar(llwchar wch); - // Not great that these are public, but they have to be accessible - // by non-class code and it's better than making them global. -#if LL_X11 - Window mSDL_XWindowID; - Display *mSDL_Display; -#endif - - void (*Lock_Display)(void); - - void (*Unlock_Display)(void); - -#if LL_X11 - - static Window get_SDL_XWindowID(void); + void *createSharedContext() override; + void makeContextCurrent(void *context) override; + void destroySharedContext(void *context) override; + void toggleVSync(bool enable_vsync) override; - static Display *get_SDL_Display(void); + F32 getSystemUISize() override; -#endif // LL_X11 + static std::vector<std::string> getDisplaysResolutionList(); #if LL_DARWIN + static U64 getVramSize(); + static void setUseMultGL(bool use_mult_gl); + static bool sUseMultGL; #endif - void *createSharedContext() override; - - void makeContextCurrent(void *context) override; - - void destroySharedContext(void *context) override; - - void toggleVSync(bool enable_vsync) override; - protected: LLWindowSDL(LLWindowCallbacks *callbacks, const std::string &title, const std::string& name, int x, int y, int width, int height, U32 flags, @@ -230,19 +188,8 @@ protected: LLSD getNativeKeyData() override; void initCursors(); - void quitCursors(); - void moveWindow(const LLCoordScreen &position, const LLCoordScreen &size); - - // Changes display resolution. Returns true if successful - bool setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh); - - // Go back to last fullscreen display resolution. - bool setFullscreenResolution(); - - bool shouldPostQuit() { return mPostQuit; } - protected: // // Platform specific methods @@ -250,50 +197,65 @@ protected: // create or re-create the GL context/window. Called from the constructor and switchContext(). bool createContext(int x, int y, int width, int height, int bits, bool fullscreen, bool enable_vsync); - void destroyContext(); void setupFailure(const std::string &text, const std::string &caption, U32 type); - U32 SDLCheckGrabbyKeys(U32 keysym, bool gain); - bool SDLReallyCaptureInput(bool capture); + U32 SDLCheckGrabbyKeys(U32 keysym, bool gain); // // Platform specific variables // - U32 mGrabbyKeyFlags; - int mReallyCapturedCount; - - SDL_Window *mWindow; + U32 mGrabbyKeyFlags = 0; + S32 mReallyCapturedCount = 0; + SDL_Window *mWindow = nullptr; SDL_GLContext mContext; SDL_Cursor *mSDLCursors[UI_CURSOR_COUNT]; std::string mWindowTitle; - double mOriginalAspectRatio; - bool mNeedsResize; // Constructor figured out the window is too big, it needs a resize. - LLCoordScreen mNeedsResizeSize; - F32 mOverrideAspectRatio; - F32 mGamma; - U32 mFSAASamples; - - int mSDLFlags; - - int mHaveInputFocus; /* 0=no, 1=yes, else unknown */ - int mIsMinimized; /* 0=no, 1=yes, else unknown */ + F32 mNativeAspectRatio = 0.0f; + F32 mOverrideAspectRatio = 0.0f; + F32 mGamma = 0.0f; + U32 mFSAASamples = 0; friend class LLWindowManager; private: - bool mFlashing; + bool mFlashing = false; LLTimer mFlashTimer; - U32 mKeyVirtualKey; - U32 mKeyModifiers; - std::string mInputType; + U32 mKeyVirtualKey = 0; + U32 mKeyModifiers = SDL_KMOD_NONE; + LLMutex mOSRMutex; + std::unordered_map<SDL_GLContext, SDL_Window*> mOSRContexts; + std::list<SDL_Window*> mDeadOSRWindows; -private: void tryFindFullscreenSize(int &aWidth, int &aHeight); + + enum EServerProtocol{ X11, Wayland, Unknown }; + EServerProtocol mServerProtocol = Unknown; +public: +#if LL_X11 + // X11 + struct X11_DATA + { + Display* xdisplay = nullptr; + Window xwindow = 0; + int xscreen = -1; + }; + static X11_DATA sX11Data; +#endif + +#if LL_WAYLAND + // Wayland + struct WAYLAND_DATA + { + struct wl_display* display = nullptr; + struct wl_surface* surface = nullptr; + }; + static WAYLAND_DATA sWaylandData; +#endif }; class LLSplashScreenSDL : public LLSplashScreen @@ -302,9 +264,9 @@ public: LLSplashScreenSDL(); virtual ~LLSplashScreenSDL(); - void showImpl(); - void updateImpl(const std::string& mesg); - void hideImpl(); + void showImpl() override; + void updateImpl(const std::string& mesg) override; + void hideImpl() override; }; S32 OSMessageBoxSDL(const std::string& text, const std::string& caption, U32 type); diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 4fca74497f..ab857fa58d 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -49,6 +49,7 @@ #include "llthreadsafequeue.h" #include "stringize.h" #include "llframetimer.h" +#include "llwatchdog.h" // System includes #include <commdlg.h> @@ -64,6 +65,7 @@ #include <utility> // std::pair #include <d3d9.h> +#include <d3d11.h> #include <dxgi1_4.h> #include <timeapi.h> @@ -76,6 +78,11 @@ #pragma comment(lib, "dxguid.lib") // needed for llurlentry test to build on some systems #pragma comment(lib, "dinput8") +#pragma comment(lib, "UxTheme.lib") +#pragma comment(lib, "Dwmapi.lib") +#include <Uxtheme.h> +#include <dwmapi.h> // needed for DwmSetWindowAttribute to set window theme + const S32 MAX_MESSAGE_PER_UPDATE = 20; const S32 BITS_PER_PIXEL = 32; const S32 MAX_NUM_RESOLUTIONS = 32; @@ -85,6 +92,10 @@ const F32 ICON_FLASH_TIME = 0.5f; #define USER_DEFAULT_SCREEN_DPI 96 // Win7 #endif +#ifndef WM_DWMCOLORIZATIONCOLORCHANGED +#define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320 +#endif + // Claim a couple unused GetMessage() message IDs const UINT WM_DUMMY_(WM_USER + 0x0017); const UINT WM_POST_FUNCTION_(WM_USER + 0x0018); @@ -104,7 +115,16 @@ static std::thread::id sMainThreadId; LPWSTR gIconResource = IDI_APPLICATION; -LPDIRECTINPUT8 gDirectInput8; +LPWSTR gIconSmallResource = IDI_APPLICATION; + +namespace +{ + LPDIRECTINPUT8 gDirectInput8; + ID3D11Device* gD3D11Device = nullptr; + ID3D11DeviceContext* gD3D11Context = nullptr; + LUID gExpectedAdapterLUID; + HMODULE gD3D11Library; +} LLW32MsgCallback gAsyncMsgCallback = NULL; @@ -137,6 +157,17 @@ typedef HRESULT(STDAPICALLTYPE *GetDpiForMonitorType)( _Out_ UINT *dpiX, _Out_ UINT *dpiY); +typedef enum PREFERRED_APP_MODE +{ + DEFAULT, + ALLOW_DARK, + FORCE_DARK, + FORCE_LIGHT, + MAX +} PREFERRED_APP_MODE; + +typedef PREFERRED_APP_MODE(WINAPI* fnSetPreferredAppMode)(PREFERRED_APP_MODE mode); + // // LLWindowWin32 // @@ -146,6 +177,19 @@ void show_window_creation_error(const std::string& title) LL_WARNS("Window") << title << LL_ENDL; } +static bool is_thread_from_current_process(DWORD thread_id) +{ + HANDLE thread_handle = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, thread_id); + if (!thread_handle) + { + return false; + } + + const DWORD process_id = GetProcessIdOfThread(thread_handle); + CloseHandle(thread_handle); + return process_id == GetCurrentProcessId(); +} + HGLRC SafeCreateContext(HDC &hdc) { __try @@ -183,6 +227,7 @@ HKL LLWindowWin32::sWinInputLocale = 0; DWORD LLWindowWin32::sWinIMEConversionMode = IME_CMODE_NATIVE; DWORD LLWindowWin32::sWinIMESentenceMode = IME_SMODE_AUTOMATIC; LLCoordWindow LLWindowWin32::sWinIMEWindowPosition(-1,-1); +HMODULE LLWindowWin32::sGLDLLHandle = nullptr; static HWND sWindowHandleForMessageBox = NULL; @@ -343,17 +388,22 @@ static LLMonitorInfo sMonitorInfo; // the containing class a friend. struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool { - static const int MAX_QUEUE_SIZE = 2048; + static constexpr int MAX_QUEUE_SIZE = 2048; + static constexpr F32 WINDOW_TIMEOUT_SEC = 90.f; LLThreadSafeQueue<MSG> mMessageQueue; LLWindowWin32Thread(); void run() override; - void close() override; - // closes queue, wakes thread, waits until thread closes - void wakeAndDestroy(); + // Detroys handles and window + // Either post to or call from window thread + void destroyWindow(); + + // Closes queue, wakes thread, waits until thread closes. + // Call from main thread + bool wakeAndDestroy(); void glReady() { @@ -401,6 +451,60 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool PostMessage(windowHandle, WM_POST_FUNCTION_, wparam, LPARAM(ptr)); } + // Call from main thread. + void initTimeout() + { + // post into thread's queue to avoid threading issues + post([this]() + { + if (!mWindowTimeout) + { + mWindowTimeout = std::make_unique<LLWatchdogTimeout>("WindowThread"); + // supposed to be executed within run(), + // so no point checking if thread is alive + resumeTimeout("TimeoutInit"); + } + }); + } + + // For mainWindowProc, it should not unpause watchdog if it was paused + void pingWindowTimeout(std::string_view state) + { + if (mWindowTimeout && mWindowTimeout->started()) + { + mWindowTimeout->setTimeout(WINDOW_TIMEOUT_SEC); + mWindowTimeout->ping(state); + } + } +private: + // These timeout related functions are strictly for the thread. + void resumeTimeout(std::string_view state) + { + if (mWindowTimeout) + { + mWindowTimeout->setTimeout(WINDOW_TIMEOUT_SEC); + mWindowTimeout->start(state); + } + } + + void pauseTimeout() + { + if (mWindowTimeout) + { + mWindowTimeout->stop(); + } + } + + void pingTimeout(std::string_view state) + { + if (mWindowTimeout) + { + mWindowTimeout->setTimeout(WINDOW_TIMEOUT_SEC); + mWindowTimeout->ping(state); + } + } + +public: using FuncType = std::function<void()>; // call GetMessage() and pull enqueue messages for later processing HWND mWindowHandleThrd = NULL; @@ -410,6 +514,9 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool // until after some graphics setup. See SL-20177. -Cosmic,2023-09-18 bool mGLReady = false; bool mGotGLBuffer = false; + LLAtomicBool mDeleteOnExit = false; +private: + std::unique_ptr<LLWatchdogTimeout> mWindowTimeout; }; @@ -424,6 +531,8 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, F32 max_gl_version) : LLWindow(callbacks, fullscreen, flags), + mAbsoluteCursorPosition(false), + mReceivedSCClose(false), mMaxGLVersion(max_gl_version), mMaxCores(max_cores) { @@ -431,8 +540,12 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, mWindowThread = new LLWindowWin32Thread(); //MAINT-516 -- force a load of opengl32.dll just in case windows went sideways - LoadLibrary(L"opengl32.dll"); + sGLDLLHandle = LoadLibrary(L"opengl32.dll"); + // Request high-performance GPU before creating OpenGL context + // This increases probability of discrete GPU being used when + // the context is created. + requestHighPerformanceGPU(); if (mMaxCores != 0) { @@ -448,6 +561,8 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, SetProcessAffinityMask(hProcess, mask); } + setThreadPriorityHigh(); + #if 0 // this is probably a bad idea, but keep it in your back pocket if you see what looks like // process deprioritization during profiles // force high thread priority @@ -470,43 +585,9 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, } #endif -#if 0 // this is also probably a bad idea, but keep it in your back pocket for getting main thread off of background thread cores (see also LLThread::threadRun) - HANDLE hThread = GetCurrentThread(); - - SYSTEM_INFO sysInfo; - - GetSystemInfo(&sysInfo); - U32 core_count = sysInfo.dwNumberOfProcessors; - - if (max_cores != 0) - { - core_count = llmin(core_count, max_cores); - } - - if (hThread) - { - int priority = GetThreadPriority(hThread); - - if (priority < THREAD_PRIORITY_TIME_CRITICAL) - { - if (SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL)) - { - LL_INFOS() << "Set thread priority to THREAD_PRIORITY_TIME_CRITICAL" << LL_ENDL; - } - else - { - LL_INFOS() << "Failed to set thread priority: " << std::hex << GetLastError() << LL_ENDL; - } - - // tell main thread to prefer core 0 - SetThreadIdealProcessor(hThread, 0); - } - } -#endif - - mFSAASamples = fsaa_samples; mIconResource = gIconResource; + mIconSmallResource = gIconSmallResource; mOverrideAspectRatio = 0.f; mNativeAspectRatio = 0.f; mInputProcessingPaused = false; @@ -576,6 +657,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, // Make an instance of our window then define the window class mhInstance = GetModuleHandle(NULL); +#if !_M_ARM64 // Init Direct Input - needed for joystick / Spacemouse LPDIRECTINPUT8 di8_interface; @@ -590,6 +672,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, { gDirectInput8 = di8_interface; } +#endif mSwapMethod = SWAP_METHOD_UNDEFINED; @@ -695,8 +778,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, } if (dev_mode.dmPelsWidth == width && - dev_mode.dmPelsHeight == height && - dev_mode.dmBitsPerPel == BITS_PER_PIXEL) + dev_mode.dmPelsHeight == height) { success = true; if ((dev_mode.dmDisplayFrequency - current_refresh) @@ -736,7 +818,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, // If we found a good resolution, use it. if (success) { - success = setDisplayResolution(width, height, BITS_PER_PIXEL, closest_refresh); + success = setDisplayResolution(width, height, closest_refresh); } // Keep a copy of the actual current device mode in case we minimize @@ -749,7 +831,6 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, mFullscreen = true; mFullscreenWidth = dev_mode.dmPelsWidth; mFullscreenHeight = dev_mode.dmPelsHeight; - mFullscreenBits = dev_mode.dmBitsPerPel; mFullscreenRefresh = dev_mode.dmDisplayFrequency; LL_INFOS("Window") << "Running at " << dev_mode.dmPelsWidth @@ -763,7 +844,6 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, mFullscreen = false; mFullscreenWidth = -1; mFullscreenHeight = -1; - mFullscreenBits = -1; mFullscreenRefresh = -1; std::map<std::string,std::string> args; @@ -786,7 +866,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, // } // SL-12971 dual GPU display - DISPLAY_DEVICEA display_device; + DISPLAY_DEVICE display_device; int display_index = -1; DWORD display_flags = 0; // EDD_GET_DEVICE_INTERFACE_NAME ? const size_t display_bytes = sizeof(display_device); @@ -797,23 +877,23 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, { // CHAR DeviceName [ 32] Adapter name // CHAR DeviceString[128] - CHAR text[256]; + WCHAR text[256]; - size_t name_len = strlen(display_device.DeviceName ); - size_t desc_len = strlen(display_device.DeviceString); + size_t name_len = lstrlen(display_device.DeviceName ); + size_t desc_len = lstrlen(display_device.DeviceString); - const CHAR *name = name_len ? display_device.DeviceName : "???"; - const CHAR *desc = desc_len ? display_device.DeviceString : "???"; + const WCHAR *name = name_len ? display_device.DeviceName : TEXT("???"); + const WCHAR *desc = desc_len ? display_device.DeviceString : TEXT("???"); - sprintf(text, "Display Device %d: %s, %s", display_index, name, desc); - LL_INFOS("Window") << text << LL_ENDL; + wsprintf(text, TEXT("Display Device %d: %s, %s"), display_index, name, desc); + LL_INFOS("Window") << ll_convert<std::string>(std::wstring(text)) << LL_ENDL; } ::ZeroMemory(&display_device,display_bytes); display_device.cb = display_bytes; display_index++; - } while( EnumDisplayDevicesA(NULL, display_index, &display_device, display_flags )); + } while( EnumDisplayDevices(NULL, display_index, &display_device, display_flags )); LL_INFOS("Window") << "Total Display Devices: " << display_index << LL_ENDL; @@ -842,6 +922,8 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, // Initialize (boot strap) the Language text input management, // based on the system's (or user's) default settings. allowLanguageTextInput(NULL, false); + updateWindowTheme(); + setCustomIcon(); } @@ -853,6 +935,7 @@ LLWindowWin32::~LLWindowWin32() } delete mDragDrop; + mDragDrop = NULL; delete [] mWindowTitle; mWindowTitle = NULL; @@ -864,6 +947,7 @@ LLWindowWin32::~LLWindowWin32() mWindowClassName = NULL; delete mWindowThread; + mWindowThread = NULL; } void LLWindowWin32::show() @@ -930,6 +1014,7 @@ void LLWindowWin32::close() } mDragDrop->reset(); + clearHighPerformanceGPURequest(); // Go back to screen mode written in the registry. @@ -972,7 +1057,7 @@ void LLWindowWin32::close() // Restore gamma to the system values. restoreGamma(); - LL_DEBUGS("Window") << "Destroying Window" << LL_ENDL; + LL_INFOS("Window") << "Cleanup and destruction of Window Thread" << LL_ENDL; if (sWindowHandleForMessageBox == mWindowHandle) { @@ -982,7 +1067,11 @@ void LLWindowWin32::close() mhDC = NULL; mWindowHandle = NULL; - mWindowThread->wakeAndDestroy(); + if (mWindowThread->wakeAndDestroy()) + { + // thread will delete itselfs once done + mWindowThread = NULL; + } } bool LLWindowWin32::isValid() @@ -990,6 +1079,52 @@ bool LLWindowWin32::isValid() return (mWindowHandle != NULL); } +void LLWindowWin32::setThreadPriorityHigh() +{ + // Threads start at normal priority. But this is our main window/rendering thread, + // even if window handle belongs to another thread. So we can raise its priority + // to ensure better responsiveness and less blocking by lack of resources. + HANDLE hThread = GetCurrentThread(); + if (hThread) + { + int priority = GetThreadPriority(hThread); + + if (priority == THREAD_PRIORITY_ERROR_RETURN) + { + LL_WARNS_ONCE("Window") << "Failed to get thread priority: " << std::hex << GetLastError() << LL_ENDL; + } + else if (priority > THREAD_PRIORITY_HIGHEST) + { + // At the moment nothing should be setting 'critical' priority, + // but if that happens for some reason, we don't want to mess with it. + LL_WARNS("Window") << "setThreadPriorityHigh ignored, priority was " << (S32)priority << LL_ENDL; + } + else if (priority != THREAD_PRIORITY_HIGHEST) + { + if (SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST)) + { + LL_DEBUGS("Window") << "Set thread priority to THREAD_PRIORITY_HIGHEST" << LL_ENDL; + } + else + { + LL_WARNS("Window") << "Failed to set thread priority: " << std::hex << GetLastError() << LL_ENDL; + } + } + } +} + +void LLWindowWin32::setThreadPriorityNormal() +{ + HANDLE hThread = GetCurrentThread(); + if (hThread) + { + if (!SetThreadPriority(hThread, THREAD_PRIORITY_NORMAL)) + { + LL_WARNS_ONCE("Window") << "Failed to set thread priority: " << std::hex << GetLastError() << LL_ENDL; + } + } +} + bool LLWindowWin32::getVisible() { return (mWindowHandle && IsWindowVisible(mWindowHandle)); @@ -1185,7 +1320,7 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo // If we found a good resolution, use it. if (success) { - success = setDisplayResolution(width, height, BITS_PER_PIXEL, closest_refresh); + success = setDisplayResolution(width, height, closest_refresh); } // Keep a copy of the actual current device mode in case we minimize @@ -1197,7 +1332,6 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo mFullscreen = true; mFullscreenWidth = dev_mode.dmPelsWidth; mFullscreenHeight = dev_mode.dmPelsHeight; - mFullscreenBits = dev_mode.dmBitsPerPel; mFullscreenRefresh = dev_mode.dmDisplayFrequency; LL_INFOS("Window") << "Running at " << dev_mode.dmPelsWidth @@ -1223,7 +1357,6 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo mFullscreen = false; mFullscreenWidth = -1; mFullscreenHeight = -1; - mFullscreenBits = -1; mFullscreenRefresh = -1; LL_INFOS("Window") << "Unable to run fullscreen at " << width << "x" << height << LL_ENDL; @@ -1314,7 +1447,7 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo catch (...) { LOG_UNHANDLED_EXCEPTION("ChoosePixelFormat"); - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } @@ -1325,7 +1458,7 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtDescErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtDescErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } @@ -1363,7 +1496,7 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo if (!SetPixelFormat(mhDC, pixel_format, &pfd)) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtSetErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtSetErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } @@ -1371,14 +1504,14 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo if (!(mhRC = SafeCreateContext(mhDC))) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBGLContextErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBGLContextErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } if (!wglMakeCurrent(mhDC, mhRC)) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBGLContextActErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBGLContextActErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } @@ -1584,14 +1717,14 @@ const S32 max_format = (S32)num_formats - 1; if (!mhDC) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBDevContextErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBDevContextErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } if (!SetPixelFormat(mhDC, pixel_format, &pfd)) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtSetErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtSetErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } @@ -1623,7 +1756,7 @@ const S32 max_format = (S32)num_formats - 1; { LL_WARNS("Window") << "No wgl_ARB_pixel_format extension!" << LL_ENDL; // cannot proceed without wgl_ARB_pixel_format extension, shutdown same as any other gGLManager.initGL() failure - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBVideoDrvErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBVideoDrvErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } @@ -1632,7 +1765,7 @@ const S32 max_format = (S32)num_formats - 1; if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtDescErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtDescErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } @@ -1654,18 +1787,25 @@ const S32 max_format = (S32)num_formats - 1; if (!wglMakeCurrent(mhDC, mhRC)) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBGLContextActErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBGLContextActErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } + gGLManager.initWGL(); // Reinit WGL functions once we have our full context + if (!gGLManager.initGL()) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBVideoDrvErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBVideoDrvErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } + // Setup Tracy gpu context + { + LL_PROFILER_GPU_CONTEXT; + } + // Disable vertical sync for swap toggleVSync(enable_vsync); @@ -1697,8 +1837,6 @@ const S32 max_format = (S32)num_formats - 1; swapBuffers(); } - LL_PROFILER_GPU_CONTEXT; - return true; } @@ -1866,7 +2004,7 @@ void* LLWindowWin32::createSharedContext() if (!rc && !(rc = wglCreateContext(mhDC))) { close(); - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBGLContextErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBGLContextErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); } return rc; @@ -1931,7 +2069,7 @@ void LLWindowWin32::setTitle(const std::string title) // to support non-ascii usernames (and region names?) mWindowThread->post([=]() { - SetWindowTextA(mWindowHandle, title.c_str()); + SetWindowText(mWindowHandle, ll_convert<std::wstring>(title).c_str()); }); } @@ -2304,18 +2442,45 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_DEVICECHANGE: { LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_DEVICECHANGE"); + window_imp->mWindowThread->pingWindowTimeout("WM_DEVICECHANGE"); + + // Log detailed device change information + std::string change_type = "UNKNOWN"; + switch (w_param) + { + case DBT_DEVICEARRIVAL: change_type = "DBT_DEVICEARRIVAL"; break; + case DBT_DEVICEREMOVECOMPLETE: change_type = "DBT_DEVICEREMOVECOMPLETE"; break; + case DBT_DEVNODES_CHANGED: change_type = "DBT_DEVNODES_CHANGED"; break; + case DBT_DEVICEQUERYREMOVE: change_type = "DBT_DEVICEQUERYREMOVE"; break; + case DBT_DEVICEQUERYREMOVEFAILED: change_type = "DBT_DEVICEQUERYREMOVEFAILED"; break; + case DBT_DEVICEREMOVEPENDING: change_type = "DBT_DEVICEREMOVEPENDING"; break; + case DBT_CONFIGCHANGED: change_type = "DBT_CONFIGCHANGED"; break; + } + if (w_param == DBT_DEVNODES_CHANGED || w_param == DBT_DEVICEARRIVAL) { - WINDOW_IMP_POST(window_imp->mCallbacks->handleDeviceChange(window_imp)); + WINDOW_IMP_POST(window_imp->mCallbacks->handleDeviceChange(window_imp, change_type)); return 1; } + else if (l_param) + { + const auto* hdr = reinterpret_cast<const DEV_BROADCAST_HDR*>(l_param); + if (hdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + { + // Might need to register for monitor device notifications + // to get this message when monitor is suspended or resumed. + // TODO: log monitor suspending and resuming. + LL_INFOS("Window") << "DEVICEINTERFACE: " << change_type << LL_ENDL; + } + } break; } case WM_PAINT: { LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_PAINT"); + window_imp->mWindowThread->pingWindowTimeout("WM_PAINT"); GetUpdateRect(window_imp->mWindowHandle, &update_rect, FALSE); update_width = update_rect.right - update_rect.left + 1; update_height = update_rect.bottom - update_rect.top + 1; @@ -2324,6 +2489,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ update_width, update_height)); break; } + case WM_ERASEBKGND: + { + RECT client_rect; + if (GetClientRect(h_wnd, &client_rect)) + { + FillRect((HDC)w_param, &client_rect, (HBRUSH)GetStockObject(BLACK_BRUSH)); + } + return 1; + } case WM_PARENTNOTIFY: { break; @@ -2361,11 +2535,24 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_ACTIVATEAPP: { LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_ACTIVATEAPP"); + // Resolve ownership before deferring the work because thread IDs can + // be reused after a thread exits. + const bool activating_same_process_thread = + !w_param && is_thread_from_current_process(static_cast<DWORD>(l_param)); window_imp->post([=]() { // This message should be sent whenever the app gains or loses focus. BOOL activating = (BOOL)w_param; + // Native dialogs run on a worker thread. Moving focus between + // the viewer and one of those dialogs must not be treated as + // switching to another application: in fullscreen that would + // minimize the viewer and hide its owned dialog. + if (!activating && activating_same_process_thread) + { + return; + } + if (window_imp->mFullscreen) { // When we run fullscreen, restoring or minimizing the app needs @@ -2415,8 +2602,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_SYSCOMMAND: { LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_SYSCOMMAND"); - switch (w_param) + switch (w_param & 0xFFF0) { + case SC_CLOSE: + // User clicked close from system menu/taskbar or 'end process' from task manager + // Do nothing, will cause WM_CLOSE. + // If we don't get this message before WM_CLOSE, we are likely getting + // a kill from some external program. Win11 task manager Does cause SC_CLOSE. + window_imp->mReceivedSCClose = true; + break; case SC_KEYMENU: // Disallow the ALT key from triggering the default system menu. return 0; @@ -2431,10 +2625,35 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_CLOSE: { LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_CLOSE"); + window_imp->mWindowThread->pingWindowTimeout("WM_CLOSE"); + + window_imp->mCallbacks->handlePreCloseRequest(); // mark app as potentially closing + if (!window_imp->mReceivedSCClose) + { + // Some external program is trying to close the app. + // Assume that it's going to destroy process if it fails + // and try to fast-quit without confirmation or cleanup. + window_imp->post([=]() + { + // Check if app needs cleanup or can be closed immediately. + if (window_imp->mCallbacks->handleSessionExit(window_imp)) + { + // Get the app to initiate cleanup. + window_imp->mCallbacks->handleQuit(window_imp); + } + }); + return 0; + } + window_imp->mReceivedSCClose = false; + + // There is no way to tell the difference between a user issued + // WM_CLOSE or task manager's WM_CLOSE. + // Assume it is a user and ask for confirmation, but create a marker file. + // If App keeps doing something after a second, or gets 'destroy' message clear the marker. window_imp->post([=]() { // Will the app allow the window to close? - if (window_imp->mCallbacks->handleCloseRequest(window_imp)) + if (window_imp->mCallbacks->handleCloseRequest(window_imp, true)) { // Get the app to initiate cleanup. window_imp->mCallbacks->handleQuit(window_imp); @@ -2452,6 +2671,247 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ } return 0; } + case WM_NCDESTROY: + LL_INFOS("Window") << "Received WM_NCDESTROY" << LL_ENDL; + break; + case WM_WTSSESSION_CHANGE: + { + // Detects Remote Desktop disconnects, fast user switching, session logoff + // w_param: WTS_CONSOLE_CONNECT, WTS_CONSOLE_DISCONNECT, WTS_SESSION_LOGOFF, etc. + LL_INFOS("Window") << "Received WM_WTSSESSION_CHANGE with wParam: " << (U32)w_param << LL_ENDL; + break; + } + case WM_QUERYENDSESSION: + { + // Generally means that OS is going to shut down or user is going to log off. + // Can use ShutdownBlockReasonCreate here. + LL_INFOS("Window") << "Received WM_QUERYENDSESSION with wParam: " << (U32)w_param << " lParam: " << (U32)l_param << LL_ENDL; + return TRUE; // 1 = ok to end session. 0 no longer works by itself, use ShutdownBlockReasonCreate + } + case WM_ENDSESSION: + { + // OS session is shutting down, initiate cleanup. + // Comes after WM_QUERYENDSESSION + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_ENDSESSION"); + LL_INFOS("Window") << "Received WM_ENDSESSION with wParam: " << (U32)w_param << " lParam: " << (U32)l_param << LL_ENDL; + window_imp->mWindowThread->pingWindowTimeout("WM_ENDSESSION"); + unsigned int end_session_flags = (U32)l_param; + + if (w_param == TRUE // if true, session is ending + || end_session_flags == 0 // not possible to determine type of the event + // || (end_session_flags & ENDSESSION_CLOSEAPP)) system update or low resources, must be acompanied by w_param == TRUE + || (end_session_flags & ENDSESSION_CRITICAL) // will shutdown regardless of app state + || (end_session_flags & ENDSESSION_LOGOFF)) // logoff, can delay shutdown + { + window_imp->mCallbacks->handlePreCloseRequest(); // mark app as closing + window_imp->post([=]() + { + LL_INFOS("Window") << "Shutting down due to session terminating" << LL_ENDL; + // Check if app needs cleanup or can be closed immediately. + if (window_imp->mCallbacks->handleSessionExit(window_imp)) + { + // Get the app to initiate cleanup. + window_imp->mCallbacks->handleQuit(window_imp); + } + }); + // Give app a second to finish up. That's not enough for a clean exit, + // but better than nothing. + // Todo: sync this better, some kind of waitForResult? Can't wait forever, + // but for ENDSESSION_LOGOFF can potentially use ShutdownBlockReasonCreate + // for a bigger delay. + ms_sleep(1000); + } + // Don't need to post quit or destroy window, + // if session is ending OS is going to take care of it. + return 0; + } + case WM_POWERBROADCAST: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_POWERBROADCAST"); + switch (w_param) + { + case PBT_APMSUSPEND: + LL_INFOS("Window") << "System is suspending (sleep/hibernate)" << LL_ENDL; + // System is about to enter sleep or hibernation + // Viewer can't function in hibernation, try to shut down. + // The system allows approximately two seconds for an + // application to handle this notification. + + // Mark app as potentially closing, to minimize issues if OS does not recover. + window_imp->mCallbacks->handlePreCloseRequest(); + window_imp->post([=]() + { + window_imp->mCallbacks->handleSuspendRequest(); + }); + // Window thread normally doesn't block main thread, but OS can suspend + // immediately if we don't wait. + // Keep OS from suspending to give a chance to send stats. + ms_sleep(1000); + return TRUE; + + case PBT_APMRESUMESUSPEND: + LL_INFOS("Window") << "System is resuming from suspend" << LL_ENDL; + window_imp->mCallbacks->handleCloseRequestCanceled(); + return TRUE; + + case PBT_APMPOWERSTATUSCHANGE: + LL_INFOS("Window") << "Power status has changed" << LL_ENDL; + // Power status change (AC/battery) + // Viewer requires high performance, not much we can do. + // about it, but log for diagnostic purposes (example: + // OS trying to throw viewer at an iGPU after this message) + return TRUE; + + default: + LL_INFOS("Window") << "Received WM_POWERBROADCAST with wParam: 0x" << std::hex << (uintptr_t)w_param << " lParam: 0x" << (uintptr_t)l_param << std::dec << LL_ENDL; + break; + } + break; + } + case WM_POST_UNINSTALL_: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_POST_UNINSTALL_"); + // Other instance, likely velopack, requested we quit. + // Don't trust PID alone (can be spoofed), verify the + // path for security purposes before processing. + // Verifying path isn't a strong varranty, if this turns + // up to be a risk, we will want something more secure. + // See sendShutdownToOtherInstances for the sender. + + // LPARAM contains message type. + DWORD message_type = static_cast<DWORD>(l_param); + if (message_type == WM_POST_UNINSTALL_MSG_SHUTDOWN || message_type == WM_POST_UNINSTALL_MSG_UPDATE) + { + DWORD sender_process_id = static_cast<DWORD>(w_param); + + // Make sure something didn't just send us our own process + DWORD our_process_id = GetCurrentProcessId(); + if (our_process_id == sender_process_id) + { + LL_WARNS("Window") << "Received WM_POST_UNINSTALL_ from our own process, ignoring" << LL_ENDL; + break; + } + + if (sender_process_id == 0) + { + LL_WARNS("Window") << "Received WM_POST_UNINSTALL_ but couldn't get sender process ID" << LL_ENDL; + break; + } + + // Open the existing sender process to verify its executable path + HANDLE hSenderProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, sender_process_id); + if (!hSenderProcess) + { + LL_WARNS("Window") << "Received WM_POST_UNINSTALL_ but couldn't open sender process" << LL_ENDL; + break; + } + + // Get the actual executable path of the sender + wchar_t sender_exe_path[MAX_PATH]; + DWORD size = MAX_PATH; + bool got_sender_path = QueryFullProcessImageNameW(hSenderProcess, 0, sender_exe_path, &size) != 0; + CloseHandle(hSenderProcess); + + if (!got_sender_path) + { + LL_WARNS("Window") << "Received WM_POST_UNINSTALL_ but couldn't query sender executable path" << LL_ENDL; + break; + } + + // Extract directory from sender's executable path + wchar_t sender_dir[MAX_PATH]; + wchar_t* file_part = nullptr; + DWORD result = GetFullPathNameW(sender_exe_path, MAX_PATH, sender_dir, &file_part); + + if (result == 0 || result >= MAX_PATH) + { + LL_WARNS("Window") << "Failed to normalize sender executable path" << LL_ENDL; + break; + } + + // Remove the filename to get just directory + if (file_part) + { + *file_part = L'\0'; + } + + // Remove trailing backslash + size_t sender_dir_len = wcslen(sender_dir); + if (sender_dir_len > 0 && sender_dir[sender_dir_len - 1] == L'\\') + { + sender_dir[sender_dir_len - 1] = L'\0'; + sender_dir_len--; + } + + // Remove "\current" suffix from sender's path if present + const std::wstring current_suffix = L"\\current"; + std::wstring sender_normalized_str(sender_dir); + if (sender_normalized_str.length() >= current_suffix.length() && + _wcsicmp(sender_normalized_str.c_str() + sender_normalized_str.length() - current_suffix.length(), + current_suffix.c_str()) == 0) + { + sender_normalized_str.resize(sender_normalized_str.length() - current_suffix.length()); + } + + // Get our executable directory for comparison + std::wstring our_wide = ll_convert<std::wstring>(gDirUtilp->getExecutableDir()); + + // Normalize our path + wchar_t our_normalized[MAX_PATH]; + file_part = nullptr; + + DWORD result2 = GetFullPathNameW(our_wide.c_str(), MAX_PATH, our_normalized, &file_part); + + if (result2 == 0 || result2 >= MAX_PATH) + { + LL_WARNS("Window") << "Failed to normalize our executable path" << LL_ENDL; + break; + } + + // Remove trailing backslash + size_t our_len = wcslen(our_normalized); + if (our_len > 0 && our_normalized[our_len - 1] == L'\\') + { + our_normalized[our_len - 1] = L'\0'; + our_len--; + } + + // Remove "\current" suffix from our path if present + std::wstring our_normalized_str(our_normalized); + if (our_normalized_str.length() >= current_suffix.length() && + _wcsicmp(our_normalized_str.c_str() + our_normalized_str.length() - current_suffix.length(), + current_suffix.c_str()) == 0) + { + our_normalized_str.resize(our_normalized_str.length() - current_suffix.length()); + } + + // Compare the normalized base installation paths (case-insensitive) + if (_wcsicmp(sender_normalized_str.c_str(), our_normalized_str.c_str()) == 0) + { + window_imp->post([=]() + { + LL_INFOS("Window") << "Received valid shutdown request from verified same installation directory" << LL_ENDL; + // Check if app needs cleanup or can be closed immediately. + if (window_imp->mCallbacks->handleCloseRequest(window_imp, false)) + { + // Get the app to initiate cleanup. + window_imp->mCallbacks->handleQuit(window_imp); + } + }); + } + else + { + LL_WARNS("Window") << "Rejected shutdown request - sender not from our installation directory. " + << "Sender: " << ll_convert_wide_to_string(sender_normalized_str) + << " Our: " << ll_convert_wide_to_string(our_normalized_str) << LL_ENDL; + } + } + else + { + LL_WARNS("Window") << "Received invalid WM_POST_UNINSTALL_ message" << LL_ENDL; + } + break; + } case WM_COMMAND: { LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_COMMAND"); @@ -2915,19 +3375,31 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ // means that the window was un-minimized. if (w_param == SIZE_RESTORED && window_imp->mLastSizeWParam != SIZE_RESTORED) { - WINDOW_IMP_POST(window_imp->mCallbacks->handleActivate(window_imp, true)); + window_imp->post([=]() + { + window_imp->setThreadPriorityHigh(); + window_imp->mCallbacks->handleActivate(window_imp, true); + }); } // handle case of window being maximized from fully minimized state if (w_param == SIZE_MAXIMIZED && window_imp->mLastSizeWParam != SIZE_MAXIMIZED) { - WINDOW_IMP_POST(window_imp->mCallbacks->handleActivate(window_imp, true)); + window_imp->post([=]() + { + window_imp->setThreadPriorityHigh(); + window_imp->mCallbacks->handleActivate(window_imp, true); + }); } // Also handle the minimization case if (w_param == SIZE_MINIMIZED && window_imp->mLastSizeWParam != SIZE_MINIMIZED) { - WINDOW_IMP_POST(window_imp->mCallbacks->handleActivate(window_imp, false)); + window_imp->post([=]() + { + window_imp->setThreadPriorityNormal(); + window_imp->mCallbacks->handleActivate(window_imp, false); + }); } // Actually resize all of our views @@ -2947,6 +3419,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_DPICHANGED: { LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_DPICHANGED"); + window_imp->mWindowThread->pingWindowTimeout("WM_DPICHANGED"); LPRECT lprc_new_scale; F32 new_scale = F32(LOWORD(w_param)) / F32(USER_DEFAULT_SCREEN_DPI); lprc_new_scale = (LPRECT)l_param; @@ -2967,7 +3440,19 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_DISPLAYCHANGE: { - WINDOW_IMP_POST(window_imp->mCallbacks->handleDisplayChanged()); + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_DISPLAYCHANGE"); + window_imp->mWindowThread->pingWindowTimeout("WM_DISPLAYCHANGE"); + window_imp->post([=]() { + window_imp->mCallbacks->handleDisplayChanged(); + // Note: WM_DISPLAYCHANGE was passing to WM_SETFOCUS + // which might have been unintended and was messing with zones. + // handleFocus was copied over and return 0 added, but + // handleFocus might be not needed here. + // handleFocus resets mouse, closes popups and keys, which + // we probablt should do on 'display change'. + window_imp->mCallbacks->handleFocus(window_imp); + }); + return 0; } case WM_SETFOCUS: @@ -3005,6 +3490,9 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_SETTINGCHANGE: { LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_SETTINGCHANGE"); + // Can be called on OS user switching + LL_INFOS("Window") << "WM_SETTINGCHANGE, with wParam: 0x" << std::hex << (uintptr_t)w_param << " lParam: 0x" << (uintptr_t)l_param << std::dec << LL_ENDL; + window_imp->mWindowThread->pingWindowTimeout("WM_SETTINGCHANGE"); if (w_param == SPI_SETMOUSEVANISH) { if (!SystemParametersInfo(SPI_GETMOUSEVANISH, 0, &window_imp->mMouseVanish, 0)) @@ -3012,6 +3500,17 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ WINDOW_IMP_POST(window_imp->mMouseVanish = true); } } + // Check if theme-related settings changed + else if (l_param && (wcscmp((LPCWSTR)l_param, L"ImmersiveColorSet") == 0)) + { + WINDOW_IMP_POST(window_imp->updateWindowTheme()); + } + } + break; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + { + WINDOW_IMP_POST(window_imp->updateWindowTheme()); } break; @@ -3065,6 +3564,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ prev_absolute_x = absolute_x; prev_absolute_y = absolute_y; + window_imp->mAbsoluteCursorPosition = true; } else { @@ -3081,6 +3581,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ window_imp->mRawMouseDelta.mX += (S32)round((F32)raw->data.mouse.lLastX * (F32)speed / DEFAULT_SPEED); window_imp->mRawMouseDelta.mY -= (S32)round((F32)raw->data.mouse.lLastY * (F32)speed / DEFAULT_SPEED); } + window_imp->mAbsoluteCursorPosition = false; } } } @@ -3104,10 +3605,14 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ break; } } - else + else // (NULL == window_imp) { - // (NULL == window_imp) LL_DEBUGS("Window") << "No window implementation to handle message with, message code: " << U32(u_msg) << LL_ENDL; + if (u_msg == WM_DESTROY) + { + PostQuitMessage(0); // Posts WM_QUIT with an exit code of 0 + return 0; + } } // pass unhandled messages down to Windows @@ -3241,7 +3746,7 @@ bool LLWindowWin32::pasteTextFromClipboard(LLWString &dst) WCHAR *utf16str = (WCHAR*) GlobalLock(h_data); if (utf16str) { - dst = utf16str_to_wstring(utf16str); + dst = ll_convert<LLWString>(std::wstring(utf16str)); LLWStringUtil::removeWindowsCR(dst); GlobalUnlock(h_data); success = true; @@ -3266,8 +3771,8 @@ bool LLWindowWin32::copyTextToClipboard(const LLWString& wstr) // Provide a copy of the data in Unicode format. LLWString sanitized_string(wstr); LLWStringUtil::addCRLF(sanitized_string); - llutf16string out_utf16 = wstring_to_utf16str(sanitized_string); - const size_t size_utf16 = (out_utf16.length() + 1) * sizeof(WCHAR); + std::wstring out_utf16 = ll_convert<std::wstring>(sanitized_string); + const size_t size_utf16 = (out_utf16.length() + 1) * sizeof(wchar_t); // Memory is allocated and then ownership of it is transfered to the system. HGLOBAL hglobal_copy_utf16 = GlobalAlloc(GMEM_MOVEABLE, size_utf16); @@ -3517,7 +4022,7 @@ F32 LLWindowWin32::getPixelAspectRatio() // Change display resolution. Returns true if successful. // protected -bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh) +bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 refresh) { DEVMODE dev_mode; ::ZeroMemory(&dev_mode, sizeof(DEVMODE)); @@ -3529,7 +4034,6 @@ bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 re { if (dev_mode.dmPelsWidth == width && dev_mode.dmPelsHeight == height && - dev_mode.dmBitsPerPel == bits && dev_mode.dmDisplayFrequency == refresh ) { // ...display mode identical, do nothing @@ -3541,9 +4045,8 @@ bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 re dev_mode.dmSize = sizeof(dev_mode); dev_mode.dmPelsWidth = width; dev_mode.dmPelsHeight = height; - dev_mode.dmBitsPerPel = bits; dev_mode.dmDisplayFrequency = refresh; - dev_mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; + dev_mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; // CDS_FULLSCREEN indicates that this is a temporary change to the device mode. LONG cds_result = ChangeDisplaySettings(&dev_mode, CDS_FULLSCREEN); @@ -3553,7 +4056,7 @@ bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 re if (!success) { LL_WARNS("Window") << "setDisplayResolution failed, " - << width << "x" << height << "x" << bits << " @ " << refresh << LL_ENDL; + << width << "x" << height << " @ " << refresh << LL_ENDL; } return success; @@ -3564,7 +4067,7 @@ bool LLWindowWin32::setFullscreenResolution() { if (mFullscreen) { - return setDisplayResolution( mFullscreenWidth, mFullscreenHeight, mFullscreenBits, mFullscreenRefresh); + return setDisplayResolution( mFullscreenWidth, mFullscreenHeight, mFullscreenRefresh); } else { @@ -3629,7 +4132,7 @@ void LLSplashScreenWin32::showImpl() ShowWindow(mWindow, SW_SHOW); // Should set taskbar text without creating a header for the window (caption) - SetWindowTextA(mWindow, "Second Life"); + SetWindowText(mWindow, TEXT("Second Life")); } @@ -3766,8 +4269,7 @@ void LLWindowWin32::spawnWebBrowser(const std::string& escaped_url, bool async) // reliablly on Vista. // this is madness.. no, this is.. - LLWString url_wstring = utf8str_to_wstring( escaped_url ); - llutf16string url_utf16 = wstring_to_utf16str( url_wstring ); + std::wstring url_utf16 = ll_convert<std::wstring>(escaped_url); // let the OS decide what to use to open the URL SHELLEXECUTEINFO sei = { sizeof( sei ) }; @@ -4055,7 +4557,7 @@ void LLWindowWin32::fillCompositionLogfont(LOGFONT *logfont) U32 LLWindowWin32::fillReconvertString(const LLWString &text, S32 focus, S32 focus_length, RECONVERTSTRING *reconvert_string) { - const llutf16string text_utf16 = wstring_to_utf16str(text); + const std::wstring text_utf16 = ll_convert<std::wstring>(text); const DWORD required_size = sizeof(RECONVERTSTRING) + (static_cast<DWORD>(text_utf16.length()) + 1) * sizeof(WCHAR); if (reconvert_string && reconvert_string->dwSize >= required_size) { @@ -4155,7 +4657,7 @@ void LLWindowWin32::handleCompositionMessage(const U32 indexes) size = LLWinImm::getCompositionString(himc, GCS_RESULTSTR, data, size); if (size > 0) { - result_string = utf16str_to_wstring(llutf16string(data, size / sizeof(WCHAR))); + result_string = ll_convert_wide_to_wstring(std::wstring(data, size / sizeof(WCHAR))); } delete[] data; needs_update = true; @@ -4172,7 +4674,7 @@ void LLWindowWin32::handleCompositionMessage(const U32 indexes) if (size > 0) { preedit_string_utf16_length = size / sizeof(WCHAR); - preedit_string = utf16str_to_wstring(llutf16string(data, size / sizeof(WCHAR))); + preedit_string = ll_convert_wide_to_wstring(std::wstring(data, size / sizeof(WCHAR))); } delete[] data; needs_update = true; @@ -4478,6 +4980,248 @@ void LLWindowWin32::setDPIAwareness() } } +void LLWindowWin32::requestHighPerformanceGPU() const +{ + // Try to load d3d11.dll and request high performance adapter + gD3D11Library = LoadLibraryA("d3d11.dll"); + if (gD3D11Library) + { + typedef HRESULT(WINAPI* PFN_D3D11_CREATE_DEVICE)( + IDXGIAdapter*, D3D_DRIVER_TYPE, HMODULE, UINT, + const D3D_FEATURE_LEVEL*, UINT, UINT, ID3D11Device**, + D3D_FEATURE_LEVEL*, ID3D11DeviceContext**); + + PFN_D3D11_CREATE_DEVICE pD3D11CreateDevice = + (PFN_D3D11_CREATE_DEVICE)GetProcAddress(gD3D11Library, "D3D11CreateDevice"); + + if (pD3D11CreateDevice) + { + // Try to enumerate adapters and select the best one + IDXGIFactory1* pFactory = nullptr; + IDXGIAdapter1* pSelectedAdapter = nullptr; + std::string selected_descr; + HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&pFactory); + + if (SUCCEEDED(hr) && pFactory) + { + IDXGIAdapter1* pAdapter = nullptr; + SIZE_T maxDedicatedMemory = 0; + UINT adapterIndex = 0; + S32 adapter_count = 0; + + // Enumerate all adapters and find the one with the most dedicated video memory + while (pFactory->EnumAdapters1(adapterIndex, &pAdapter) != DXGI_ERROR_NOT_FOUND) + { + DXGI_ADAPTER_DESC1 desc; + pAdapter->GetDesc1(&desc); + + std::wstring description_w(desc.Description); + std::string description = ll_convert_wide_to_string(description_w); + + + // Skip software adapters + if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) + { + LL_DEBUGS("Window") << "Adapter " << adapterIndex << ": " << description + << ", Dedicated VRAM: " << (desc.DedicatedVideoMemory / 1024 / 1024) << " MB" + << ", Vendor: 0x" << std::hex << desc.VendorId << std::dec + << ", Flags: " << desc.Flags << LL_ENDL; + } + // Skip Microsoft Basic Render Driver, it's a placeholder for missing drivers + else if (description.find("Microsoft Basic Render Driver") != std::string::npos) + { + // User is likely missing drivers, so log a warning. + // Don't consider this adapter as a valid selection. + LL_WARNS("Window") << "Adapter " << adapterIndex << ": " << description + << ", Dedicated VRAM: " << (desc.DedicatedVideoMemory / 1024 / 1024) << " MB" + << ", Vendor: 0x" << std::hex << desc.VendorId << std::dec + << ", Flags: " << desc.Flags << LL_ENDL; + } + else + { + LL_INFOS("Window") << "Adapter " << adapterIndex << ": " << description + << ", Dedicated VRAM: " << (desc.DedicatedVideoMemory / 1024 / 1024) << " MB" + << ", Vendor: 0x" << std::hex << desc.VendorId << std::dec + << ", Flags: " << desc.Flags << LL_ENDL; + + adapter_count++; + // Select adapter with most dedicated video memory (typically the discrete GPU) + if (desc.DedicatedVideoMemory > maxDedicatedMemory) + { + if (pSelectedAdapter) + { + pSelectedAdapter->Release(); + } + pSelectedAdapter = pAdapter; + pSelectedAdapter->AddRef(); + maxDedicatedMemory = desc.DedicatedVideoMemory; + gExpectedAdapterLUID = desc.AdapterLuid; + selected_descr = description; + } + } + + pAdapter->Release(); + adapterIndex++; + } + pFactory->Release(); + + if (adapter_count < 2) + { + // Only one adapter, no need to request high-performance GPU + if (pSelectedAdapter) + { + pSelectedAdapter->Release(); + } + gExpectedAdapterLUID = { 0, 0 }; + FreeLibrary(gD3D11Library); + gD3D11Library = nullptr; + return; + } + + LL_INFOS("Window") << "Selected as preferred adapter (highest VRAM): " << selected_descr << LL_ENDL; + } + + // Create a temporary device to ensure high-performance GPU is selected + // This initialization can help "wake up" the discrete GPU + D3D_FEATURE_LEVEL featureLevel; + D3D_FEATURE_LEVEL requestedLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1 }; + + bool adapterSelected = (pSelectedAdapter != nullptr); + if (adapterSelected) + { + hr = pD3D11CreateDevice( + pSelectedAdapter, + D3D_DRIVER_TYPE_UNKNOWN, + nullptr, + 0, + requestedLevels, + _countof(requestedLevels), + D3D11_SDK_VERSION, + &gD3D11Device, + &featureLevel, + &gD3D11Context + ); + pSelectedAdapter->Release(); + + if (!SUCCEEDED(hr)) + { + LL_WARNS("Window") << "D3D11 failed to use preffered adapter " << selected_descr << LL_ENDL; + gExpectedAdapterLUID = { 0, 0 }; + adapterSelected = false; + } + } + + if (!adapterSelected) + { + // Either failed to select or didn't find an adapter. + hr = pD3D11CreateDevice( + nullptr, + D3D_DRIVER_TYPE_HARDWARE, + nullptr, + 0, + requestedLevels, + _countof(requestedLevels), + D3D11_SDK_VERSION, + &gD3D11Device, + &featureLevel, + &gD3D11Context + ); + if (!SUCCEEDED(hr)) + { + LL_WARNS("Window") << "D3D11 failed to use hardware adapter" << LL_ENDL; + FreeLibrary(gD3D11Library); + gD3D11Library = nullptr; + // These shouldn't be set, but make sure they are null. + gD3D11Device = nullptr; + gD3D11Context = nullptr; + } + } + } + else + { + LL_WARNS("Window") << "Failed to get D3D11CreateDevice function from d3d11.dll. High-performance GPU request failed." << LL_ENDL; + FreeLibrary(gD3D11Library); + gD3D11Library = nullptr; + } + } +} + +bool LLWindowWin32::detectGPUChange() const +{ + if (!gD3D11Device) + { + // Can't detect without D3D11 device + return false; + } + + if (gExpectedAdapterLUID.LowPart == 0 && gExpectedAdapterLUID.HighPart == 0) + { + // No specific adapter was selected, can't detect changes. + return false; + } + + IDXGIDevice* pDXGIDevice = nullptr; + HRESULT hr = gD3D11Device->QueryInterface(__uuidof(IDXGIDevice), (void**)&pDXGIDevice); + + if (SUCCEEDED(hr) && pDXGIDevice) + { + IDXGIAdapter* pCurrentAdapter = nullptr; + hr = pDXGIDevice->GetAdapter(&pCurrentAdapter); + + if (SUCCEEDED(hr) && pCurrentAdapter) + { + DXGI_ADAPTER_DESC desc; + pCurrentAdapter->GetDesc(&desc); + + std::wstring description_w(desc.Description); + + bool changed = false; + + // Check if LUID has changed + if (desc.AdapterLuid.LowPart != gExpectedAdapterLUID.LowPart || + desc.AdapterLuid.HighPart != gExpectedAdapterLUID.HighPart) + { + changed = true; + std::string current_gpu_name = ll_convert_wide_to_string(description_w); + LL_WARNS("Window") << "GPU change detected! Current adapter: " << current_gpu_name << LL_ENDL; + } + + pCurrentAdapter->Release(); + pDXGIDevice->Release(); + + return changed; + } + + if (pDXGIDevice) + { + pDXGIDevice->Release(); + } + } + + return false; +} + +void LLWindowWin32::clearHighPerformanceGPURequest() const +{ + detectGPUChange(); + gExpectedAdapterLUID = { 0, 0 }; + if (gD3D11Context) + { + gD3D11Context->Release(); + gD3D11Context = nullptr; + } + if (gD3D11Device) + { + gD3D11Device->Release(); + gD3D11Device = nullptr; + } + if (gD3D11Library) + { + FreeLibrary(gD3D11Library); + gD3D11Library = nullptr; + } +} + void* LLWindowWin32::getDirectInput8() { return &gDirectInput8; @@ -4503,6 +5247,17 @@ bool LLWindowWin32::getInputDevices(U32 device_type_filter, return false; } +void LLWindowWin32::initWatchdog() +{ + mWindowThread->initTimeout(); + + // Watchdog is effectively a 'login complete event', as the + // 'unstable' part is done and from now on we are tracking + // performance. + // No need to hold D3D11 context/device any more. + clearHighPerformanceGPURequest(); +} + F32 LLWindowWin32::getSystemUISize() { F32 scale_value = 1.f; @@ -4565,6 +5320,18 @@ F32 LLWindowWin32::getSystemUISize() } //static +PROC WINAPI LLWindowWin32::getProcAddress(const char* func) +{ + PROC ret_func = wglGetProcAddress(func); + if (!ret_func && sGLDLLHandle) + { + // Try to fallback to OpenGL32.dll + ret_func = GetProcAddress(sGLDLLHandle, func); + } + return ret_func; +} + +//static std::vector<std::string> LLWindowWin32::getDisplaysResolutionList() { return sMonitorInfo.getResolutionsList(); @@ -4576,28 +5343,27 @@ std::vector<std::string> LLWindowWin32::getDynamicFallbackFontList() // Fonts previously in getFontListSans() have moved to fonts.xml. return std::vector<std::string>(); } + +LLFontFallbackMatch LLWindowWin32::findFallbackFontForChar(llwchar wch) +{ + // Not implemented on Windows; would use DirectWrite (IDWriteFontFallback::MapCharacters). + return LLFontFallbackMatch(); +} #endif // LL_WINDOWS inline LLWindowWin32::LLWindowWin32Thread::LLWindowWin32Thread() - : LL::ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE, true /*should be false, temporary workaround for SL-18721*/) + : LL::ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE, false) { LL::ThreadPool::start(); -} -void LLWindowWin32::LLWindowWin32Thread::close() -{ - if (!mQueue->isClosed()) + // Set thread name for the window thread + // This will make it distinguishable in Visual Studio debugger + post([this]() { - LL_WARNS() << "Closing window thread without using destroy_window_handler" << LL_ENDL; - LL::ThreadPool::close(); - - // Workaround for SL-18721 in case window closes too early and abruptly - LLSplashScreen::show(); - LLSplashScreen::update("..."); // will be updated later - } + SetThreadDescription(GetCurrentThread(), L"LLWindowWin32 Thread"); + }); } - /** * LogChange is to log changes in status while trying to avoid spamming the * log with repeated messages, especially in a tight loop. It refuses to log @@ -4654,6 +5420,8 @@ void LLWindowWin32::LLWindowWin32Thread::checkDXMem() return; } + pauseTimeout(); + IDXGIFactory4* p_factory = nullptr; HRESULT res = CreateDXGIFactory1(__uuidof(IDXGIFactory4), (void**)&p_factory); @@ -4757,6 +5525,8 @@ void LLWindowWin32::LLWindowWin32Thread::checkDXMem() } mGotGLBuffer = true; + + resumeTimeout("checkDXMem"); } void LLWindowWin32::LLWindowWin32Thread::run() @@ -4772,6 +5542,9 @@ void LLWindowWin32::LLWindowWin32Thread::run() timeBeginPeriod(llclamp((U32) 1, tc.wPeriodMin, tc.wPeriodMax)); } + // Normally won't exist yet, but in case of re-init, make sure it's cleaned up + resumeTimeout("Window:WindowThread"); + while (! getQueue().done()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32; @@ -4785,18 +5558,21 @@ void LLWindowWin32::LLWindowWin32Thread::run() BOOL status; if (mhDCThrd == 0) { + pingTimeout("Window:PeekMessage"); LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - PeekMessage"); logger.onChange("PeekMessage(", std::hex, mWindowHandleThrd, ")"); status = PeekMessage(&msg, mWindowHandleThrd, 0, 0, PM_REMOVE); } else { + pingTimeout("Window:GetMessage"); LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - GetMessage"); logger.always("GetMessage(", std::hex, mWindowHandleThrd, ")"); status = GetMessage(&msg, NULL, 0, 0); } if (status > 0) { + pingTimeout("Window:TranslateMessage"); logger.always("got MSG (", std::hex, msg.hwnd, ", ", msg.message, ", ", msg.wParam, ")"); TranslateMessage(&msg); @@ -4808,6 +5584,7 @@ void LLWindowWin32::LLWindowWin32Thread::run() { LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - Function Queue"); + pingTimeout("Window:Queue"); logger.onChange("runPending()"); //process any pending functions getQueue().runPending(); @@ -4821,108 +5598,103 @@ void LLWindowWin32::LLWindowWin32Thread::run() } #endif } + + pauseTimeout(); + destroyWindow(); + + if (mDeleteOnExit) + { + delete this; + } +} + +void LLWindowWin32::LLWindowWin32Thread::destroyWindow() +{ + if (mWindowHandleThrd != NULL && IsWindow(mWindowHandleThrd)) + { + if (mhDCThrd) + { + if (!ReleaseDC(mWindowHandleThrd, mhDCThrd)) + { + LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL; + } + mhDCThrd = NULL; + } + + // This causes WM_DESTROY to be sent *immediately* + if (!destroy_window_handler(mWindowHandleThrd)) + { + LL_WARNS("Window") << "Failed to destroy Window! " << std::hex << GetLastError() << LL_ENDL; + } + } + else + { + // Something killed the window while we were busy destroying gl or handle somehow got broken + LL_WARNS("Window") << "Failed to destroy Window, invalid handle!" << LL_ENDL; + } + mWindowHandleThrd = NULL; + mhDCThrd = NULL; } -void LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy() +bool LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy() { if (mQueue->isClosed()) { - LL_WARNS() << "Tried to close Queue. Win32 thread Queue already closed." << LL_ENDL; - return; + LL_WARNS("Window") << "Tried to close Queue. Win32 thread Queue already closed." << LL_ENDL; + return false; } - // Make sure we don't leave a blank toolbar button. - // Also hiding window now prevents user from suspending it - // via some action (like dragging it around) - ShowWindow(mWindowHandleThrd, SW_HIDE); + // Stop checking budget + mGLReady = false; - // Schedule destruction + // Capture current handle before we lose it HWND old_handle = mWindowHandleThrd; - post([this]() - { - if (IsWindow(mWindowHandleThrd)) - { - if (mhDCThrd) - { - if (!ReleaseDC(mWindowHandleThrd, mhDCThrd)) - { - LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL; - } - mhDCThrd = NULL; - } - // This causes WM_DESTROY to be sent *immediately* - if (!destroy_window_handler(mWindowHandleThrd)) - { - LL_WARNS("Window") << "Failed to destroy Window! " << std::hex << GetLastError() << LL_ENDL; - } - } - else - { - // Something killed the window while we were busy destroying gl or handle somehow got broken - LL_WARNS("Window") << "Failed to destroy Window, invalid handle!" << LL_ENDL; - } - mWindowHandleThrd = NULL; - mhDCThrd = NULL; - mGLReady = false; - }); - - LL_DEBUGS("Window") << "Closing window's pool queue" << LL_ENDL; - mQueue->close(); - - // Post a nonsense user message to wake up the thread in - // case it is waiting for a getMessage() + // Clear the user data to prevent callbacks from finding us if (old_handle) { - WPARAM wparam{ 0xB0B0 }; - LL_DEBUGS("Window") << "PostMessage(" << std::hex << old_handle - << ", " << WM_DUMMY_ - << ", " << wparam << ")" << std::dec << LL_ENDL; - PostMessage(old_handle, WM_DUMMY_, wparam, 0x1337); + SetWindowLongPtr(old_handle, GWLP_USERDATA, NULL); } - // There are cases where window will refuse to close, - // can't wait forever on join, check state instead - LLTimer timeout; - timeout.setTimerExpirySec(2.0); - while (!getQueue().done() && !timeout.hasExpired() && mWindowHandleThrd) - { - ms_sleep(100); - } + // Signal thread to clean up when done + mDeleteOnExit = true; - if (getQueue().done() || mWindowHandleThrd == NULL) + LL_INFOS("Window") << "Detaching window's thread" << LL_ENDL; + // Cleanly detach threads instead of joining them to avoid blocking the main thread + // This is acceptable since the thread will self-delete with mDeleteOnExit + // Doing it before close() to make sure thread doesn't die before or mid detach. + for (auto& pair : mThreads) { - // Window is closed, started closing or is cleaning up - // now wait for our single thread to die. - if (mWindowHandleThrd) - { - LL_INFOS("Window") << "Window is closing, waiting on pool's thread to join, time since post: " << timeout.getElapsedSeconds() << "s" << LL_ENDL; - } - else - { - LL_DEBUGS("Window") << "Waiting on pool's thread, time since post: " << timeout.getElapsedSeconds() << "s" << LL_ENDL; + try { + // Only detach if the thread is joinable + if (pair.second.joinable()) + { + pair.second.detach(); + } } - for (auto& pair : mThreads) - { - pair.second.join(); + catch (const std::system_error& e) { + LL_WARNS("Window") << "Exception detaching thread: " << e.what() << LL_ENDL; } } - else + + // Close the queue. + LL_INFOS("Window") << "Closing window's pool queue" << LL_ENDL; + mQueue->close(); + + // Wake up the thread if it's stuck in GetMessage() + if (old_handle) { - // Something suspended window thread, can't afford to wait forever - // so kill thread instead - // Ex: This can happen if user starts dragging window arround (if it - // was visible) or a modal notification pops up - LL_WARNS("Window") << "Window is frozen, couldn't perform clean exit" << LL_ENDL; + WPARAM wparam{ 0xB0B0 }; + LL_DEBUGS("Window") << "PostMessage(" << std::hex << old_handle + << ", " << WM_DUMMY_ + << ", " << wparam << ")" << std::dec << LL_ENDL; - for (auto& pair : mThreads) - { - // very unsafe - TerminateThread(pair.second.native_handle(), 0); - pair.second.detach(); - } + // Use PostMessage to signal thread to wake up + PostMessage(old_handle, WM_DUMMY_, wparam, 0x1337); } - LL_DEBUGS("Window") << "thread pool shutdown complete" << LL_ENDL; + + LL_INFOS("Window") << "Thread pool shutdown complete" << LL_ENDL; + return true; } void LLWindowWin32::post(const std::function<void()>& func) @@ -4969,3 +5741,69 @@ void LLWindowWin32::updateWindowRect() }); } } + +bool LLWindowWin32::isSystemAppDarkMode() +{ + HKEY hKey; + DWORD dwValue = 1; // Default to light theme + DWORD dwSize = sizeof(DWORD); + + // Check registry for system theme preference + LSTATUS ret_code = + RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_READ, &hKey); + if (ERROR_SUCCESS == ret_code) + { + if (RegQueryValueExW(hKey, L"AppsUseLightTheme", NULL, NULL, (LPBYTE)&dwValue, &dwSize) != ERROR_SUCCESS) + { + // If AppsUseLightTheme is not found, check SystemUsesLightTheme + dwSize = sizeof(DWORD); + RegQueryValueExW(hKey, L"SystemUsesLightTheme", NULL, NULL, (LPBYTE)&dwValue, &dwSize); + } + RegCloseKey(hKey); + } + + // Return true if dark mode + return dwValue == 0; +} + +void LLWindowWin32::updateWindowTheme() +{ + bool use_dark_mode = isSystemAppDarkMode(); + if (use_dark_mode == mCurrentDarkMode) + { + return; + } + mCurrentDarkMode = use_dark_mode; + + HMODULE hUxTheme = LoadLibraryExW(L"uxtheme.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (hUxTheme) + { + auto SetPreferredAppMode = (fnSetPreferredAppMode)GetProcAddress(hUxTheme, "SetPreferredAppMode"); + if (SetPreferredAppMode) + { + SetPreferredAppMode(use_dark_mode ? ALLOW_DARK : FORCE_LIGHT); + } + FreeLibrary(hUxTheme); + } + BOOL dark_mode(use_dark_mode); + DwmSetWindowAttribute(mWindowHandle, DWMWA_USE_IMMERSIVE_DARK_MODE, &dark_mode, sizeof(dark_mode)); + + LL_INFOS("Window") << "Viewer window theme is set to " << (use_dark_mode ? "dark" : "light") << " mode" << LL_ENDL; +} + +void LLWindowWin32::setCustomIcon() +{ + if (mWindowHandle) + { + HICON hDefaultIcon = LoadIcon(mhInstance, mIconResource); + HICON hSmallIcon = LoadIcon(mhInstance, mIconSmallResource); + mWindowThread->post([=]() + { + SendMessage(mWindowHandle, WM_SETICON, ICON_BIG, (LPARAM)hDefaultIcon); + SendMessage(mWindowHandle, WM_SETICON, ICON_SMALL, (LPARAM)hSmallIcon); + + SetClassLongPtr(mWindowHandle, GCLP_HICON, (LONG_PTR)hDefaultIcon); + SetClassLongPtr(mWindowHandle, GCLP_HICONSM, (LONG_PTR)hSmallIcon); + }); + } +} diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h index 36e89e4586..9ccd0a3ee2 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -40,102 +40,116 @@ // Hack for async host by name #define LL_WM_HOST_RESOLVED (WM_APP + 1) +// For requesting shutdown on uninstall, +// make sure it does not conflict with messages like WM_DUMMY_ +inline constexpr UINT WM_POST_UNINSTALL_ = WM_USER + 0x0019; +inline constexpr DWORD WM_POST_UNINSTALL_MSG_SHUTDOWN = 1; +inline constexpr DWORD WM_POST_UNINSTALL_MSG_UPDATE = 2; + typedef void (*LLW32MsgCallback)(const MSG &msg); class LLWindowWin32 : public LLWindow { public: - /*virtual*/ void show(); - /*virtual*/ void hide(); - /*virtual*/ void close(); - /*virtual*/ bool getVisible(); - /*virtual*/ bool getMinimized(); - /*virtual*/ bool getMaximized(); - /*virtual*/ bool maximize(); - /*virtual*/ void minimize(); - /*virtual*/ void restore(); - /*virtual*/ bool getFullscreen(); - /*virtual*/ bool getPosition(LLCoordScreen *position); - /*virtual*/ bool getSize(LLCoordScreen *size); - /*virtual*/ bool getSize(LLCoordWindow *size); - /*virtual*/ bool setPosition(LLCoordScreen position); - /*virtual*/ bool setSizeImpl(LLCoordScreen size); - /*virtual*/ bool setSizeImpl(LLCoordWindow size); - /*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL); - /*virtual*/ void setTitle(const std::string title); + void show() override; + void hide() override; + void close() override; + bool getVisible() override; + bool getMinimized() override; + bool getMaximized() override; + bool maximize() override; + void minimize() override; + void restore() override; + bool getFullscreen(); + bool getPosition(LLCoordScreen *position) override; + + // Outer window frame size in pixels (includes title bar and borders). + // Note OS Specific behavior: On macOS excludes title bar. + bool getSize(LLCoordScreen *size) override; + + bool getSize(LLCoordWindow *size) override; + bool setPosition(LLCoordScreen position) override; + bool setSizeImpl(LLCoordScreen size) override; + bool setSizeImpl(LLCoordWindow size) override; + bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL) override; + void setTitle(const std::string title) override; void* createSharedContext() override; void makeContextCurrent(void* context) override; void destroySharedContext(void* context) override; - /*virtual*/ void toggleVSync(bool enable_vsync); - /*virtual*/ bool setCursorPosition(LLCoordWindow position); - /*virtual*/ bool getCursorPosition(LLCoordWindow *position); - /*virtual*/ bool getCursorDelta(LLCoordCommon* delta); - /*virtual*/ void showCursor(); - /*virtual*/ void hideCursor(); - /*virtual*/ void showCursorFromMouseMove(); - /*virtual*/ void hideCursorUntilMouseMove(); - /*virtual*/ bool isCursorHidden(); - /*virtual*/ void updateCursor(); - /*virtual*/ ECursorType getCursor() const; - /*virtual*/ void captureMouse(); - /*virtual*/ void releaseMouse(); - /*virtual*/ void setMouseClipping( bool b ); - /*virtual*/ bool isClipboardTextAvailable(); - /*virtual*/ bool pasteTextFromClipboard(LLWString &dst); - /*virtual*/ bool copyTextToClipboard(const LLWString &src); - /*virtual*/ void flashIcon(F32 seconds); - /*virtual*/ F32 getGamma(); - /*virtual*/ bool setGamma(const F32 gamma); // Set the gamma - /*virtual*/ void setFSAASamples(const U32 fsaa_samples); - /*virtual*/ U32 getFSAASamples(); - /*virtual*/ bool restoreGamma(); // Restore original gamma table (before updating gamma) - /*virtual*/ ESwapMethod getSwapMethod() { return mSwapMethod; } - /*virtual*/ void gatherInput(); - /*virtual*/ void delayInputProcessing(); - /*virtual*/ void swapBuffers(); - /*virtual*/ void restoreGLContext() {}; + void toggleVSync(bool enable_vsync) override; + bool setCursorPosition(LLCoordWindow position) override; + bool getCursorPosition(LLCoordWindow *position) override; + bool getCursorDelta(LLCoordCommon* delta) override; + bool isWrapMouse() const override { return !mAbsoluteCursorPosition; }; + void showCursor() override; + void hideCursor() override; + void showCursorFromMouseMove() override; + void hideCursorUntilMouseMove() override; + bool isCursorHidden() override; + void updateCursor() override; + ECursorType getCursor() const override; + void captureMouse() override; + void releaseMouse() override; + void setMouseClipping( bool b ) override; + bool isClipboardTextAvailable() override; + bool pasteTextFromClipboard(LLWString &dst) override; + bool copyTextToClipboard(const LLWString &src) override; + void flashIcon(F32 seconds) override; + F32 getGamma() override; + bool setGamma(const F32 gamma) override; // Set the gamma + void setFSAASamples(const U32 fsaa_samples) override; + U32 getFSAASamples() override; + bool restoreGamma() override; // Restore original gamma table (before updating gamma) + ESwapMethod getSwapMethod() override { return mSwapMethod; } + void gatherInput() override; + void delayInputProcessing() override; + void swapBuffers() override; // handy coordinate space conversion routines - /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordWindow *to); - /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordScreen *to); - /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordGL *to); - /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordWindow *to); - /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordGL *to); - /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordScreen *to); + bool convertCoords(LLCoordScreen from, LLCoordWindow *to) override; + bool convertCoords(LLCoordWindow from, LLCoordScreen *to) override; + bool convertCoords(LLCoordWindow from, LLCoordGL *to) override; + bool convertCoords(LLCoordGL from, LLCoordWindow *to) override; + bool convertCoords(LLCoordScreen from, LLCoordGL *to) override; + bool convertCoords(LLCoordGL from, LLCoordScreen *to) override; - /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions); - /*virtual*/ F32 getNativeAspectRatio(); - /*virtual*/ F32 getPixelAspectRatio(); - /*virtual*/ void setNativeAspectRatio(F32 ratio) { mOverrideAspectRatio = ratio; } + LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override; + F32 getNativeAspectRatio() override; + F32 getPixelAspectRatio() override; + void setNativeAspectRatio(F32 ratio) override { mOverrideAspectRatio = ratio; } - /*virtual*/ bool dialogColorPicker(F32 *r, F32 *g, F32 *b ); + bool dialogColorPicker(F32 *r, F32 *g, F32 *b ) override; - /*virtual*/ void *getPlatformWindow(); - /*virtual*/ void bringToFront(); - /*virtual*/ void focusClient(); + void *getPlatformWindow() override; + void bringToFront() override; + void focusClient() override; - /*virtual*/ void allowLanguageTextInput(LLPreeditor *preeditor, bool b); - /*virtual*/ void setLanguageTextInput( const LLCoordGL & pos ); - /*virtual*/ void updateLanguageTextInputArea(); - /*virtual*/ void interruptLanguageTextInput(); - /*virtual*/ void spawnWebBrowser(const std::string& escaped_url, bool async); + void allowLanguageTextInput(LLPreeditor *preeditor, bool b) override; + void setLanguageTextInput( const LLCoordGL & pos ) override; + void updateLanguageTextInputArea() override; + void interruptLanguageTextInput() override; + void spawnWebBrowser(const std::string& escaped_url, bool async) override; - /*virtual*/ F32 getSystemUISize(); + F32 getSystemUISize() override; LLWindowCallbacks::DragNDropResult completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url ); + static PROC WINAPI getProcAddress(const char* func); static std::vector<std::string> getDisplaysResolutionList(); static std::vector<std::string> getDynamicFallbackFontList(); + static LLFontFallbackMatch findFallbackFontForChar(llwchar wch); static void setDPIAwareness(); - /*virtual*/ void* getDirectInput8(); - /*virtual*/ bool getInputDevices(U32 device_type_filter, + void* getDirectInput8() override; + bool getInputDevices(U32 device_type_filter, std::function<bool(std::string&, LLSD&, void*)> osx_callback, void* win_callback, - void* userdata); + void* userdata) override; U32 getRawWParam() { return mRawWParam; } + void initWatchdog() override; + protected: LLWindowWin32(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags, @@ -146,11 +160,13 @@ protected: void initCursors(); HCURSOR loadColorCursor(LPCTSTR name); bool isValid(); + void setThreadPriorityHigh(); + void setThreadPriorityNormal(); void moveWindow(const LLCoordScreen& position,const LLCoordScreen& size); virtual LLSD getNativeKeyData(); // Changes display resolution. Returns true if successful - bool setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh); + bool setDisplayResolution(S32 width, S32 height, S32 refresh); // Go back to last fullscreen display resolution. bool setFullscreenResolution(); @@ -168,6 +184,17 @@ protected: void handleCompositionMessage(U32 indexes); bool handleImeRequests(WPARAM request, LPARAM param, LRESULT *result); + // Additional function to request and hold a high-performance GPU on Windows 10+ + // + // Laptops can dynamically switch between integrated and discrete GPUs. + // The Viewer has gpu-specific optimizations, and this switching can cause problems and crashes. + // The login screen requires low performance, which can lead to the OS deciding to switch to the integrated GPU. + // To avoid this, we request and hold a high-performance GPU using A D3D11 context until login. + // For diagnostics, we also log GPU changes. + void requestHighPerformanceGPU() const; + bool detectGPUChange() const; + void clearHighPerformanceGPURequest() const; + protected: // // Platform specific methods @@ -195,6 +222,7 @@ protected: HCURSOR mCursor[ UI_CURSOR_COUNT ]; // Array of all mouse cursors LLCoordWindow mCursorPosition; // mouse cursor position, should only be mutated on main thread + bool mAbsoluteCursorPosition; // true if last position was received in absolute coordinates. LLMutex mRawMouseMutex; RAWINPUTDEVICE mRawMouse; LLCoordWindow mLastCursorPosition; // mouse cursor position from previous frame @@ -214,7 +242,9 @@ protected: bool mCustomGammaSet; LPWSTR mIconResource; + LPWSTR mIconSmallResource; bool mInputProcessingPaused; + bool mReceivedSCClose; // received SC_CLOSE and expecting WM_CLOSE // The following variables are for Language Text Input control. // They are all static, since one context is shared by all LLWindowWin32 @@ -241,11 +271,18 @@ protected: bool mMouseVanish; + static HMODULE sGLDLLHandle; + // Cached values of GetWindowRect and GetClientRect to be used by app thread void updateWindowRect(); RECT mRect; RECT mClientRect; + void updateWindowTheme(); + bool isSystemAppDarkMode(); + void setCustomIcon(); + bool mCurrentDarkMode { false }; + struct LLWindowWin32Thread; LLWindowWin32Thread* mWindowThread = nullptr; LLThreadSafeQueue<std::function<void()>> mFunctionQueue; @@ -281,6 +318,7 @@ private: extern LLW32MsgCallback gAsyncMsgCallback; extern LPWSTR gIconResource; +extern LPWSTR gIconSmallResource; S32 OSMessageBoxWin32(const std::string& text, const std::string& caption, U32 type); |
