Compare commits

..

1 Commits

Author SHA1 Message Date
cristian64 95c6b5fc86
Merge 788f140936 into 6b686be5f1 2025-01-05 14:52:41 +00:00
62 changed files with 1935 additions and 1231 deletions

View File

@ -1,24 +0,0 @@
function(dolphin_inject_version_info target)
set(INFO_PLIST_PATH "$<TARGET_BUNDLE_DIR:${target}>/Contents/Info.plist")
add_custom_command(TARGET ${target}
POST_BUILD
COMMAND /usr/libexec/PlistBuddy -c
"Delete :CFBundleShortVersionString"
"${INFO_PLIST_PATH}"
|| true
COMMAND /usr/libexec/PlistBuddy -c
"Delete :CFBundleLongVersionString"
"${INFO_PLIST_PATH}"
|| true
COMMAND /usr/libexec/PlistBuddy -c
"Delete :CFBundleVersion"
"${INFO_PLIST_PATH}"
|| true
COMMAND /usr/libexec/PlistBuddy -c
"Merge '${CMAKE_BINARY_DIR}/Source/Core/VersionInfo.plist'"
"${INFO_PLIST_PATH}")
endfunction()

View File

@ -19,27 +19,28 @@ function(dolphin_make_imported_target_if_missing target lib)
endif()
endfunction()
function(dolphin_optional_system_library out_use_system library)
function(dolphin_optional_system_library library)
string(TOUPPER ${library} upperlib)
set(USE_SYSTEM_${upperlib} "" CACHE STRING "Use system ${library} instead of bundled. ON - Always use system and fail if unavailable, OFF - Always use bundled, AUTO - Use system if available, otherwise use bundled, blank - Delegate to USE_SYSTEM_LIBS. Default is blank.")
if("${USE_SYSTEM_${upperlib}}" STREQUAL "")
if(APPROVED_VENDORED_DEPENDENCIES)
string(TOLOWER ${library} lowerlib)
if(lowerlib IN_LIST APPROVED_VENDORED_DEPENDENCIES)
set(${out_use_system} AUTO PARENT_SCOPE)
set(RESOLVED_USE_SYSTEM_${upperlib} AUTO PARENT_SCOPE)
else()
set(${out_use_system} ON PARENT_SCOPE)
set(RESOLVED_USE_SYSTEM_${upperlib} ON PARENT_SCOPE)
endif()
else()
set(${out_use_system} ${USE_SYSTEM_LIBS} PARENT_SCOPE)
set(RESOLVED_USE_SYSTEM_${upperlib} ${USE_SYSTEM_LIBS} PARENT_SCOPE)
endif()
else()
set(${out_use_system} ${USE_SYSTEM_${upperlib}} PARENT_SCOPE)
set(RESOLVED_USE_SYSTEM_${upperlib} ${USE_SYSTEM_${upperlib}} PARENT_SCOPE)
endif()
endfunction()
function(dolphin_add_bundled_library library use_system bundled_path)
if (${use_system} STREQUAL "AUTO")
function(dolphin_add_bundled_library library bundled_path)
string(TOUPPER ${library} upperlib)
if (${RESOLVED_USE_SYSTEM_${upperlib}} STREQUAL "AUTO")
message(STATUS "No system ${library} was found. Using static ${library} from Externals.")
else()
message(STATUS "Using static ${library} from Externals")
@ -51,9 +52,9 @@ function(dolphin_add_bundled_library library use_system bundled_path)
endfunction()
function(dolphin_find_optional_system_library library bundled_path)
dolphin_optional_system_library(use_system ${library})
dolphin_optional_system_library(${library})
string(TOUPPER ${library} upperlib)
if(use_system)
if(RESOLVED_USE_SYSTEM_${upperlib})
find_package(${library} ${ARGN})
# Yay for cmake packages being inconsistent
if(DEFINED ${library}_FOUND)
@ -61,7 +62,7 @@ function(dolphin_find_optional_system_library library bundled_path)
else()
set(prefix ${upperlib})
endif()
if((NOT ${prefix}_FOUND) AND (NOT ${use_system} STREQUAL "AUTO"))
if((NOT ${found}) AND (NOT ${RESOLVED_USE_SYSTEM_${upperlib}} STREQUAL "AUTO"))
message(FATAL_ERROR "No system ${library} was found. Please install it or set USE_SYSTEM_${upperlib} to AUTO or OFF.")
endif()
endif()
@ -69,17 +70,17 @@ function(dolphin_find_optional_system_library library bundled_path)
message(STATUS "Using system ${library}")
set(${prefix}_TYPE "System" PARENT_SCOPE)
else()
dolphin_add_bundled_library(${library} ${use_system} ${bundled_path})
dolphin_add_bundled_library(${library} ${bundled_path})
set(${prefix}_TYPE "Bundled" PARENT_SCOPE)
endif()
endfunction()
function(dolphin_find_optional_system_library_pkgconfig library search alias bundled_path)
dolphin_optional_system_library(use_system ${library})
dolphin_optional_system_library(${library})
string(TOUPPER ${library} upperlib)
if(use_system)
if(RESOLVED_USE_SYSTEM_${upperlib})
pkg_search_module(${library} ${search} ${ARGN} IMPORTED_TARGET)
if((NOT ${library}_FOUND) AND (NOT ${use_system} STREQUAL "AUTO"))
if((NOT ${library}_FOUND) AND (NOT ${RESOLVED_USE_SYSTEM_${upperlib}} STREQUAL "AUTO"))
message(FATAL_ERROR "No system ${library} was found. Please install it or set USE_SYSTEM_${upperlib} to AUTO or OFF.")
endif()
endif()
@ -88,7 +89,7 @@ function(dolphin_find_optional_system_library_pkgconfig library search alias bun
dolphin_alias_library(${alias} PkgConfig::${library})
set(${library}_TYPE "System" PARENT_SCOPE)
else()
dolphin_add_bundled_library(${library} ${use_system} ${bundled_path})
dolphin_add_bundled_library(${library} ${bundled_path})
set(${library}_TYPE "Bundled" PARENT_SCOPE)
endif()
endfunction()

View File

@ -65,5 +65,6 @@ endfunction()
configure_source_file("Source/Core/Common/scmrev.h")
if(APPLE)
configure_source_file("Source/Core/VersionInfo.plist")
configure_source_file("Source/Core/DolphinQt/Info.plist")
configure_source_file("Source/Core/MacUpdater/Info.plist")
endif()

View File

@ -783,9 +783,14 @@ if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/Common/scmrev.h)
endif()
if(APPLE)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Source/Core)
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/VersionInfo.plist)
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/VersionInfo.plist)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/DolphinQt)
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/DolphinQt/Info.plist)
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/DolphinQt/Info.plist)
endif()
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/MacUpdater)
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/MacUpdater/Info.plist)
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/Source/Core/MacUpdater/Info.plist)
endif()
endif()

View File

@ -1,53 +1,62 @@
set(BUILD_CURL_EXE OFF)
set(BUILD_EXAMPLES OFF)
set(BUILD_LIBCURL_DOCS OFF)
set(BUILD_MISC_DOCS OFF)
set(BUILD_SHARED_LIBS OFF)
set(BUILD_STATIC_LIBS ON)
set(BUILD_TESTING OFF)
set(CURL_ENABLE_EXPORT_TARGET OFF)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/curl/include)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/curl/lib)
set(HTTP_ONLY ON)
set(CURL_LIBS MbedTLS::mbedtls zlibstatic)
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(use_core_foundation ON)
set(CURL_USE_LIBPSL OFF)
set(CURL_USE_LIBSSH2 OFF)
set(CURL_ZLIB OFF CACHE BOOL "" FORCE)
set(CURL_ZSTD OFF)
set(USE_LIBIDN2 OFF)
set(USE_NGHTTP2 OFF)
find_library(SYSTEMCONFIGURATION_FRAMEWORK "SystemConfiguration")
if(NOT SYSTEMCONFIGURATION_FRAMEWORK)
message(FATAL_ERROR "SystemConfiguration framework not found")
endif()
if(UNIX)
# We use mbedtls on Unix(-like) systems and Android.
set(CURL_USE_OPENSSL OFF)
list(APPEND CURL_LIBS "-framework SystemConfiguration")
endif()
# This is set if the dolphin_find_optional_system_library call from the main CMakeLists.txt
# is able to find mbedtls on the system.
if(MBEDTLS_FOUND)
# We can just enable CURL_USE_MBEDTLS.
set(CURL_USE_MBEDTLS ON)
else()
# HACK: Set some internal variables to pretend like mbedtls was found on the system.
# We can't use CURL_USE_MBEDTLS with our copy from Externals, as that flag expects
# mbedtls to be installed (the CMakeLists attempts to search for it with find_package).
set(_ssl_enabled ON)
set(USE_MBEDTLS ON)
set(_curl_ca_bundle_supported TRUE)
file(GLOB SRCS curl/lib/*.c curl/lib/vauth/*.c curl/lib/vquic/*.c curl/lib/vssh/*.c curl/lib/vtls/*.c)
add_library(
curl
STATIC
curl/include/curl/curl.h
${SRCS}
)
set(SEARCH_CA_BUNDLE_PATHS
/etc/ssl/certs/ca-certificates.crt
/etc/pki/tls/certs/ca-bundle.crt
/usr/share/ssl/certs/ca-bundle.crt
/usr/local/share/certs/ca-root-nss.crt
/etc/ssl/cert.pem)
foreach(SEARCH_CA_BUNDLE_PATH ${SEARCH_CA_BUNDLE_PATHS})
if(EXISTS "${SEARCH_CA_BUNDLE_PATH}")
message(STATUS "Found CA bundle: ${SEARCH_CA_BUNDLE_PATH}")
set(CURL_CA_BUNDLE "${SEARCH_CA_BUNDLE_PATH}")
set(CURL_CA_BUNDLE_SET TRUE)
break()
endif()
endforeach()
if(NOT CURL_CA_PATH_SET)
if(EXISTS "/etc/ssl/certs")
set(CURL_CA_PATH "/etc/ssl/certs")
set(CURL_CA_PATH_SET TRUE)
endif()
endif()
if(WIN32)
set(CURL_USE_SCHANNEL ON)
dolphin_disable_warnings(curl)
target_link_libraries(curl ${CURL_LIBS})
target_include_directories(curl PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/curl/include")
target_compile_definitions(curl PRIVATE "BUILDING_LIBCURL=1")
if (WIN32)
target_compile_definitions(curl PUBLIC "CURL_STATICLIB=1" "CURL_DISABLE_LDAP" "USE_WINDOWS_SSPI" "USE_SCHANNEL")
target_link_libraries(curl Crypt32.lib)
else()
target_compile_definitions(curl PUBLIC "CURL_STATICLIB=1" "USE_MBEDTLS=1" "HAVE_CONFIG_H=1" "CURL_DISABLE_LDAP")
if (CURL_CA_PATH_SET)
target_compile_definitions(curl PUBLIC CURL_CA_PATH="${CURL_CA_PATH}")
endif()
endif()
if(ANDROID)
set(CURL_CA_PATH "/system/etc/security/cacerts" CACHE STRING "")
endif()
add_subdirectory(curl)
if(UNIX AND NOT MBEDTLS_FOUND)
# HACK: Manually link with the mbedtls libraries.
target_link_libraries(libcurl_static PRIVATE
MbedTLS::mbedtls
MbedTLS::mbedx509)
endif()
add_library(CURL::libcurl ALIAS curl)

22
Externals/curl/COPYING vendored Normal file
View File

@ -0,0 +1,22 @@
COPYRIGHT AND PERMISSION NOTICE
Copyright (c) 1996 - 2016, Daniel Stenberg, <daniel@haxx.se>, and many
contributors, see the THANKS file.
All rights reserved.
Permission to use, copy, modify, and distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright
notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of a copyright holder shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization of the copyright holder.

2
Externals/curl/curl vendored

@ -1 +1 @@
Subproject commit 75a2079d5c28debb2eaa848ca9430f1fe0d7844c
Subproject commit d755a5f7c009dd63a61b2c745180d8ba937cbfeb

View File

@ -50,14 +50,14 @@
<ClCompile Include="curl\lib\curl_memrchr.c" />
<ClCompile Include="curl\lib\curl_multibyte.c" />
<ClCompile Include="curl\lib\curl_ntlm_core.c" />
<ClCompile Include="curl\lib\curl_ntlm_wb.c" />
<ClCompile Include="curl\lib\curl_path.c" />
<ClCompile Include="curl\lib\curl_range.c" />
<ClCompile Include="curl\lib\curl_rtmp.c" />
<ClCompile Include="curl\lib\curl_sasl.c" />
<ClCompile Include="curl\lib\curl_sha512_256.c" />
<ClCompile Include="curl\lib\curl_sspi.c" />
<ClCompile Include="curl\lib\curl_threads.c" />
<ClCompile Include="curl\lib\curl_trc.c" />
<ClCompile Include="curl\lib\cw-out.c" />
<ClCompile Include="curl\lib\dict.c" />
<ClCompile Include="curl\lib\doh.c" />
<ClCompile Include="curl\lib\dynbuf.c" />
@ -120,7 +120,6 @@
<ClCompile Include="curl\lib\psl.c" />
<ClCompile Include="curl\lib\rand.c" />
<ClCompile Include="curl\lib\rename.c" />
<ClCompile Include="curl\lib\request.c" />
<ClCompile Include="curl\lib\rtsp.c" />
<ClCompile Include="curl\lib\select.c" />
<ClCompile Include="curl\lib\sendf.c" />
@ -168,16 +167,12 @@
<ClCompile Include="curl\lib\vauth\vauth.c" />
<ClCompile Include="curl\lib\vquic\curl_msh3.c" />
<ClCompile Include="curl\lib\vquic\curl_ngtcp2.c" />
<ClCompile Include="curl\lib\vquic\curl_osslq.c" />
<ClCompile Include="curl\lib\vquic\curl_quiche.c" />
<ClCompile Include="curl\lib\vquic\vquic.c" />
<ClCompile Include="curl\lib\vquic\vquic-tls.c" />
<ClCompile Include="curl\lib\vssh\curl_path.c" />
<ClCompile Include="curl\lib\vssh\libssh.c" />
<ClCompile Include="curl\lib\vssh\libssh2.c" />
<ClCompile Include="curl\lib\vssh\wolfssh.c" />
<ClCompile Include="curl\lib\vtls\bearssl.c" />
<ClCompile Include="curl\lib\vtls\cipher_suite.c" />
<ClCompile Include="curl\lib\vtls\gtls.c" />
<ClCompile Include="curl\lib\vtls\hostcheck.c" />
<ClCompile Include="curl\lib\vtls\keylog.c" />
@ -249,6 +244,8 @@
<ClInclude Include="curl\lib\curl_memrchr.h" />
<ClInclude Include="curl\lib\curl_multibyte.h" />
<ClInclude Include="curl\lib\curl_ntlm_core.h" />
<ClInclude Include="curl\lib\curl_ntlm_wb.h" />
<ClInclude Include="curl\lib\curl_path.h" />
<ClInclude Include="curl\lib\curl_printf.h" />
<ClInclude Include="curl\lib\curl_range.h" />
<ClInclude Include="curl\lib\curl_rtmp.h" />
@ -256,11 +253,9 @@
<ClInclude Include="curl\lib\curl_setup.h" />
<ClInclude Include="curl\lib\curl_setup_once.h" />
<ClInclude Include="curl\lib\curl_sha256.h" />
<ClInclude Include="curl\lib\curl_sha512_256.h" />
<ClInclude Include="curl\lib\curl_sspi.h" />
<ClInclude Include="curl\lib\curl_threads.h" />
<ClInclude Include="curl\lib\curl_trc.h" />
<ClInclude Include="curl\lib\cw_out.h" />
<ClInclude Include="curl\lib\dict.h" />
<ClInclude Include="curl\lib\doh.h" />
<ClInclude Include="curl\lib\dynbuf.h" />
@ -313,7 +308,6 @@
<ClInclude Include="curl\lib\psl.h" />
<ClInclude Include="curl\lib\rand.h" />
<ClInclude Include="curl\lib\rename.h" />
<ClInclude Include="curl\lib\request.h" />
<ClInclude Include="curl\lib\rtsp.h" />
<ClInclude Include="curl\lib\select.h" />
<ClInclude Include="curl\lib\sendf.h" />
@ -353,15 +347,11 @@
<ClInclude Include="curl\lib\vauth\vauth.h" />
<ClInclude Include="curl\lib\vquic\curl_msh3.h" />
<ClInclude Include="curl\lib\vquic\curl_ngtcp2.h" />
<ClInclude Include="curl\lib\vquic\curl_osslq.h" />
<ClInclude Include="curl\lib\vquic\curl_quiche.h" />
<ClInclude Include="curl\lib\vquic\vquic.h" />
<ClInclude Include="curl\lib\vquic\vquic-tls.h" />
<ClInclude Include="curl\lib\vquic\vquic_int.h" />
<ClInclude Include="curl\lib\vssh\curl_path.h" />
<ClInclude Include="curl\lib\vssh\ssh.h" />
<ClInclude Include="curl\lib\vtls\bearssl.h" />
<ClInclude Include="curl\lib\vtls\cipher_suites.h" />
<ClInclude Include="curl\lib\vtls\gtls.h" />
<ClInclude Include="curl\lib\vtls\hostcheck.h" />
<ClInclude Include="curl\lib\vtls\keylog.h" />

1049
Externals/curl/curl_config.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -19,9 +19,7 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
@ -328,11 +326,12 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
}
private fun enableFullscreenImmersive() {
WindowCompat.setDecorFitsSystemWindows(window, false)
WindowInsetsControllerCompat(window, window.decorView).let { controller ->
controller.hide(WindowInsetsCompat.Type.systemBars())
controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
}
private fun updateDisplaySettings() {

View File

@ -191,7 +191,7 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_downloadCodes(JN
const std::string gametdb_id = GetJString(env, jGameTdbId);
bool success = true;
const std::vector<Gecko::GeckoCode> codes = Gecko::DownloadCodes(gametdb_id, &success);
const std::vector<Gecko::GeckoCode> codes = Gecko::DownloadCodes(gametdb_id, &success, false);
if (!success)
return nullptr;

View File

@ -1,9 +1,13 @@
// Copyright 2003 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <condition_variable>
#include <EGL/egl.h>
#include <android/log.h>
#include <android/native_window_jni.h>
#include <cstdio>
#include <cstdlib>
#include <fmt/format.h>
#include <jni.h>
#include <memory>
#include <mutex>
#include <optional>
@ -11,12 +15,6 @@
#include <thread>
#include <utility>
#include <EGL/egl.h>
#include <android/log.h>
#include <android/native_window_jni.h>
#include <fmt/format.h>
#include <jni.h>
#include "Common/AndroidAnalytics.h"
#include "Common/Assert.h"
#include "Common/CPUDetect.h"
@ -79,8 +77,6 @@ Common::Event s_update_main_frame_event;
// This exists to prevent surfaces from being destroyed during the boot process,
// as that can lead to the boot process dereferencing nullptr.
std::mutex s_surface_lock;
std::condition_variable s_surface_cv;
bool s_need_nonblocking_alert_msg;
Common::Flag s_is_booting;
@ -486,8 +482,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceChang
if (g_presenter)
g_presenter->ChangeSurface(s_surf);
s_surface_cv.notify_all();
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestroyed(JNIEnv*,
@ -521,8 +515,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestr
ANativeWindow_release(s_surf);
s_surf = nullptr;
}
s_surface_cv.notify_all();
}
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_HasSurface(JNIEnv*, jclass)
@ -614,14 +606,12 @@ static void Run(JNIEnv* env, std::unique_ptr<BootParameters>&& boot, bool riivol
volume.GetDiscNumber()));
}
s_need_nonblocking_alert_msg = true;
std::unique_lock<std::mutex> surface_guard(s_surface_lock);
s_surface_cv.wait(surface_guard, []() { return s_surf != nullptr; });
WindowSystemInfo wsi(WindowSystemType::Android, nullptr, s_surf, s_surf);
wsi.render_surface_scale = GetRenderSurfaceScale(env);
s_need_nonblocking_alert_msg = true;
std::unique_lock<std::mutex> surface_guard(s_surface_lock);
if (BootManager::BootCore(Core::System::GetInstance(), std::move(boot), wsi))
{
static constexpr int WAIT_STEP = 25;

View File

@ -85,8 +85,7 @@ bool IniFile::Section::Delete(std::string_view key)
return false;
values.erase(it);
keys_order.erase(std::ranges::find_if(
keys_order, [&](std::string_view v) { return CaseInsensitiveEquals(key, v); }));
keys_order.erase(std::ranges::find(keys_order, key));
return true;
}

View File

@ -99,13 +99,7 @@ void GenericLogFmt(LogLevel level, LogType type, const char* file, int line, con
static_assert(NumFields == sizeof...(args),
"Unexpected number of replacement fields in format string; did you pass too few or "
"too many arguments?");
#if FMT_VERSION >= 110000
auto&& format_str = fmt::format_string<Args...>(format);
#else
auto&& format_str = format;
#endif
GenericLogFmtImpl(level, type, file, line, format_str, fmt::make_format_args(args...));
GenericLogFmtImpl(level, type, file, line, format, fmt::make_format_args(args...));
}
} // namespace Common::Log

View File

@ -41,17 +41,12 @@ bool MsgAlertFmt(bool yes_no, MsgType style, Common::Log::LogType log_type, cons
static_assert(NumFields == sizeof...(args),
"Unexpected number of replacement fields in format string; did you pass too few or "
"too many arguments?");
#if FMT_VERSION >= 110000
static_assert(std::is_base_of_v<fmt::detail::compile_string, S>);
auto&& format_str = fmt::format_string<Args...>(format);
#elif FMT_VERSION >= 90000
#if FMT_VERSION >= 90000
static_assert(fmt::detail::is_compile_string<S>::value);
auto&& format_str = format;
#else
static_assert(fmt::is_compile_string<S>::value);
auto&& format_str = format;
#endif
return MsgAlertFmtImpl(yes_no, style, log_type, file, line, format_str,
return MsgAlertFmtImpl(yes_no, style, log_type, file, line, format,
fmt::make_format_args(args...));
}
@ -65,9 +60,7 @@ bool MsgAlertFmtT(bool yes_no, MsgType style, Common::Log::LogType log_type, con
static_assert(NumFields == sizeof...(args),
"Unexpected number of replacement fields in format string; did you pass too few or "
"too many arguments?");
#if FMT_VERSION >= 110000
static_assert(std::is_base_of_v<fmt::detail::compile_string, S>);
#elif FMT_VERSION >= 90000
#if FMT_VERSION >= 90000
static_assert(fmt::detail::is_compile_string<S>::value);
#else
static_assert(fmt::is_compile_string<S>::value);

View File

@ -41,7 +41,6 @@ static void LoadFromDTM(Config::Layer* config_layer, Movie::DTMHeader* dtm)
else
config_layer->Set(Config::MAIN_GC_LANGUAGE, static_cast<int>(dtm->language));
config_layer->Set(Config::SYSCONF_WIDESCREEN, dtm->bWidescreen);
config_layer->Set(Config::SYSCONF_COUNTRY, dtm->countryCode);
config_layer->Set(Config::GFX_HACK_EFB_ACCESS_ENABLE, dtm->bEFBAccessEnable);
config_layer->Set(Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM, dtm->bSkipEFBCopyToRam);
@ -70,7 +69,6 @@ void SaveToDTM(Movie::DTMHeader* dtm)
else
dtm->language = Config::Get(Config::MAIN_GC_LANGUAGE);
dtm->bWidescreen = Config::Get(Config::SYSCONF_WIDESCREEN);
dtm->countryCode = Config::Get(Config::SYSCONF_COUNTRY);
dtm->bEFBAccessEnable = Config::Get(Config::GFX_HACK_EFB_ACCESS_ENABLE);
dtm->bSkipEFBCopyToRam = Config::Get(Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM);

View File

@ -776,11 +776,14 @@ bool DSPAssembler::AssemblePass(const std::string& text, int pass)
m_location.line_num = 0;
m_cur_pass = pass;
constexpr int LINEBUF_SIZE = 1024;
char line[LINEBUF_SIZE] = {};
while (!m_failed && fsrc.getline(line, LINEBUF_SIZE))
#define LINEBUF_SIZE 1024
char line[LINEBUF_SIZE] = {0};
while (!m_failed && !fsrc.fail() && !fsrc.eof())
{
int opcode_size = 0;
fsrc.getline(line, LINEBUF_SIZE);
if (fsrc.fail())
break;
m_location.line_text = line;
m_location.line_num++;

View File

@ -17,10 +17,13 @@
namespace Gecko
{
std::vector<GeckoCode> DownloadCodes(std::string gametdb_id, bool* succeeded)
std::vector<GeckoCode> DownloadCodes(std::string gametdb_id, bool* succeeded, bool use_https)
{
// TODO: Fix https://bugs.dolphin-emu.org/issues/11772 so we don't need this workaround
const std::string protocol = use_https ? "https://" : "http://";
// codes.rc24.xyz is a mirror of the now defunct geckocodes.org.
std::string endpoint{"https://codes.rc24.xyz/txt.php?txt=" + gametdb_id};
std::string endpoint{protocol + "codes.rc24.xyz/txt.php?txt=" + gametdb_id};
Common::HttpRequest http;
// The server always redirects once to the same location.

View File

@ -17,7 +17,8 @@ class IniFile;
namespace Gecko
{
std::vector<GeckoCode> LoadCodes(const Common::IniFile& globalIni, const Common::IniFile& localIni);
std::vector<GeckoCode> DownloadCodes(std::string gametdb_id, bool* succeeded);
std::vector<GeckoCode> DownloadCodes(std::string gametdb_id, bool* succeeded,
bool use_https = true);
void SaveCodes(Common::IniFile& inifile, const std::vector<GeckoCode>& gcodes);
std::optional<GeckoCode::Code> DeserializeLine(const std::string& line);

View File

@ -686,7 +686,7 @@ bool CEXIETHERNET::BuiltInBBAInterface::SendFrame(const u8* frame, u32 size)
}
default:
ERROR_LOG_FMT(SP1, "Unsupported EtherType {:#06x}", *ethertype);
ERROR_LOG_FMT(SP1, "Unsupported EtherType {#06x}", *ethertype);
return false;
}

View File

@ -132,8 +132,7 @@ struct DTMHeader
bool bUseFMA;
u8 GBAControllers; // GBA Controllers plugged in (the bits are ports 1-4)
bool bWidescreen; // true indicates SYSCONF aspect ratio is 16:9, false for 4:3
u8 countryCode; // SYSCONF country code
std::array<u8, 5> reserved; // Padding for any new config options
std::array<u8, 6> reserved; // Padding for any new config options
std::array<char, 40> discChange; // Name of iso file to switch to, for two disc games.
std::array<u8, 20> revision; // Git hash
u32 DSPiromHash;

View File

@ -765,14 +765,6 @@ void JitArm64::rlwinmx_internal(UGeckoInstruction inst, u32 sh)
return;
}
if (mask == 0)
{
gpr.SetImmediate(a, 0);
if (inst.Rc)
ComputeRC0(0);
return;
}
gpr.BindToRegister(a, a == s);
if (sh == 0 && mask == 0xFFFFFFFF)
@ -1136,43 +1128,6 @@ void JitArm64::addzex(UGeckoInstruction inst)
int a = inst.RA, d = inst.RD;
if (gpr.IsImm(a) && (gpr.GetImm(a) == 0 || HasConstantCarry()))
{
const u32 imm = gpr.GetImm(a);
const bool is_all_ones = imm == 0xFFFFFFFF;
switch (js.carryFlag)
{
case CarryFlag::InPPCState:
{
gpr.BindToRegister(d, false);
LDRB(IndexType::Unsigned, gpr.R(d), PPC_REG, PPCSTATE_OFF(xer_ca));
ComputeCarry(false);
break;
}
case CarryFlag::InHostCarry:
{
gpr.BindToRegister(d, false);
CSET(gpr.R(d), CCFlags::CC_CS);
ComputeCarry(false);
break;
}
case CarryFlag::ConstantTrue:
{
gpr.SetImmediate(d, imm + 1);
ComputeCarry(is_all_ones);
break;
}
case CarryFlag::ConstantFalse:
{
gpr.SetImmediate(d, imm);
ComputeCarry(false);
break;
}
}
}
else
{
switch (js.carryFlag)
{
case CarryFlag::InPPCState:
@ -1215,7 +1170,6 @@ void JitArm64::addzex(UGeckoInstruction inst)
break;
}
}
}
if (inst.Rc)
ComputeRC0(gpr.R(d));
@ -1262,62 +1216,40 @@ void JitArm64::subfex(UGeckoInstruction inst)
if (gpr.IsImm(a) && (mex || gpr.IsImm(b)))
{
const u32 i = gpr.GetImm(a);
const u32 j = mex ? -1 : gpr.GetImm(b);
const u32 imm = ~i + j;
const bool is_zero = imm == 0;
const bool is_all_ones = imm == 0xFFFFFFFF;
u32 i = gpr.GetImm(a), j = mex ? -1 : gpr.GetImm(b);
gpr.BindToRegister(d, false);
switch (js.carryFlag)
{
case CarryFlag::InPPCState:
{
gpr.BindToRegister(d, false);
ARM64Reg RD = gpr.R(d);
if (is_zero)
{
LDRB(IndexType::Unsigned, RD, PPC_REG, PPCSTATE_OFF(xer_ca));
}
else
{
auto WA = gpr.GetScopedReg();
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
ADDI2R(RD, WA, imm, RD);
}
ADDI2R(gpr.R(d), WA, ~i + j, gpr.R(d));
break;
}
case CarryFlag::InHostCarry:
{
gpr.BindToRegister(d, false);
ARM64Reg RD = gpr.R(d);
if (is_all_ones)
{
// RD = -1 + carry = carry ? 0 : -1
// CSETM sets the destination to -1 if the condition is true, 0
// otherwise. Hence, the condition must be carry clear.
CSETM(RD, CC_CC);
}
else
{
MOVI2R(RD, imm);
ADC(RD, RD, ARM64Reg::WZR);
}
auto WA = gpr.GetScopedReg();
MOVI2R(WA, ~i + j);
ADC(gpr.R(d), WA, ARM64Reg::WZR);
break;
}
case CarryFlag::ConstantTrue:
{
gpr.SetImmediate(d, imm + 1);
gpr.SetImmediate(d, ~i + j + 1);
break;
}
case CarryFlag::ConstantFalse:
{
gpr.SetImmediate(d, imm);
gpr.SetImmediate(d, ~i + j);
break;
}
}
const bool must_have_carry = Interpreter::Helper_Carry(~i, j);
const bool might_have_carry = is_all_ones;
const bool might_have_carry = (~i + j) == 0xFFFFFFFF;
if (must_have_carry)
{
@ -1405,15 +1337,6 @@ void JitArm64::subfzex(UGeckoInstruction inst)
int a = inst.RA, d = inst.RD;
if (gpr.IsImm(a) && HasConstantCarry())
{
const u32 imm = ~gpr.GetImm(a);
const u32 carry = js.carryFlag == CarryFlag::ConstantTrue;
gpr.SetImmediate(d, imm + carry);
ComputeCarry(Interpreter::Helper_Carry(imm, carry));
}
else
{
gpr.BindToRegister(d, d == a);
switch (js.carryFlag)
@ -1448,7 +1371,6 @@ void JitArm64::subfzex(UGeckoInstruction inst)
break;
}
}
}
if (inst.Rc)
ComputeRC0(gpr.R(d));
@ -1514,66 +1436,40 @@ void JitArm64::addex(UGeckoInstruction inst)
if (gpr.IsImm(a) && (mex || gpr.IsImm(b)))
{
const u32 i = gpr.GetImm(a), j = mex ? -1 : gpr.GetImm(b);
const u32 imm = i + j;
const bool is_zero = imm == 0;
const bool is_all_ones = imm == 0xFFFFFFFF;
u32 i = gpr.GetImm(a), j = mex ? -1 : gpr.GetImm(b);
gpr.BindToRegister(d, false);
switch (js.carryFlag)
{
case CarryFlag::InPPCState:
{
gpr.BindToRegister(d, false);
ARM64Reg RD = gpr.R(d);
if (is_zero)
{
LDRB(IndexType::Unsigned, RD, PPC_REG, PPCSTATE_OFF(xer_ca));
}
else
{
auto WA = gpr.GetScopedReg();
LDRB(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(xer_ca));
ADDI2R(RD, WA, imm, RD);
}
ADDI2R(gpr.R(d), WA, i + j, gpr.R(d));
break;
}
case CarryFlag::InHostCarry:
{
gpr.BindToRegister(d, false);
ARM64Reg RD = gpr.R(d);
if (is_zero)
{
// RD = 0 + carry = carry ? 1 : 0
CSET(RD, CC_CS);
}
else if (is_all_ones)
{
// RD = -1 + carry = carry ? 0 : -1
// Note that CSETM sets the destination to -1 if the condition is true,
// and 0 otherwise. Hence, the condition must be carry clear.
CSETM(RD, CC_CC);
}
else
{
MOVI2R(RD, imm);
MOVI2R(RD, i + j);
ADC(RD, RD, ARM64Reg::WZR);
}
break;
}
case CarryFlag::ConstantTrue:
{
gpr.SetImmediate(d, imm + 1);
gpr.SetImmediate(d, i + j + 1);
break;
}
case CarryFlag::ConstantFalse:
{
gpr.SetImmediate(d, imm);
gpr.SetImmediate(d, i + j);
break;
}
}
const bool must_have_carry = Interpreter::Helper_Carry(i, j);
const bool might_have_carry = is_all_ones;
const bool might_have_carry = (i + j) == 0xFFFFFFFF;
if (must_have_carry)
{

View File

@ -817,8 +817,9 @@ void JitArm64::dcbx(UGeckoInstruction inst)
STR(IndexType::Unsigned, loop_counter, PPC_REG, PPCSTATE_OFF_SPR(SPR_CTR));
// downcount -= (WA * reg_cycle_count)
MSUB(reg_downcount, WA, reg_cycle_count, reg_downcount);
MUL(WB, WA, reg_cycle_count);
// ^ Note that this cannot overflow because it's limited by (downcount/cycle_count).
SUB(reg_downcount, reg_downcount, WB);
STR(IndexType::Unsigned, reg_downcount, PPC_REG, PPCSTATE_OFF(downcount));
SetJumpTarget(downcount_is_zero_or_negative);

View File

@ -110,7 +110,7 @@ JitBase::~JitBase()
CPUThreadConfigCallback::RemoveConfigChangedCallback(m_registered_config_callback_id);
}
bool JitBase::DoesConfigNeedRefresh() const
bool JitBase::DoesConfigNeedRefresh()
{
return std::ranges::any_of(JIT_SETTINGS, [this](const auto& pair) {
return this->*pair.first != Config::Get(*pair.second);
@ -276,7 +276,7 @@ bool JitBase::CanMergeNextInstructions(int count) const
return true;
}
bool JitBase::ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op) const
bool JitBase::ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op)
{
if (jo.fp_exceptions)
return (op->opinfo->flags & FL_FLOAT_EXCEPTION) != 0;

View File

@ -167,7 +167,7 @@ protected:
static const std::array<std::pair<bool JitBase::*, const Config::Info<bool>*>, 23> JIT_SETTINGS;
bool DoesConfigNeedRefresh() const;
bool DoesConfigNeedRefresh();
void RefreshConfig();
void InitFastmemArena();
@ -178,16 +178,8 @@ protected:
void CleanUpAfterStackFault();
bool CanMergeNextInstructions(int count) const;
bool HasConstantCarry() const
{
#ifdef _M_ARM_64
return js.carryFlag == CarryFlag::ConstantTrue || js.carryFlag == CarryFlag::ConstantFalse;
#else
return false;
#endif
}
bool ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op) const;
bool ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op);
public:
explicit JitBase(Core::System& system);

View File

@ -6,11 +6,11 @@
#include <algorithm>
#include <cstring>
#include <map>
#include <ranges>
#include <sstream>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include <fmt/format.h>
@ -18,7 +18,6 @@
#include "Common/IOFile.h"
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Common/Unreachable.h"
#include "Core/Core.h"
#include "Core/Debugger/DebugInterface.h"
#include "Core/PowerPC/MMU.h"
@ -252,7 +251,7 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
continue;
// Support CodeWarrior and Dolphin map
if (StripWhitespace(line).ends_with(" section layout") || strcmp(temp, ".text") == 0 ||
if (std::string_view{line}.ends_with(" section layout\n") || strcmp(temp, ".text") == 0 ||
strcmp(temp, ".init") == 0)
{
section_name = temp;
@ -311,7 +310,7 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
continue;
column_count = 2;
// Three columns format (with optional alignment):
// Three columns format:
// Starting Virtual
// address Size address
// -----------------------
@ -320,7 +319,7 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
else
iss.str("");
// Four columns format (with optional alignment):
// Four columns format:
// Starting Virtual File
// address Size address offset
// ---------------------------------
@ -328,77 +327,85 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
column_count = 4;
}
u32 address;
u32 vaddress;
u32 size = 0;
u32 offset = 0;
u32 alignment = 0;
char name[512]{};
static constexpr char ENTRY_OF_STRING[] = " (entry of ";
static constexpr std::string_view ENTRY_OF_VIEW(ENTRY_OF_STRING);
auto parse_entry_of = [](char* name) {
if (char* s1 = strstr(name, ENTRY_OF_STRING); s1 != nullptr)
u32 address, vaddress, size, offset, alignment;
char name[512], container[512];
if (column_count == 4)
{
char container[512];
char* ptr = s1 + ENTRY_OF_VIEW.size();
sscanf(ptr, "%511s", container);
// Skip sections, those start with a dot, e.g. (entry of .text)
if (char* s2 = strchr(container, ')'); s2 != nullptr && *container != '.')
// sometimes there is no alignment value, and sometimes it is because it is an entry of
// something else
if (length > 37 && line[37] == ' ')
{
ptr += strlen(container);
// Preserve data after the entry part, usually it contains object names
strcpy(s1, ptr);
*s2 = '\0';
alignment = 0;
sscanf(line, "%08x %08x %08x %08x %511s", &address, &size, &vaddress, &offset, name);
char* s = strstr(line, "(entry of ");
if (s)
{
sscanf(s + 10, "%511s", container);
char* s2 = (strchr(container, ')'));
if (s2 && container[0] != '.')
{
s2[0] = '\0';
strcat(container, "::");
strcat(container, name);
strcpy(name, container);
}
}
};
auto was_alignment = [](const char* name) {
return *name == ' ' || (*name >= '0' && *name <= '9');
};
auto parse_alignment = [](char* name, u32* alignment) {
const std::string buffer(StripWhitespace(name));
return sscanf(buffer.c_str(), "%i %511[^\r\n]", alignment, name);
};
switch (column_count)
}
else
{
sscanf(line, "%08x %08x %08x %08x %i %511s", &address, &size, &vaddress, &offset,
&alignment, name);
}
}
else if (column_count == 3)
{
case 4:
// sometimes there is no alignment value, and sometimes it is because it is an entry of
// something else
sscanf(line, "%08x %08x %08x %08x %511[^\r\n]", &address, &size, &vaddress, &offset, name);
if (was_alignment(name))
parse_alignment(name, &alignment);
// The `else` statement was omitted to handle symbol already saved in Dolphin symbol map
// since it doesn't omit the alignment on save for such case.
parse_entry_of(name);
break;
case 3:
// some entries in the table have a function name followed by " (entry of " followed by a
// container name, followed by ")"
// instead of a space followed by a number followed by a space followed by a name
sscanf(line, "%08x %08x %08x %511[^\r\n]", &address, &size, &vaddress, name);
if (was_alignment(name))
parse_alignment(name, &alignment);
// The `else` statement was omitted to handle symbol already saved in Dolphin symbol map
// since it doesn't omit the alignment on save for such case.
parse_entry_of(name);
break;
case 2:
sscanf(line, "%08x %511[^\r\n]", &address, name);
if (length > 27 && line[27] != ' ' && strstr(line, "(entry of "))
{
alignment = 0;
sscanf(line, "%08x %08x %08x %511s", &address, &size, &vaddress, name);
char* s = strstr(line, "(entry of ");
if (s)
{
sscanf(s + 10, "%511s", container);
char* s2 = (strchr(container, ')'));
if (s2 && container[0] != '.')
{
s2[0] = '\0';
strcat(container, "::");
strcat(container, name);
strcpy(name, container);
}
}
}
else
{
sscanf(line, "%08x %08x %08x %i %511s", &address, &size, &vaddress, &alignment, name);
}
}
else if (column_count == 2)
{
sscanf(line, "%08x %511s", &address, name);
vaddress = address;
break;
default:
// Should never happen
Common::Unreachable();
size = 0;
}
else
{
break;
}
const char* namepos = strstr(line, name);
if (namepos != nullptr) // would be odd if not :P
strcpy(name, namepos);
name[strlen(name) - 1] = 0;
if (name[strlen(name) - 1] == '\r')
name[strlen(name) - 1] = 0;
// Split the current name string into separate parts, and get the object name
// if it exists.
const std::vector<std::string> parts = SplitString(name, '\t');
const std::string name_string(StripWhitespace(parts.size() > 0 ? parts[0] : name));
const std::string name_string(StripWhitespace(parts[0]));
const std::string object_filename_string =
parts.size() > 1 ? std::string(StripWhitespace(parts[1])) : "";
@ -459,38 +466,42 @@ bool PPCSymbolDB::SaveSymbolMap(const std::string& filename) const
if (!f)
return false;
std::vector<const Common::Symbol*> function_symbols;
std::vector<const Common::Symbol*> data_symbols;
for (const auto& function : m_functions)
{
const Common::Symbol& symbol = function.second;
if (symbol.type == Common::Symbol::Type::Function)
function_symbols.push_back(&symbol);
else
data_symbols.push_back(&symbol);
}
// Write .text section
auto function_symbols =
m_functions |
std::views::filter([](auto f) { return f.second.type == Common::Symbol::Type::Function; }) |
std::views::transform([](auto f) { return f.second; });
f.WriteString(".text section layout\n");
for (const auto& symbol : function_symbols)
{
// Write symbol address, size, virtual address, alignment, name
std::string line = fmt::format("{:08x} {:06x} {:08x} {} {}", symbol.address, symbol.size,
symbol.address, 0, symbol.name);
std::string line = fmt::format("{0:08x} {1:06x} {2:08x} {3} {4}", symbol->address, symbol->size,
symbol->address, 0, symbol->name);
// Also write the object name if it exists
if (!symbol.object_name.empty())
line += fmt::format(" \t{0}", symbol.object_name);
if (!symbol->object_name.empty())
line += fmt::format(" \t{0}", symbol->object_name);
line += "\n";
f.WriteString(line);
}
// Write .data section
auto data_symbols =
m_functions |
std::views::filter([](auto f) { return f.second.type == Common::Symbol::Type::Data; }) |
std::views::transform([](auto f) { return f.second; });
f.WriteString("\n.data section layout\n");
for (const auto& symbol : data_symbols)
{
// Write symbol address, size, virtual address, alignment, name
std::string line = fmt::format("{:08x} {:06x} {:08x} {} {}", symbol.address, symbol.size,
symbol.address, 0, symbol.name);
std::string line = fmt::format("{0:08x} {1:06x} {2:08x} {3} {4}", symbol->address, symbol->size,
symbol->address, 0, symbol->name);
// Also write the object name if it exists
if (!symbol.object_name.empty())
line += fmt::format(" \t{0}", symbol.object_name);
if (!symbol->object_name.empty())
line += fmt::format(" \t{0}", symbol->object_name);
line += "\n";
f.WriteString(line);
}

View File

@ -467,7 +467,12 @@ OnlineSystemUpdater::Response OnlineSystemUpdater::GetSystemTitles()
// but the backing data CDN is still active and accessible from other URLs. We take advantage
// of this by hosting our own NetUpdateSOAP endpoint which serves the correct list of titles to
// install along with URLs for the Wii U CDN.
#ifdef ANDROID
// HTTPS is unsupported on Android (https://bugs.dolphin-emu.org/issues/11772).
base_url = "http://fakenus.dolphin-emu.org";
#else
base_url = "https://fakenus.dolphin-emu.org";
#endif
}
const std::string url = fmt::format("{}/nus/services/NetUpdateSOAP", base_url);

View File

@ -573,11 +573,12 @@ endif()
if(APPLE)
include(BundleUtilities)
set(BUNDLE_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/DolphinQt.app)
# Ask for an application bundle.
set_target_properties(dolphin-emu PROPERTIES
MACOSX_BUNDLE true
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in"
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/Info.plist"
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
OUTPUT_NAME DolphinQt
)
@ -614,9 +615,6 @@ if(APPLE)
source_group("Resources" FILES "${CMAKE_SOURCE_DIR}/Data/${res}")
endforeach()
include(DolphinInjectVersionInfo)
dolphin_inject_version_info(dolphin-emu)
# Copy MoltenVK into the bundle
if(ENABLE_VULKAN)
if(USE_BUNDLED_MOLTENVK)
@ -651,7 +649,7 @@ if(APPLE)
COMMAND "${CMAKE_SOURCE_DIR}/Tools/mac-codesign.sh"
"-e" "${CMAKE_CURRENT_SOURCE_DIR}/DolphinEmu$<$<CONFIG:Debug>:Debug>.entitlements"
"${MACOS_CODE_SIGNING_IDENTITY}"
"$<TARGET_BUNDLE_DIR:dolphin-emu>"
"${BUNDLE_PATH}"
)
endif()
else()

View File

@ -74,7 +74,7 @@ void ConfigStringChoice::OnConfigChanged()
ConfigComplexChoice::ConfigComplexChoice(const InfoVariant setting1, const InfoVariant setting2,
Config::Layer* layer)
: m_layer(layer), m_setting1(setting1), m_setting2(setting2)
: m_setting1(setting1), m_setting2(setting2), m_layer(layer)
{
connect(&Settings::Instance(), &Settings::ConfigChanged, this, &ConfigComplexChoice::Refresh);
connect(this, &QComboBox::currentIndexChanged, this, &ConfigComplexChoice::SaveValue);
@ -115,7 +115,7 @@ void ConfigComplexChoice::Reset()
void ConfigComplexChoice::SaveValue(int choice)
{
auto Set = [this](auto& setting, auto& value) {
auto Set = [this, choice](auto& setting, auto& value) {
if (m_layer != nullptr)
{
m_layer->Set(setting.GetLocation(), value);

View File

@ -34,8 +34,7 @@ using SIDeviceName = std::pair<SerialInterface::SIDevices, const char*>;
static constexpr std::array s_gc_types = {
SIDeviceName{SerialInterface::SIDEVICE_NONE, _trans("None")},
SIDeviceName{SerialInterface::SIDEVICE_GC_CONTROLLER, _trans("Standard Controller")},
SIDeviceName{SerialInterface::SIDEVICE_WIIU_ADAPTER,
_trans("GameCube Controller Adapter (USB)")},
SIDeviceName{SerialInterface::SIDEVICE_WIIU_ADAPTER, _trans("GameCube Adapter for Wii U")},
SIDeviceName{SerialInterface::SIDEVICE_GC_STEERING, _trans("Steering Wheel")},
SIDeviceName{SerialInterface::SIDEVICE_DANCEMAT, _trans("Dance Mat")},
SIDeviceName{SerialInterface::SIDEVICE_GC_TARUKONGA, _trans("DK Bongos")},

View File

@ -36,7 +36,7 @@ AdvancedWidget::AdvancedWidget(GraphicsWindow* parent)
OnEmulationStateChanged(state != Core::State::Uninitialized);
});
connect(m_manual_texture_sampling, &QCheckBox::toggled,
[parent] { emit parent->UseFastTextureSamplingChanged(); });
[this, parent] { emit parent->UseFastTextureSamplingChanged(); });
OnBackendChanged();
OnEmulationStateChanged(!Core::IsUninitialized(Core::System::GetInstance()));
@ -262,7 +262,7 @@ void AdvancedWidget::ConnectWidgets()
m_dump_base_textures->setEnabled(checked);
});
connect(m_enable_graphics_mods, &QCheckBox::toggled, this,
[](bool checked) { emit Settings::Instance().EnableGfxModsChanged(checked); });
[this](bool checked) { emit Settings::Instance().EnableGfxModsChanged(checked); });
#if defined(HAVE_FFMPEG)
connect(m_dump_use_lossless, &QCheckBox::toggled, this,
[this](bool checked) { m_dump_bitrate->setEnabled(!checked); });

View File

@ -32,7 +32,7 @@ GCPadWiiUConfigDialog::~GCPadWiiUConfigDialog()
void GCPadWiiUConfigDialog::CreateLayout()
{
setWindowTitle(tr("GameCube Controller Adapter at Port %1").arg(m_port + 1));
setWindowTitle(tr("GameCube Adapter for Wii U at Port %1").arg(m_port + 1));
m_layout = new QVBoxLayout();
m_status_label = new QLabel();

View File

@ -4,6 +4,7 @@
#include "DolphinQt/Config/Mapping/IOWindow.h"
#include <optional>
#include <thread>
#include <QBrush>
#include <QColor>
@ -19,15 +20,16 @@
#include <QSlider>
#include <QSpinBox>
#include <QTableWidget>
#include <QTimer>
#include <QVBoxLayout>
#include "Core/Core.h"
#include "DolphinQt/Config/Mapping/MappingCommon.h"
#include "DolphinQt/Config/Mapping/MappingIndicator.h"
#include "DolphinQt/Config/Mapping/MappingWidget.h"
#include "DolphinQt/Config/Mapping/MappingWindow.h"
#include "DolphinQt/QtUtils/BlockUserInputFilter.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
#include "DolphinQt/Settings.h"
#include "InputCommon/ControlReference/ControlReference.h"
@ -38,9 +40,6 @@
namespace
{
constexpr auto INPUT_DETECT_TIME = std::chrono::seconds(2);
constexpr auto OUTPUT_TEST_TIME = std::chrono::seconds(2);
QTextCharFormat GetSpecialCharFormat()
{
QTextCharFormat format;
@ -229,17 +228,15 @@ private:
bool m_should_paint_state_indicator = false;
};
IOWindow::IOWindow(MappingWindow* window, ControllerEmu::EmulatedController* controller,
IOWindow::IOWindow(MappingWidget* parent, ControllerEmu::EmulatedController* controller,
ControlReference* ref, IOWindow::Type type)
: QDialog(window), m_reference(ref), m_original_expression(ref->GetExpression()),
: QDialog(parent), m_reference(ref), m_original_expression(ref->GetExpression()),
m_controller(controller), m_type(type)
{
SetQWidgetWindowDecorations(this);
CreateMainLayout();
connect(window, &MappingWindow::Update, this, &IOWindow::Update);
connect(window, &MappingWindow::ConfigChanged, this, &IOWindow::ConfigChanged);
connect(parent, &MappingWidget::Update, this, &IOWindow::Update);
connect(parent->GetParent(), &MappingWindow::ConfigChanged, this, &IOWindow::ConfigChanged);
connect(&Settings::Instance(), &Settings::ConfigChanged, this, &IOWindow::ConfigChanged);
setWindowTitle(type == IOWindow::Type::Input ? tr("Configure Input") : tr("Configure Output"));
@ -261,29 +258,18 @@ void IOWindow::CreateMainLayout()
m_devices_combo = new QComboBox();
m_option_list = new QTableWidget();
m_select_button =
new QPushButton(m_type == IOWindow::Type::Input ? tr("Insert Input") : tr("Insert Output"));
m_detect_button = new QPushButton(tr("Detect Input"), this);
m_test_button = new QPushButton(tr("Test Output"), this);
m_select_button = new QPushButton(tr("Select"));
m_detect_button = new QPushButton(tr("Detect"), this);
m_test_button = new QPushButton(tr("Test"), this);
m_button_box = new QDialogButtonBox();
m_clear_button = new QPushButton(tr("Clear"));
m_scalar_spinbox = new QSpinBox();
if (m_type == Type::Input)
{
m_parse_text = new InputStateLineEdit([this] {
const auto lock = m_controller->GetStateLock();
return m_reference->GetState<ControlState>();
});
}
else
{
m_parse_text = new InputStateLineEdit([this] {
const auto lock = m_controller->GetStateLock();
return m_output_test_timer->isActive() * m_reference->range;
});
}
m_parse_text->setReadOnly(true);
m_expression_text = new QPlainTextEdit();
m_expression_text->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
@ -433,17 +419,11 @@ void IOWindow::CreateMainLayout()
m_button_box->addButton(m_clear_button, QDialogButtonBox::ActionRole);
m_button_box->addButton(QDialogButtonBox::Ok);
m_output_test_timer = new QTimer(this);
m_output_test_timer->setSingleShot(true);
setLayout(m_main_layout);
}
void IOWindow::ConfigChanged()
{
emit DetectInputComplete();
emit TestOutputComplete();
const QSignalBlocker blocker(this);
const auto lock = ControllerEmu::EmulatedController::GetStateLock();
@ -464,31 +444,6 @@ void IOWindow::Update()
{
m_option_list->viewport()->update();
m_parse_text->update();
if (!m_input_detector)
return;
if (m_input_detector->IsComplete())
{
const auto results = m_input_detector->TakeResults();
emit DetectInputComplete();
if (results.empty())
return;
// Select the first detected input.
auto list = m_option_list->findItems(QString::fromStdString(results.front().input->GetName()),
Qt::MatchFixedString);
if (list.empty())
return;
m_option_list->setCurrentItem(list.front());
}
else
{
m_input_detector->Update(INPUT_DETECT_TIME, {}, INPUT_DETECT_TIME);
}
}
void IOWindow::ConnectWidgets()
@ -498,50 +453,8 @@ void IOWindow::ConnectWidgets()
connect(&Settings::Instance(), &Settings::ReleaseDevices, this, &IOWindow::ReleaseDevices);
connect(&Settings::Instance(), &Settings::DevicesChanged, this, &IOWindow::UpdateDeviceList);
// Input detection:
// Clicking "Detect" button starts a timer before the actual detection.
auto* const input_detect_start_timer = new QTimer(this);
input_detect_start_timer->setSingleShot(true);
connect(m_detect_button, &QPushButton::clicked, [this, input_detect_start_timer] {
m_detect_button->setText(tr("[ ... ]"));
input_detect_start_timer->start(MappingCommon::INPUT_DETECT_INITIAL_DELAY);
});
connect(input_detect_start_timer, &QTimer::timeout, [this] {
m_detect_button->setText(tr("[ Press Now ]"));
m_input_detector = std::make_unique<ciface::Core::InputDetector>();
const auto lock = m_controller->GetStateLock();
m_input_detector->Start(g_controller_interface, {m_devq.ToString()});
QtUtils::InstallKeyboardBlocker(m_detect_button, this, &IOWindow::DetectInputComplete);
});
connect(this, &IOWindow::DetectInputComplete,
[this, initial_text = m_detect_button->text(), input_detect_start_timer] {
input_detect_start_timer->stop();
m_input_detector.reset();
m_detect_button->setText(initial_text);
});
// Rumble testing:
connect(m_test_button, &QPushButton::clicked, [this] {
// Stop if already started.
if (m_output_test_timer->isActive())
{
emit IOWindow::TestOutputComplete();
return;
}
m_test_button->setText(QStringLiteral("[ ... ]"));
m_output_test_timer->start(OUTPUT_TEST_TIME);
const auto lock = m_controller->GetStateLock();
m_reference->State(1.0);
});
connect(m_output_test_timer, &QTimer::timeout,
[this, initial_text = m_test_button->text()] { emit TestOutputComplete(); });
connect(this, &IOWindow::TestOutputComplete, [this, initial_text = m_test_button->text()] {
m_output_test_timer->stop();
m_test_button->setText(initial_text);
const auto lock = m_controller->GetStateLock();
m_reference->State(0.0);
});
connect(this, &IOWindow::closeEvent, this, &IOWindow::TestOutputComplete);
connect(m_detect_button, &QPushButton::clicked, this, &IOWindow::OnDetectButtonPressed);
connect(m_test_button, &QPushButton::clicked, this, &IOWindow::OnTestButtonPressed);
connect(m_button_box, &QDialogButtonBox::clicked, this, &IOWindow::OnDialogButtonPressed);
connect(m_devices_combo, &QComboBox::currentTextChanged, this, &IOWindow::OnDeviceChanged);
@ -633,10 +546,30 @@ void IOWindow::OnDialogButtonPressed(QAbstractButton* button)
}
}
void IOWindow::OnDetectButtonPressed()
{
const auto expression =
MappingCommon::DetectExpression(m_detect_button, g_controller_interface, {m_devq.ToString()},
m_devq, ciface::MappingCommon::Quote::Off);
if (expression.isEmpty())
return;
const auto list = m_option_list->findItems(expression, Qt::MatchFixedString);
// Try to select the first. If this fails, the last selected item would still appear as such
if (!list.empty())
m_option_list->setCurrentItem(list[0]);
}
void IOWindow::OnTestButtonPressed()
{
MappingCommon::TestOutput(m_test_button, static_cast<OutputReference*>(m_reference));
}
void IOWindow::OnRangeChanged(int value)
{
m_reference->range = value / 100.0;
emit TestOutputComplete();
}
void IOWindow::ReleaseDevices()
@ -737,8 +670,6 @@ void IOWindow::UpdateDeviceList()
void IOWindow::UpdateExpression(std::string new_expression, UpdateMode mode)
{
emit TestOutputComplete();
const auto lock = m_controller->GetStateLock();
if (mode != UpdateMode::Force && new_expression == m_reference->GetExpression())
return;
@ -788,7 +719,6 @@ InputStateDelegate::InputStateDelegate(IOWindow* parent, int column,
InputStateLineEdit::InputStateLineEdit(std::function<ControlState()> state_evaluator)
: m_state_evaluator(std::move(state_evaluator))
{
setReadOnly(true);
}
static void PaintStateIndicator(QPainter& painter, const QRect& region, ControlState state)

View File

@ -12,10 +12,11 @@
#include <QString>
#include <QSyntaxHighlighter>
#include "Common/Flag.h"
#include "InputCommon/ControllerInterface/CoreDevice.h"
class ControlReference;
class MappingWindow;
class MappingWidget;
class QAbstractButton;
class QDialogButtonBox;
class QLineEdit;
@ -65,13 +66,9 @@ public:
Output
};
explicit IOWindow(MappingWindow* window, ControllerEmu::EmulatedController* m_controller,
explicit IOWindow(MappingWidget* parent, ControllerEmu::EmulatedController* m_controller,
ControlReference* ref, Type type);
signals:
void DetectInputComplete();
void TestOutputComplete();
private:
std::shared_ptr<ciface::Core::Device> GetSelectedDevice() const;
@ -82,6 +79,8 @@ private:
void OnDialogButtonPressed(QAbstractButton* button);
void OnDeviceChanged();
void OnDetectButtonPressed();
void OnTestButtonPressed();
void OnRangeChanged(int range);
void AppendSelectedOption();
@ -116,12 +115,10 @@ private:
// Input actions
QPushButton* m_detect_button;
std::unique_ptr<ciface::Core::InputDetector> m_input_detector;
QComboBox* m_functions_combo;
// Output actions
QPushButton* m_test_button;
QTimer* m_output_test_timer;
// Textarea
QPlainTextEdit* m_expression_text;

View File

@ -9,12 +9,13 @@
#include <QString>
#include "DolphinQt/Config/Mapping/IOWindow.h"
#include "DolphinQt/Config/Mapping/MappingCommon.h"
#include "DolphinQt/Config/Mapping/MappingWidget.h"
#include "DolphinQt/Config/Mapping/MappingWindow.h"
#include "DolphinQt/QtUtils/BlockUserInputFilter.h"
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
@ -73,7 +74,7 @@ bool MappingButton::IsInput() const
}
MappingButton::MappingButton(MappingWidget* parent, ControlReference* ref, bool indicator)
: ElidedButton(RefToDisplayString(ref)), m_mapping_window(parent->GetParent()), m_reference(ref)
: ElidedButton(RefToDisplayString(ref)), m_parent(parent), m_reference(ref)
{
if (IsInput())
{
@ -91,22 +92,17 @@ MappingButton::MappingButton(MappingWidget* parent, ControlReference* ref, bool
connect(parent, &MappingWidget::Update, this, &MappingButton::UpdateIndicator);
connect(parent, &MappingWidget::ConfigChanged, this, &MappingButton::ConfigChanged);
connect(this, &MappingButton::ConfigChanged, [this] {
setText(RefToDisplayString(m_reference));
m_is_mapping = false;
});
}
void MappingButton::AdvancedPressed()
{
m_mapping_window->CancelMapping();
IOWindow io(m_mapping_window, m_mapping_window->GetController(), m_reference,
IOWindow io(m_parent, m_parent->GetController(), m_reference,
m_reference->IsInput() ? IOWindow::Type::Input : IOWindow::Type::Output);
SetQWidgetWindowDecorations(&io);
io.exec();
ConfigChanged();
m_mapping_window->Save();
m_parent->SaveSettings();
}
void MappingButton::Clicked()
@ -117,8 +113,31 @@ void MappingButton::Clicked()
return;
}
m_is_mapping = true;
m_mapping_window->QueueInputDetection(this);
const auto default_device_qualifier = m_parent->GetController()->GetDefaultDevice();
QString expression;
if (m_parent->GetParent()->IsMappingAllDevices())
{
expression = MappingCommon::DetectExpression(this, g_controller_interface,
g_controller_interface.GetAllDeviceStrings(),
default_device_qualifier);
}
else
{
expression = MappingCommon::DetectExpression(this, g_controller_interface,
{default_device_qualifier.ToString()},
default_device_qualifier);
}
if (expression.isEmpty())
return;
m_reference->SetExpression(expression.toStdString());
m_parent->GetController()->UpdateSingleControlReference(g_controller_interface, m_reference);
ConfigChanged();
m_parent->SaveSettings();
}
void MappingButton::Clear()
@ -126,21 +145,22 @@ void MappingButton::Clear()
m_reference->range = 100.0 / SLIDER_TICK_COUNT;
m_reference->SetExpression("");
m_mapping_window->GetController()->UpdateSingleControlReference(g_controller_interface,
m_reference);
m_parent->GetController()->UpdateSingleControlReference(g_controller_interface, m_reference);
m_mapping_window->Save();
m_mapping_window->UnQueueInputDetection(this);
m_parent->SaveSettings();
ConfigChanged();
}
void MappingButton::UpdateIndicator()
{
QFont f = m_mapping_window->font();
if (!isActiveWindow())
return;
if (isActiveWindow() && m_reference->IsInput() && m_reference->GetState<bool>() && !m_is_mapping)
QFont f = m_parent->font();
// If the input state is "true" (we can't know the state of outputs), show it in bold.
if (m_reference->IsInput() && m_reference->GetState<bool>())
f.setBold(true);
// If the expression has failed to parse, show it in italic.
// Some expressions still work even the failed to parse so don't prevent the GetState() above.
if (m_reference->GetParseStatus() == ciface::ExpressionParser::ParseStatus::SyntaxError)
@ -149,12 +169,9 @@ void MappingButton::UpdateIndicator()
setFont(f);
}
void MappingButton::StartMapping()
void MappingButton::ConfigChanged()
{
// Focus just makes it more clear which button is currently being mapped.
setFocus();
setText(tr("[ Press Now ]"));
QtUtils::InstallKeyboardBlocker(this, this, &MappingButton::ConfigChanged);
setText(RefToDisplayString(m_reference));
}
void MappingButton::mouseReleaseEvent(QMouseEvent* event)
@ -172,8 +189,3 @@ void MappingButton::mouseReleaseEvent(QMouseEvent* event)
return;
}
}
ControlReference* MappingButton::GetControlReference()
{
return m_reference;
}

View File

@ -3,11 +3,11 @@
#pragma once
#include "Common/Flag.h"
#include "DolphinQt/QtUtils/ElidedButton.h"
class ControlReference;
class MappingWidget;
class MappingWindow;
class QEvent;
class QMouseEvent;
@ -18,21 +18,16 @@ public:
MappingButton(MappingWidget* widget, ControlReference* ref, bool indicator);
bool IsInput() const;
ControlReference* GetControlReference();
void StartMapping();
signals:
void ConfigChanged();
private:
void Clear();
void UpdateIndicator();
void ConfigChanged();
void AdvancedPressed();
void Clicked();
void mouseReleaseEvent(QMouseEvent* event) override;
MappingWindow* const m_mapping_window;
ControlReference* const m_reference;
bool m_is_mapping = false;
MappingWidget* m_parent;
ControlReference* m_reference;
};

View File

@ -3,147 +3,85 @@
#include "DolphinQt/Config/Mapping/MappingCommon.h"
#include <deque>
#include <memory>
#include <chrono>
#include <QApplication>
#include <QPushButton>
#include <QRegularExpression>
#include <QString>
#include <QTimer>
#include "DolphinQt/Config/Mapping/MappingButton.h"
#include "DolphinQt/Config/Mapping/MappingWindow.h"
#include "DolphinQt/QtUtils/BlockUserInputFilter.h"
#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
#include "InputCommon/ControllerInterface/MappingCommon.h"
#include "Common/Thread.h"
namespace MappingCommon
{
constexpr auto INPUT_DETECT_INITIAL_TIME = std::chrono::seconds(3);
constexpr auto INPUT_DETECT_CONFIRMATION_TIME = std::chrono::milliseconds(0);
constexpr auto INPUT_DETECT_MAXIMUM_TIME = std::chrono::seconds(5);
class MappingProcessor : public QWidget
constexpr auto OUTPUT_TEST_TIME = std::chrono::seconds(2);
QString DetectExpression(QPushButton* button, ciface::Core::DeviceContainer& device_container,
const std::vector<std::string>& device_strings,
const ciface::Core::DeviceQualifier& default_device,
ciface::MappingCommon::Quote quote)
{
public:
MappingProcessor(MappingWindow* parent) : QWidget{parent}, m_parent{parent}
{
using MW = MappingWindow;
using MP = MappingProcessor;
const auto filter = new BlockUserInputFilter(button);
connect(parent, &MW::Update, this, &MP::ProcessMappingButtons);
connect(parent, &MW::ConfigChanged, this, &MP::CancelMapping);
button->installEventFilter(filter);
button->grabKeyboard();
button->grabMouse();
connect(parent, &MW::UnQueueInputDetection, this, &MP::UnQueueInputDetection);
connect(parent, &MW::QueueInputDetection, this, &MP::QueueInputDetection);
connect(parent, &MW::CancelMapping, this, &MP::CancelMapping);
const auto old_text = button->text();
button->setText(QStringLiteral("..."));
m_input_detection_start_timer = new QTimer(this);
m_input_detection_start_timer->setSingleShot(true);
connect(m_input_detection_start_timer, &QTimer::timeout, this, &MP::StartInputDetection);
}
// The button text won't be updated if we don't process events here
QApplication::processEvents();
void StartInputDetection()
{
const auto& default_device = m_parent->GetController()->GetDefaultDevice();
auto& button = m_clicked_mapping_buttons.front();
// Avoid that the button press itself is registered as an event
Common::SleepCurrentThread(50);
button->StartMapping();
auto detections =
device_container.DetectInput(device_strings, INPUT_DETECT_INITIAL_TIME,
INPUT_DETECT_CONFIRMATION_TIME, INPUT_DETECT_MAXIMUM_TIME);
std::vector device_strings{default_device.ToString()};
if (m_parent->IsMappingAllDevices())
device_strings = g_controller_interface.GetAllDeviceStrings();
m_input_detector = std::make_unique<ciface::Core::InputDetector>();
const auto lock = m_parent->GetController()->GetStateLock();
m_input_detector->Start(g_controller_interface, device_strings);
}
void ProcessMappingButtons()
{
if (!m_input_detector)
return;
m_input_detector->Update(INPUT_DETECT_INITIAL_TIME, INPUT_DETECT_CONFIRMATION_TIME,
INPUT_DETECT_MAXIMUM_TIME);
if (m_input_detector->IsComplete())
{
auto detections = m_input_detector->TakeResults();
ciface::MappingCommon::RemoveSpuriousTriggerCombinations(&detections);
// No inputs detected. Cancel this and any other queued mappings.
if (detections.empty())
{
CancelMapping();
return;
}
const auto timer = new QTimer(button);
const auto& default_device = m_parent->GetController()->GetDefaultDevice();
auto& button = m_clicked_mapping_buttons.front();
auto* const control_reference = button->GetControlReference();
timer->setSingleShot(true);
control_reference->SetExpression(
BuildExpression(detections, default_device, ciface::MappingCommon::Quote::On));
m_parent->Save();
button->connect(timer, &QTimer::timeout, [button, filter] {
button->releaseMouse();
button->releaseKeyboard();
button->removeEventFilter(filter);
});
m_parent->GetController()->UpdateSingleControlReference(g_controller_interface,
control_reference);
UnQueueInputDetection(button);
}
}
// Prevent mappings of "space", "return", or mouse clicks from re-activating detection.
timer->start(500);
void UpdateInputDetectionStartTimer()
{
m_input_detector.reset();
button->setText(old_text);
if (m_clicked_mapping_buttons.empty())
m_input_detection_start_timer->stop();
else
m_input_detection_start_timer->start(INPUT_DETECT_INITIAL_DELAY);
}
return QString::fromStdString(BuildExpression(detections, default_device, quote));
}
void UnQueueInputDetection(MappingButton* button)
{
std::erase(m_clicked_mapping_buttons, button);
button->ConfigChanged();
UpdateInputDetectionStartTimer();
}
void QueueInputDetection(MappingButton* button)
{
// UnQueue if already queued.
if (std::erase(m_clicked_mapping_buttons, button))
{
button->ConfigChanged();
UpdateInputDetectionStartTimer();
return;
}
button->setText(QStringLiteral("[ ... ]"));
m_clicked_mapping_buttons.push_back(button);
UpdateInputDetectionStartTimer();
}
void CancelMapping()
{
// Signal buttons to take on their proper input expression text.
for (auto* button : m_clicked_mapping_buttons)
button->ConfigChanged();
m_clicked_mapping_buttons = {};
UpdateInputDetectionStartTimer();
}
private:
std::deque<MappingButton*> m_clicked_mapping_buttons;
std::unique_ptr<ciface::Core::InputDetector> m_input_detector;
QTimer* m_input_detection_start_timer;
MappingWindow* const m_parent;
};
void CreateMappingProcessor(MappingWindow* window)
void TestOutput(QPushButton* button, OutputReference* reference)
{
new MappingProcessor{window};
const auto old_text = button->text();
button->setText(QStringLiteral("..."));
// The button text won't be updated if we don't process events here
QApplication::processEvents();
reference->State(1.0);
std::this_thread::sleep_for(OUTPUT_TEST_TIME);
reference->State(0.0);
button->setText(old_text);
}
} // namespace MappingCommon

View File

@ -3,14 +3,23 @@
#pragma once
#include <chrono>
#include <string>
#include <vector>
class MappingWindow;
#include "InputCommon/ControllerInterface/CoreDevice.h"
#include "InputCommon/ControllerInterface/MappingCommon.h"
class QString;
class OutputReference;
class QPushButton;
namespace MappingCommon
{
// A slight delay improves behavior when "clicking" the detect button via key-press.
static constexpr auto INPUT_DETECT_INITIAL_DELAY = std::chrono::milliseconds{100};
QString DetectExpression(QPushButton* button, ciface::Core::DeviceContainer& device_container,
const std::vector<std::string>& device_strings,
const ciface::Core::DeviceQualifier& default_device,
ciface::MappingCommon::Quote quote = ciface::MappingCommon::Quote::On);
void TestOutput(QPushButton* button, OutputReference* reference);
void CreateMappingProcessor(MappingWindow*);
} // namespace MappingCommon

View File

@ -20,6 +20,7 @@
#include "DolphinQt/Config/Mapping/MappingWindow.h"
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/Control/Control.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h"
@ -339,10 +340,10 @@ MappingWidget::CreateSettingAdvancedMappingButton(ControllerEmu::NumericSettingB
setting.SetExpressionFromValue();
// Ensure the UI has the game-controller indicator while editing the expression.
// And cancel in-progress mappings.
ConfigChanged();
IOWindow io(GetParent(), GetController(), &setting.GetInputReference(), IOWindow::Type::Input);
IOWindow io(this, GetController(), &setting.GetInputReference(), IOWindow::Type::Input);
SetQWidgetWindowDecorations(&io);
io.exec();
setting.SimplifyIfPossible();

View File

@ -3,10 +3,15 @@
#pragma once
#include <memory>
#include <vector>
#include <QString>
#include <QWidget>
class ControlGroupBox;
class InputConfig;
class MappingButton;
class MappingNumeric;
class MappingWindow;
class QFormLayout;

View File

@ -41,7 +41,6 @@
#include "DolphinQt/Config/Mapping/HotkeyTAS.h"
#include "DolphinQt/Config/Mapping/HotkeyUSBEmu.h"
#include "DolphinQt/Config/Mapping/HotkeyWii.h"
#include "DolphinQt/Config/Mapping/MappingCommon.h"
#include "DolphinQt/Config/Mapping/WiimoteEmuExtension.h"
#include "DolphinQt/Config/Mapping/WiimoteEmuExtensionMotionInput.h"
#include "DolphinQt/Config/Mapping/WiimoteEmuExtensionMotionSimulation.h"
@ -94,8 +93,6 @@ MappingWindow::MappingWindow(QWidget* parent, Type type, int port_num)
[] { HotkeyManagerEmu::Enable(true); });
filter->connect(filter, &WindowActivationEventFilter::windowActivated,
[] { HotkeyManagerEmu::Enable(false); });
MappingCommon::CreateMappingProcessor(this);
}
void MappingWindow::CreateDevicesLayout()
@ -188,8 +185,9 @@ void MappingWindow::CreateMainLayout()
void MappingWindow::ConnectWidgets()
{
connect(&Settings::Instance(), &Settings::DevicesChanged, this, &MappingWindow::ConfigChanged);
connect(this, &MappingWindow::ConfigChanged, this, &MappingWindow::UpdateDeviceList);
connect(&Settings::Instance(), &Settings::DevicesChanged, this,
&MappingWindow::OnGlobalDevicesChanged);
connect(this, &MappingWindow::ConfigChanged, this, &MappingWindow::OnGlobalDevicesChanged);
connect(m_devices_combo, &QComboBox::currentIndexChanged, this, &MappingWindow::OnSelectDevice);
connect(m_reset_clear, &QPushButton::clicked, this, &MappingWindow::OnClearFieldsPressed);
@ -205,8 +203,6 @@ void MappingWindow::ConnectWidgets()
// We currently use the "Close" button as an "Accept" button so we must save on reject.
connect(this, &QDialog::rejected, [this] { emit Save(); });
connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(m_tab_widget, &QTabWidget::currentChanged, this, &MappingWindow::CancelMapping);
}
void MappingWindow::UpdateProfileIndex()
@ -349,8 +345,6 @@ void MappingWindow::OnSelectDevice(int)
const auto device = m_devices_combo->currentData().toString().toStdString();
m_controller->SetDefaultDevice(device);
emit ConfigChanged();
m_controller->UpdateReferences(g_controller_interface);
}
@ -364,7 +358,7 @@ void MappingWindow::RefreshDevices()
g_controller_interface.RefreshDevices();
}
void MappingWindow::UpdateDeviceList()
void MappingWindow::OnGlobalDevicesChanged()
{
const QSignalBlocker blocker(m_devices_combo);

View File

@ -5,6 +5,9 @@
#include <QDialog>
#include <QString>
#include <memory>
#include "InputCommon/ControllerInterface/CoreDevice.h"
namespace ControllerEmu
{
@ -12,8 +15,6 @@ class EmulatedController;
}
class InputConfig;
class MappingButton;
class QComboBox;
class QDialogButtonBox;
class QEvent;
@ -57,14 +58,10 @@ public:
signals:
// Emitted when config has changed so widgets can update to reflect the change.
void ConfigChanged();
// Emitted at INDICATOR_UPDATE_FREQ Hz for real-time indicators to be updated.
// Emitted at 30hz for real-time indicators to be updated.
void Update();
void Save();
void UnQueueInputDetection(MappingButton*);
void QueueInputDetection(MappingButton*);
void CancelMapping();
private:
void SetMappingType(Type type);
void CreateDevicesLayout();
@ -85,11 +82,11 @@ private:
void UpdateProfileIndex();
void UpdateProfileButtonState();
void PopulateProfileSelection();
void UpdateDeviceList();
void OnDefaultFieldsPressed();
void OnClearFieldsPressed();
void OnSelectDevice(int index);
void OnGlobalDevicesChanged();
ControllerEmu::EmulatedController* m_controller = nullptr;

View File

@ -39,6 +39,12 @@
<string>English</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${DOLPHIN_WC_DESCRIBE}</string>
<key>CFBundleLongVersionString</key>
<string>${DOLPHIN_WC_REVISION}</string>
<key>CFBundleVersion</key>
<string>${DOLPHIN_VERSION_MAJOR}.${DOLPHIN_VERSION_MINOR}</string>
<key>NSHumanReadableCopyright</key>
<string>Licensed under GPL version 2 or later (GPLv2+)</string>
<key>LSApplicationCategoryType</key>

View File

@ -3,34 +3,12 @@
#include "DolphinQt/QtUtils/BlockUserInputFilter.h"
#include <chrono>
#include <QEvent>
#include <QTimer>
namespace QtUtils
bool BlockUserInputFilter::eventFilter(QObject* object, QEvent* event)
{
// Leave filter active for a bit to prevent Return/Space detection from reactivating the button.
constexpr auto REMOVAL_DELAY = std::chrono::milliseconds{100};
BlockKeyboardInputFilter::BlockKeyboardInputFilter(QObject* parent) : QObject{parent}
{
parent->installEventFilter(this);
const QEvent::Type event_type = event->type();
return event_type == QEvent::KeyPress || event_type == QEvent::KeyRelease ||
event_type == QEvent::MouseButtonPress || event_type == QEvent::MouseButtonRelease ||
event_type == QEvent::MouseButtonDblClick;
}
void BlockKeyboardInputFilter::ScheduleRemoval()
{
auto* const timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, &QTimer::timeout, [this] { delete this; });
timer->start(REMOVAL_DELAY);
}
bool BlockKeyboardInputFilter::eventFilter(QObject* object, QEvent* event)
{
const auto event_type = event->type();
return event_type == QEvent::KeyPress || event_type == QEvent::KeyRelease;
}
} // namespace QtUtils

View File

@ -5,26 +5,14 @@
#include <QObject>
namespace QtUtils
{
class QEvent;
class BlockKeyboardInputFilter : public QObject
class BlockUserInputFilter : public QObject
{
Q_OBJECT
public:
BlockKeyboardInputFilter(QObject* parent);
void ScheduleRemoval();
using QObject::QObject;
private:
bool eventFilter(QObject* object, QEvent* event) override;
};
template <typename T>
void InstallKeyboardBlocker(QObject* obj, T* removal_signal_object, void (T::*removal_signal)())
{
removal_signal_object->connect(removal_signal_object, removal_signal,
new QtUtils::BlockKeyboardInputFilter{obj},
&QtUtils::BlockKeyboardInputFilter::ScheduleRemoval);
}
} // namespace QtUtils

View File

@ -345,20 +345,6 @@ auto DeviceContainer::DetectInput(const std::vector<std::string>& device_strings
std::chrono::milliseconds confirmation_wait,
std::chrono::milliseconds maximum_wait) const
-> std::vector<InputDetection>
{
InputDetector input_detector;
input_detector.Start(*this, device_strings);
while (!input_detector.IsComplete())
{
Common::SleepCurrentThread(10);
input_detector.Update(initial_wait, confirmation_wait, maximum_wait);
}
return input_detector.TakeResults();
}
struct InputDetector::Impl
{
struct InputState
{
@ -369,7 +355,7 @@ struct InputDetector::Impl
ControlState last_state = initial_state;
MathUtil::RunningVariance<ControlState> stats;
// Prevent multiple detections until after release.
// Prevent multiiple detections until after release.
bool is_ready = true;
void Update()
@ -406,32 +392,18 @@ struct InputDetector::Impl
std::vector<InputState> input_states;
};
std::vector<DeviceState> device_states;
};
InputDetector::InputDetector() : m_start_time{}, m_state{}
{
}
void InputDetector::Start(const DeviceContainer& container,
const std::vector<std::string>& device_strings)
{
m_start_time = Clock::now();
m_detections = {};
m_state = std::make_unique<Impl>();
// Acquire devices and initial input states.
std::vector<DeviceState> device_states;
for (const auto& device_string : device_strings)
{
DeviceQualifier dq;
dq.FromString(device_string);
auto device = container.FindDevice(dq);
auto device = FindDevice(dq);
if (!device)
continue;
std::vector<Impl::InputState> input_states;
std::vector<InputState> input_states;
for (auto* input : device->Inputs())
{
@ -441,42 +413,38 @@ void InputDetector::Start(const DeviceContainer& container,
// Undesirable axes will have negative values here when trying to map a
// "FullAnalogSurface".
input_states.push_back(Impl::InputState{input});
input_states.push_back(InputState{input});
}
if (!input_states.empty())
{
m_state->device_states.emplace_back(
Impl::DeviceState{std::move(device), std::move(input_states)});
}
device_states.emplace_back(DeviceState{std::move(device), std::move(input_states)});
}
// If no inputs were found via the supplied device strings, immediately complete.
if (m_state->device_states.empty())
m_state.reset();
}
if (device_states.empty())
return {};
void InputDetector::Update(std::chrono::milliseconds initial_wait,
std::chrono::milliseconds confirmation_wait,
std::chrono::milliseconds maximum_wait)
{
if (m_state)
std::vector<InputDetection> detections;
const auto start_time = Clock::now();
while (true)
{
const auto now = Clock::now();
const auto elapsed_time = now - m_start_time;
const auto elapsed_time = now - start_time;
if (elapsed_time >= maximum_wait || (m_detections.empty() && elapsed_time >= initial_wait) ||
(!m_detections.empty() && m_detections.back().release_time.has_value() &&
now >= *m_detections.back().release_time + confirmation_wait))
if (elapsed_time >= maximum_wait || (detections.empty() && elapsed_time >= initial_wait) ||
(!detections.empty() && detections.back().release_time.has_value() &&
now >= *detections.back().release_time + confirmation_wait))
{
m_state.reset();
return;
break;
}
for (auto& device_state : m_state->device_states)
Common::SleepCurrentThread(10);
for (auto& device_state : device_states)
{
for (auto& input_state : device_state.input_states)
for (std::size_t i = 0; i != device_state.input_states.size(); ++i)
{
auto& input_state = device_state.input_states[i];
input_state.Update();
if (input_state.IsPressed())
@ -488,42 +456,26 @@ void InputDetector::Update(std::chrono::milliseconds initial_wait,
const auto smoothness =
1 / std::sqrt(input_state.stats.Variance() / input_state.stats.Mean());
Detection new_detection;
InputDetection new_detection;
new_detection.device = device_state.device;
new_detection.input = input_state.input;
new_detection.press_time = Clock::now();
new_detection.smoothness = smoothness;
// We found an input. Add it to our detections.
m_detections.emplace_back(std::move(new_detection));
detections.emplace_back(std::move(new_detection));
}
}
}
// Check for any releases of our detected inputs.
for (auto& d : m_detections)
for (auto& d : detections)
{
if (!d.release_time.has_value() && d.input->GetState() < (1 - INPUT_DETECT_THRESHOLD))
d.release_time = Clock::now();
}
}
return detections;
}
InputDetector::~InputDetector() = default;
bool InputDetector::IsComplete() const
{
return !m_state;
}
auto InputDetector::GetResults() const -> const std::vector<Detection>&
{
return m_detections;
}
auto InputDetector::TakeResults() -> std::vector<Detection>
{
return std::move(m_detections);
}
} // namespace ciface::Core

View File

@ -245,32 +245,5 @@ protected:
mutable std::recursive_mutex m_devices_mutex;
std::vector<std::shared_ptr<Device>> m_devices;
};
class InputDetector
{
public:
using Detection = DeviceContainer::InputDetection;
InputDetector();
~InputDetector();
void Start(const DeviceContainer& container, const std::vector<std::string>& device_strings);
void Update(std::chrono::milliseconds initial_wait, std::chrono::milliseconds confirmation_wait,
std::chrono::milliseconds maximum_wait);
bool IsComplete() const;
const std::vector<Detection>& GetResults() const;
// move-return'd to prevent copying.
std::vector<Detection> TakeResults();
private:
struct Impl;
Clock::time_point m_start_time;
std::vector<Detection> m_detections;
std::unique_ptr<Impl> m_state;
};
} // namespace Core
} // namespace ciface

View File

@ -16,7 +16,7 @@ add_dependencies(MacUpdater dolphin_scmrev)
set_target_properties(MacUpdater PROPERTIES
MACOSX_BUNDLE true
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in"
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/Info.plist"
OUTPUT_NAME "Dolphin Updater")
target_compile_options(MacUpdater PRIVATE -x objective-c++)
@ -53,9 +53,6 @@ foreach(sb ${STORYBOARDS})
COMMENT "Compiling Storyboard ${sb}...")
endforeach()
include(DolphinInjectVersionInfo)
dolphin_inject_version_info(MacUpdater)
if(NOT SKIP_POSTPROCESS_BUNDLE)
# Update library references to make the bundle portable
include(DolphinPostprocessBundle)

View File

@ -16,6 +16,10 @@
<string>Dolphin Updater</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${DOLPHIN_WC_DESCRIBE}</string>
<key>CFBundleVersion</key>
<string>${DOLPHIN_VERSION_MAJOR}.${DOLPHIN_VERSION_MINOR}</string>
<key>LSMinimumSystemVersion</key>
<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
<key>NSHumanReadableCopyright</key>

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- This plist file is merged with the application's Info.plist to set its version info. -->
<key>CFBundleShortVersionString</key>
<string>${DOLPHIN_WC_DESCRIBE}</string>
<key>CFBundleLongVersionString</key>
<string>${DOLPHIN_WC_REVISION}</string>
<key>CFBundleVersion</key>
<string>${DOLPHIN_VERSION_MAJOR}.${DOLPHIN_VERSION_MINOR}</string>
</dict>
</plist>

View File

@ -259,10 +259,6 @@ void VideoBackend::Shutdown()
void VideoBackend::PrepareWindow(WindowSystemInfo& wsi)
{
#if defined(VK_USE_PLATFORM_METAL_EXT)
// We only need to manually create the CAMetalLayer on macOS.
if (wsi.type != WindowSystemType::MacOS)
return;
// This is kinda messy, but it avoids having to write Objective C++ just to create a metal layer.
id view = reinterpret_cast<id>(wsi.render_surface);
Class clsCAMetalLayer = objc_getClass("CAMetalLayer");

View File

@ -77,6 +77,11 @@ std::pair<std::string, bool> GetNameArbPair(const TextureInfo& texture_info)
}
} // namespace
void HiresTexture::Init()
{
Update();
}
void HiresTexture::Shutdown()
{
Clear();

View File

@ -22,6 +22,7 @@ std::set<std::string> GetTextureDirectoriesWithGameId(const std::string& root_di
class HiresTexture
{
public:
static void Init();
static void Update();
static void Clear();
static void Shutdown();

View File

@ -137,8 +137,10 @@ void PostProcessingConfiguration::LoadOptions(const std::string& code)
std::vector<GLSLStringOption> option_strings;
GLSLStringOption* current_strings = nullptr;
while (!in.eof())
{
std::string line_str;
while (std::getline(in, line_str))
if (std::getline(in, line_str))
{
std::string_view line = line_str;
@ -175,6 +177,7 @@ void PostProcessingConfiguration::LoadOptions(const std::string& code)
}
}
}
}
for (const auto& it : option_strings)
{

View File

@ -99,6 +99,8 @@ TextureCacheBase::TextureCacheBase()
TexDecoder_SetTexFmtOverlayOptions(m_backup_config.texfmt_overlay,
m_backup_config.texfmt_overlay_center);
HiresTexture::Init();
TMEM::InvalidateAll();
}

View File

@ -24,7 +24,7 @@ protected:
}
}
void SetUp() override
void SetUp()
{
if (m_parent_directory.empty())
{

View File

@ -44,7 +44,7 @@ TEST(TMDReader, Validity)
class TMDReaderTest : public ::testing::Test
{
protected:
void SetUp() override = 0;
virtual void SetUp() = 0;
// Common tests.
void TestGeneralInfo();

View File

@ -41,7 +41,7 @@ protected:
m_fs.reset();
File::DeleteDirRecursively(m_profile_path);
}
void SetUp() override
void SetUp()
{
if (UserDirectoryCreationFailed())
{