From f8b85edd0cdf87c9f9a8507258b7989023d028bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joshua=20Vanda=C3=ABle?= Date: Mon, 4 Aug 2025 19:34:31 +0200 Subject: [PATCH] Qt: Better wayland detection to enforce xcb In certain cases, the platform can be "wayland-egl", "wayland-xcomposite", and other values for which I haven't found a full list yet. Instead of matching only "wayland", we now look for "wayland" anywhere in the `QT_QPA_PLATFORM` string in a case-insensitive manner. Acknowledgements: `CaseInsensitiveContains`' implementation was heavily inspired by GNU's non-standard glibc `strcasestr` function, which can be found here licensed under GPLv2 or later: https://ftp.gnu.org/gnu/libc/ --- Source/Core/Common/StringUtil.cpp | 16 +++++++++++ Source/Core/Common/StringUtil.h | 1 + Source/Core/DolphinQt/Main.cpp | 5 ++-- Source/UnitTests/Common/StringUtilTest.cpp | 31 ++++++++++++++++++++++ 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/Source/Core/Common/StringUtil.cpp b/Source/Core/Common/StringUtil.cpp index 935db75d2f..bfd03be9ff 100644 --- a/Source/Core/Common/StringUtil.cpp +++ b/Source/Core/Common/StringUtil.cpp @@ -669,6 +669,22 @@ bool CaseInsensitiveEquals(std::string_view a, std::string_view b) a, b, [](char ca, char cb) { return Common::ToLower(ca) == Common::ToLower(cb); }); } +bool CaseInsensitiveContains(std::string_view haystack, std::string_view needle) +{ + if (needle.empty()) + return true; + + for (size_t i = 0; i + needle.size() <= haystack.size(); ++i) + { + if (std::ranges::equal(needle, haystack.substr(i, needle.size()), + [](char a, char b) { return Common::ToLower(a) == Common::ToLower(b); })) + { + return true; + } + } + return false; +} + bool CaseInsensitiveLess::operator()(std::string_view a, std::string_view b) const { return std::ranges::lexicographical_compare( diff --git a/Source/Core/Common/StringUtil.h b/Source/Core/Common/StringUtil.h index c09d8e3732..7d964199aa 100644 --- a/Source/Core/Common/StringUtil.h +++ b/Source/Core/Common/StringUtil.h @@ -311,6 +311,7 @@ std::string GetEscapedHtml(std::string html); void ToLower(std::string* str); void ToUpper(std::string* str); bool CaseInsensitiveEquals(std::string_view a, std::string_view b); +bool CaseInsensitiveContains(std::string_view a, std::string_view b); // 'std::less'-like comparison function object type for case-insensitive strings. struct CaseInsensitiveLess diff --git a/Source/Core/DolphinQt/Main.cpp b/Source/Core/DolphinQt/Main.cpp index 535c2370e4..8ab4a21ee7 100644 --- a/Source/Core/DolphinQt/Main.cpp +++ b/Source/Core/DolphinQt/Main.cpp @@ -23,6 +23,7 @@ #include "Common/Config/Config.h" #include "Common/MsgHandler.h" #include "Common/ScopeGuard.h" +#include "Common/StringUtil.h" #include "Core/Boot/Boot.h" #include "Core/Config/MainSettings.h" @@ -152,8 +153,8 @@ int main(int argc, char* argv[]) // from happening. // For more information: https://bugs.dolphin-emu.org/issues/11807 const char* current_qt_platform = getenv("QT_QPA_PLATFORM"); - const bool replace_qt_platform = - (current_qt_platform && strcasecmp(current_qt_platform, "wayland") == 0); + const bool replace_qt_platform = current_qt_platform != nullptr && + Common::CaseInsensitiveContains(current_qt_platform, "wayland"); setenv("QT_QPA_PLATFORM", "xcb", replace_qt_platform); #endif diff --git a/Source/UnitTests/Common/StringUtilTest.cpp b/Source/UnitTests/Common/StringUtilTest.cpp index 2be462a1b5..28bd150c06 100644 --- a/Source/UnitTests/Common/StringUtilTest.cpp +++ b/Source/UnitTests/Common/StringUtilTest.cpp @@ -225,3 +225,34 @@ TEST(StringUtil, SplitPathWindowsPathWithDriveLetter) EXPECT_EQ(extension, ""); } #endif + +TEST(StringUtil, CaseInsensitiveContains_BasicMatches) +{ + EXPECT_TRUE(Common::CaseInsensitiveContains("hello world", "hello")); + EXPECT_TRUE(Common::CaseInsensitiveContains("hello world", "world")); + EXPECT_TRUE(Common::CaseInsensitiveContains("HELLO WORLD", "hello")); + EXPECT_TRUE(Common::CaseInsensitiveContains("HeLLo WoRLd", "WORLD")); +} + +TEST(StringUtil, CaseInsensitiveContains_SubstringNotFound) +{ + EXPECT_FALSE(Common::CaseInsensitiveContains("hello world", "hey")); +} + +TEST(StringUtil, CaseInsensitiveContains_EmptyStrings) +{ + EXPECT_TRUE(Common::CaseInsensitiveContains("", "")); + EXPECT_TRUE(Common::CaseInsensitiveContains("hello", "")); + EXPECT_FALSE(Common::CaseInsensitiveContains("", "world")); +} + +TEST(StringUtil, CaseInsensitiveContains_EntireStringMatch) +{ + EXPECT_TRUE(Common::CaseInsensitiveContains("Test", "TEST")); +} + +TEST(StringUtil, CaseInsensitiveContains_OverlappingMatches) +{ + EXPECT_TRUE(Common::CaseInsensitiveContains("aaaaaa", "aa")); + EXPECT_TRUE(Common::CaseInsensitiveContains("ababababa", "bABa")); +}