Compare commits

..

43 Commits

Author SHA1 Message Date
4505760ec7 renderer_gl: Disable stereo on bottom screen 2023-08-07 17:16:01 +03:00
651663e5f7 renderer_gl: Add swap eyes options
* Remove reverse interlaced in favour of the checkbox
2023-08-07 03:27:30 +03:00
a9e5e59d9a core: frontend: Add top-bottom stereo rendering option 2023-08-07 03:27:23 +03:00
96c375229b host_shaders: Improve anaglyph quality
* Use matrices optimized for LCD displays
2023-08-07 03:27:17 +03:00
ea928374a6 renderer_opengl: Adjust interlaced lines to monitor 2023-08-07 03:27:11 +03:00
20f4677f80 qt: Fix dark themes not showing alternating row icons with Qt 6.5.2 (#6862) 2023-08-06 12:24:03 -07:00
0048e61fc7 Fix compilation without ENABLE_WEB_SERVICE (#6856) 2023-08-06 12:23:53 -07:00
aaeba6759e citra_qt: do not pass memory to widget (#6849) 2023-08-04 16:35:52 -07:00
ebac2e4978 custom_tex_manager: Allow old hash in more cases (#6843) 2023-08-03 00:51:10 +03:00
5b9f4d4129 SOC: Fix setting TTL to default value when TTL = 0 (#6835) 2023-08-02 13:06:51 -07:00
b1b6f08926 citra_qt: fix two graphics configuration bugs (#6840) 2023-08-02 21:38:57 +02:00
7e6a761f07 cmake: fix USE_SYSTEM_BOOST behavior ... (#6837) 2023-08-02 12:20:35 -07:00
6f7612f73d Res cache fixes (#6838)
* rasterizer_cache: Dont consider res_scale during recycle

* rasterizer_cache: Switch to plain erase loop

* rasterizer_cache: Fix crash due to memory corruption
2023-08-02 21:38:45 +03:00
88ea66053e Miscallenious fixes to gl backend and qt frontend (#6834)
* renderer_gl: Make rasterizer normal class member

* It doesn't need to be heap allocated anymore

* gl_rasterizer: Remove default_texture

* It's unused

* gl_rasterizer: General cleanup

* gl_rasterizer: Lower case lambdas

* Match style with review comments from vulkan backend

* rasterizer_cache: Prevent memory leak

* Since the switch from shared_ptr these surfaces were no longer being destroyed properly. Use our garbage collector for that purpose to destroy it safely for both backends

* rasterizer_cache: Make temp copy of old surface

* The custom surface would override the memory region of the old region resulting in garbage data, this ensures the custom surface is constructed correctly

* citra_qt: Manually create dialog tabs

* Allows for custom constructors which is very useful. While at it, global state is now eliminated from configuration

* citra_qt: Eliminate global system usage

* core: Remove global system usage in memory and HIO

* citra_qt: Use qOverload

* tests: Run clang format

* gl_texture_runtime: Fix surface scaling
2023-08-02 01:40:39 +03:00
970f2284d8 http/soc: Various implementations and fixes (#6828) 2023-08-02 00:37:56 +02:00
baf3ea4beb custom_tex_manager: Allow old hash in the dumper (#6832) 2023-08-01 20:38:51 +03:00
35e208b447 Move MiiData to its own namespace and add ChecksummedMiiData (#6824)
* Move mii to own namespace and add checksummed mii data

* Fix compile issues

* Make mii classes trivial and add cast operator

* Fix Android side

* Add new line at the end of files.

* Make miidata a struct and crc16 a u32_be as per switch code.

* Apply suggestions

* Change back crc to u16 and set padding to 0.
2023-07-31 20:38:47 -07:00
f8b8b6e53c core: De-globalize movie (#6659) 2023-08-01 02:57:38 +02:00
a955f02771 rasterizer_cache: Remove runtime allocation caching (#6705)
* rasterizer_cache: Sentence surfaces

* gl_texture_runtime: Remove runtime side allocation cache

* rasterizer_cache: Adjust surface scale during reinterpreration

* Fixes pixelated outlines. Also allows to remove the d24s8 specific hack and is more generic in general

* rasterizer_cache: Remove Expand flag

* Begone!

* rasterizer_cache: Cache framebuffers with surface id

* rasterizer_cache: Sentence texture cubes

* renderer_opengl: Move texture mailbox to separate file

* Makes renderer_opengl cleaner overall and allows to report removal threshold from runtime instead of hardcoding. Vulkan requires this

* rasterizer_cache: Dont flush cache on layout change

* rasterizer_cache: Overhaul framebuffer management

* video_core: Remove duplicate

* rasterizer_cache: Sentence custom surfaces

* Vulkan cannot destroy images immediately so this ensures we use our garbage collector for that purpose
2023-08-01 03:35:41 +03:00
3fedc68230 common: Only use libbacktrace if present. (#6827) 2023-07-31 14:24:27 -07:00
335fb78c5c service/am: Clean up and optimize CIA installation. (#6718) 2023-07-30 21:40:35 +02:00
22c4eb86d7 core/frd: Correct FunctionInfo and stub more functions (#6209) 2023-07-30 04:18:52 +02:00
964f9ee3cf service/gsp: Implement saving of framebuffers in SaveVramSysArea. (#6821)
* service/gsp: Implement saving of framebuffers in SaveVramSysArea.

* Address review comments.

* service/apt: Separate capture info and capture buffer info.

The former is used with the RequestForSysApplet message and GetCaptureInfo.
The latter is used with SendCaptureBufferInfo and ReceiveCaptureBufferInfo.
2023-07-29 00:26:24 -07:00
bb364d9bc0 service/apt: Add and implement more service commands. (#6721)
* service/apt: Add and implement more service commands.

* service/apt: Implement power button.

* Address review comments and fix GetApplicationRunningMode bug.
2023-07-29 00:26:16 -07:00
51996c54f0 audio_core\hle\adts_reader.cpp: Use BitField to parse ADTS header (#6719) 2023-07-28 12:15:58 -07:00
539a1a0b6e service/cfg: Separate out config block defaults and create when not found. (#6716) 2023-07-26 10:33:00 -07:00
8b21b902f2 hw/y2r: Templatize input/output formats. (#6717) 2023-07-25 05:51:57 -07:00
19107cec4b citra-qt: update the separate window titles to use full_name and show Primary/Secondary identifier (#6712)
* citra-qt: update the separate window titles to use full_name and Primary/Secondary window identifier

* build_fullname: remove trailing space
2023-07-25 05:51:46 -07:00
a537f56766 services/cfg: Clean up definitions and access flag handling. (#6711) 2023-07-21 18:04:15 -07:00
b5e1a27a7e rasterizer_cache: Handle texture cubes with duplicate faces (#6710) 2023-07-21 01:55:19 +03:00
a9e390b1b1 frontend: Fix clicking primary screen window touching screen. (#6707) 2023-07-20 15:48:50 -07:00
71582a72a4 sdl: Check correct windowID based on event type. (#6703) 2023-07-19 01:29:13 -07:00
e783b0d4a9 rasterizer_cache: Fixes to (unaligned) texture downloads (#6697)
* rasterizer_cache: Header cleanup

* gl_texture_runtime: Fix incorrect stride in single scanline downloads

* texture_codec: Fix unaligned texture downloads
2023-07-18 17:31:31 +03:00
700c00f021 audio_core/hle: Stub binary requests SaveState and LoadState. (#6684)
* audio_core/hle: Stub binary requests SaveState and LoadState.

* audio_core/hle: Fire DSP binary pipe interrupt on request completion instead of tick.
2023-07-16 17:54:40 -07:00
9cb14044ec kernel: Improvements to process cleanup. (#6680)
* kernel: Properly clean up process threads on exit.

* kernel: Track process-owned memory and free on destruction.

* apt: Implement DoApplicationJump via home menu when available.

* kernel: Move TLS allocation management to owning process.
2023-07-16 17:54:29 -07:00
8b6b58a364 y2r: Pass ConversionConfiguration by copying. (#6690) 2023-07-15 01:57:06 -07:00
e043caac27 hle: Eliminate need to specify command headers for IPC. (#6678) 2023-07-14 17:32:59 -07:00
0bedb28bdc rpc: Fix class hierarchy. (#6694) 2023-07-13 16:29:54 -07:00
c10ffda91f android: Restore notification permission request (#6693)
When we targeted API <32, the notification permission would automatically be requested on startup. This restores that behavior temporarily while we work on new UX.
2023-07-12 18:54:21 -07:00
5e8ae4fa8a qt: Fix per-game config loading for 3DSX/ELF. (#6691)
Co-authored-by: Michael Theall <pigman46@gmail.com>
2023-07-12 18:54:11 -07:00
7a7f485640 core: Cleanup RPC (#6674) 2023-07-12 18:54:02 -07:00
bbf833bceb citra_qt\game_list: Make columns hideable (#6467) 2023-07-12 23:17:16 +02:00
8eebb83c2c service: nfc: Keep tag alive while it's being used (#6687) 2023-07-12 14:01:08 -07:00
321 changed files with 8080 additions and 6705 deletions

View File

@ -57,6 +57,7 @@ CMAKE_DEPENDENT_OPTION(ENABLE_TESTS "Enable generating tests executable" ON "NOT
CMAKE_DEPENDENT_OPTION(ENABLE_DEDICATED_ROOM "Enable generating dedicated room executable" ON "NOT ANDROID AND NOT IOS" OFF) CMAKE_DEPENDENT_OPTION(ENABLE_DEDICATED_ROOM "Enable generating dedicated room executable" ON "NOT ANDROID AND NOT IOS" OFF)
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
option(ENABLE_SCRIPTING "Enable RPC server for scripting" ON)
CMAKE_DEPENDENT_OPTION(ENABLE_CUBEB "Enables the cubeb audio backend" ON "NOT IOS" OFF) CMAKE_DEPENDENT_OPTION(ENABLE_CUBEB "Enables the cubeb audio backend" ON "NOT IOS" OFF)
option(ENABLE_OPENAL "Enables the OpenAL audio backend" ON) option(ENABLE_OPENAL "Enables the OpenAL audio backend" ON)
@ -341,13 +342,6 @@ function(get_timestamp _var)
set(${_var} "${timestamp}" PARENT_SCOPE) set(${_var} "${timestamp}" PARENT_SCOPE)
endfunction() endfunction()
# Prevent boost from linking against libs when building
add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY
-DBOOST_SYSTEM_NO_LIB
-DBOOST_DATE_TIME_NO_LIB
-DBOOST_REGEX_NO_LIB
)
# generate git/build information # generate git/build information
include(GetGitRevisionDescription) include(GetGitRevisionDescription)
get_git_head_revision(GIT_REF_SPEC GIT_REV) get_git_head_revision(GIT_REF_SPEC GIT_REV)
@ -355,17 +349,23 @@ git_describe(GIT_DESC --always --long --dirty)
git_branch_name(GIT_BRANCH) git_branch_name(GIT_BRANCH)
get_timestamp(BUILD_DATE) get_timestamp(BUILD_DATE)
if (NOT USE_SYSTEM_BOOST) # Boost
add_definitions( -DBOOST_ALL_NO_LIB ) # Prevent boost from linking against libs when building
add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY
-DBOOST_SYSTEM_NO_LIB
-DBOOST_DATE_TIME_NO_LIB
-DBOOST_REGEX_NO_LIB
)
if (USE_SYSTEM_BOOST)
find_package(Boost 1.70.0 COMPONENTS container locale serialization iostreams REQUIRED)
endif() endif()
enable_testing() enable_testing()
add_subdirectory(externals) add_subdirectory(externals)
# Boost # Boost (bundled)
if (USE_SYSTEM_BOOST) if (NOT USE_SYSTEM_BOOST)
find_package(Boost 1.70.0 COMPONENTS serialization iostreams REQUIRED) add_definitions( -DBOOST_ALL_NO_LIB )
else()
add_library(Boost::boost ALIAS boost) add_library(Boost::boost ALIAS boost)
add_library(Boost::serialization ALIAS boost_serialization) add_library(Boost::serialization ALIAS boost_serialization)
add_library(Boost::iostreams ALIAS boost_iostreams) add_library(Boost::iostreams ALIAS boost_iostreams)

View File

@ -57,9 +57,7 @@ if (DEFINED ENV{CI})
set(BUILD_VERSION ${CMAKE_MATCH_1}) set(BUILD_VERSION ${CMAKE_MATCH_1})
endif() endif()
if (BUILD_VERSION) if (BUILD_VERSION)
# This leaves a trailing space on the last word, but we actually want that set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION}")
# because of how it's styled in the title bar.
set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ")
else() else()
set(BUILD_FULLNAME "") set(BUILD_FULLNAME "")
endif() endif()

View File

@ -298,6 +298,11 @@ QAbstractItemView:read-only {
alternate-background-color: #232629; alternate-background-color: #232629;
} }
/* Workaround for https://bugreports.qt.io/browse/QTBUG-115529 */
QAbstractItemView:item {
border: 0px;
}
QWidget:focus { QWidget:focus {
border: 1px solid #3daee9; border: 1px solid #3daee9;
} }

View File

@ -481,6 +481,11 @@ QAbstractItemView QLineEdit {
padding: 2px; padding: 2px;
} }
/* Workaround for https://bugreports.qt.io/browse/QTBUG-115529 */
QAbstractItemView:item {
border: 0px;
}
/* QAbstractScrollArea ---------------------------------------------------- /* QAbstractScrollArea ----------------------------------------------------
https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qabstractscrollarea https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qabstractscrollarea

View File

@ -12,27 +12,29 @@ include(DownloadExternals)
include(ExternalProject) include(ExternalProject)
# Boost # Boost
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "") if (NOT USE_SYSTEM_BOOST)
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "") message(STATUS "Including vendored Boost library")
set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "") set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
add_library(boost INTERFACE) set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR}) set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "")
add_library(boost INTERFACE)
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR})
# Boost::serialization # Boost::serialization
file(GLOB boost_serialization_SRC "${CMAKE_SOURCE_DIR}/externals/boost/libs/serialization/src/*.cpp") file(GLOB boost_serialization_SRC "${CMAKE_SOURCE_DIR}/externals/boost/libs/serialization/src/*.cpp")
add_library(boost_serialization STATIC ${boost_serialization_SRC}) add_library(boost_serialization STATIC ${boost_serialization_SRC})
target_link_libraries(boost_serialization PUBLIC boost) target_link_libraries(boost_serialization PUBLIC boost)
# Boost::iostreams
add_library(
boost_iostreams
STATIC
${CMAKE_SOURCE_DIR}/externals/boost/libs/iostreams/src/file_descriptor.cpp
${CMAKE_SOURCE_DIR}/externals/boost/libs/iostreams/src/mapped_file.cpp
)
target_link_libraries(boost_iostreams PUBLIC boost)
# Boost::iostreams
add_library(
boost_iostreams
STATIC
${CMAKE_SOURCE_DIR}/externals/boost/libs/iostreams/src/file_descriptor.cpp
${CMAKE_SOURCE_DIR}/externals/boost/libs/iostreams/src/mapped_file.cpp
)
target_link_libraries(boost_iostreams PUBLIC boost)
# Add additional boost libs here; remember to ALIAS them in the root CMakeLists! # Add additional boost libs here; remember to ALIAS them in the root CMakeLists!
endif()
# Catch2 # Catch2
set(CATCH_INSTALL_DOCS OFF CACHE BOOL "") set(CATCH_INSTALL_DOCS OFF CACHE BOOL "")
@ -171,37 +173,38 @@ endif()
add_library(json-headers INTERFACE) add_library(json-headers INTERFACE)
target_include_directories(json-headers INTERFACE ./json) target_include_directories(json-headers INTERFACE ./json)
# OpenSSL
if (USE_SYSTEM_OPENSSL)
find_package(OpenSSL 1.1)
if (OPENSSL_FOUND)
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
endif()
endif()
if (NOT OPENSSL_FOUND)
# LibreSSL
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
set(OPENSSLDIR "/etc/ssl/")
add_subdirectory(libressl EXCLUDE_FROM_ALL)
target_include_directories(ssl INTERFACE ./libressl/include)
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
get_directory_property(OPENSSL_LIBRARIES
DIRECTORY libressl
DEFINITION OPENSSL_LIBS)
endif()
# httplib
add_library(httplib INTERFACE)
target_include_directories(httplib INTERFACE ./httplib)
target_compile_options(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
if(ANDROID)
add_subdirectory(android-ifaddrs)
endif()
# cpp-jwt
if (ENABLE_WEB_SERVICE) if (ENABLE_WEB_SERVICE)
if (USE_SYSTEM_OPENSSL)
find_package(OpenSSL 1.1)
if (OPENSSL_FOUND)
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
endif()
endif()
if (NOT OPENSSL_FOUND)
# LibreSSL
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
set(OPENSSLDIR "/etc/ssl/")
add_subdirectory(libressl EXCLUDE_FROM_ALL)
target_include_directories(ssl INTERFACE ./libressl/include)
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
get_directory_property(OPENSSL_LIBRARIES
DIRECTORY libressl
DEFINITION OPENSSL_LIBS)
endif()
if(ANDROID)
add_subdirectory(android-ifaddrs)
endif()
# httplib
add_library(httplib INTERFACE)
target_include_directories(httplib INTERFACE ./httplib)
target_compile_options(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
# cpp-jwt
add_library(cpp-jwt INTERFACE) add_library(cpp-jwt INTERFACE)
target_include_directories(cpp-jwt INTERFACE ./cpp-jwt/include) target_include_directories(cpp-jwt INTERFACE ./cpp-jwt/include)
target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON) target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON)

View File

@ -29,6 +29,7 @@
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application <application
android:name="org.citra.citra_emu.CitraApplication" android:name="org.citra.citra_emu.CitraApplication"

View File

@ -1,7 +1,10 @@
package org.citra.citra_emu.ui.main; package org.citra.citra_emu.ui.main;
import android.Manifest;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@ -13,6 +16,7 @@ import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.core.splashscreen.SplashScreen; import androidx.core.splashscreen.SplashScreen;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.Collections; import java.util.Collections;
@ -124,6 +128,9 @@ public final class MainActivity extends AppCompatActivity implements MainView {
); );
}); });
private final ActivityResultLauncher<String> requestNotificationPermissionLauncher =
registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { });
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
SplashScreen splashScreen = SplashScreen.installSplashScreen(this); SplashScreen splashScreen = SplashScreen.installSplashScreen(this);
@ -165,6 +172,12 @@ public final class MainActivity extends AppCompatActivity implements MainView {
EmulationActivity.tryDismissRunningNotification(this); EmulationActivity.tryDismissRunningNotification(this);
setInsets(); setInsets();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
requestNotificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
}
}
} }
@Override @Override

View File

@ -53,7 +53,7 @@ void AndroidMiiSelector::Setup(const Frontend::MiiSelectorConfig& config) {
const u32 return_code = static_cast<u32>( const u32 return_code = static_cast<u32>(
env->GetLongField(data, env->GetFieldID(s_mii_selector_data_class, "return_code", "J"))); env->GetLongField(data, env->GetFieldID(s_mii_selector_data_class, "return_code", "J")));
if (return_code == 1) { if (return_code == 1) {
Finalize(return_code, HLE::Applets::MiiData{}); Finalize(return_code, Mii::MiiData{});
return; return;
} }

View File

@ -26,6 +26,7 @@
#include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nfc/nfc.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/savestate.h" #include "core/savestate.h"
#include "core/telemetry_session.h"
#include "jni/android_common/android_common.h" #include "jni/android_common/android_common.h"
#include "jni/applets/mii_selector.h" #include "jni/applets/mii_selector.h"
#include "jni/applets/swkbd.h" #include "jni/applets/swkbd.h"
@ -156,7 +157,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
system.RegisterSoftwareKeyboard(std::make_shared<SoftwareKeyboard::AndroidKeyboard>()); system.RegisterSoftwareKeyboard(std::make_shared<SoftwareKeyboard::AndroidKeyboard>());
// Register microphone permission check // Register microphone permission check
Core::System::GetInstance().RegisterMicPermissionCheck(&CheckMicPermission); system.RegisterMicPermissionCheck(&CheckMicPermission);
InputManager::Init(); InputManager::Init();
@ -166,7 +167,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
return load_result; return load_result;
} }
auto& telemetry_session = Core::System::GetInstance().TelemetrySession(); auto& telemetry_session = system.TelemetrySession();
telemetry_session.AddField(Common::Telemetry::FieldType::App, "Frontend", "Android"); telemetry_session.AddField(Common::Telemetry::FieldType::App, "Frontend", "Android");
stop_run = false; stop_run = false;
@ -187,8 +188,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
audio_stretching_event = audio_stretching_event =
system.CoreTiming().RegisterEvent("AudioStretchingEvent", [&](u64, s64 cycles_late) { system.CoreTiming().RegisterEvent("AudioStretchingEvent", [&](u64, s64 cycles_late) {
if (Settings::values.enable_audio_stretching) { if (Settings::values.enable_audio_stretching) {
Core::DSP().EnableStretching( system.DSP().EnableStretching(system.GetAndResetPerfStats().emulation_speed < 0.95);
Core::System::GetInstance().GetAndResetPerfStats().emulation_speed < 0.95);
} }
system.CoreTiming().ScheduleEvent(audio_stretching_ticks - cycles_late, system.CoreTiming().ScheduleEvent(audio_stretching_ticks - cycles_late,
@ -219,7 +219,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
SCOPE_EXIT({ Settings::values.volume = volume; }); SCOPE_EXIT({ Settings::values.volume = volume; });
Settings::values.volume = 0; Settings::values.volume = 0;
std::unique_lock<std::mutex> pause_lock(paused_mutex); std::unique_lock pause_lock{paused_mutex};
running_cv.wait(pause_lock, [] { return !pause_emulation || stop_run; }); running_cv.wait(pause_lock, [] { return !pause_emulation || stop_run; });
window->PollEvents(); window->PollEvents();
} }
@ -621,7 +621,7 @@ jobjectArray Java_org_citra_citra_1emu_NativeLibrary_GetSavestateInfo(
return nullptr; return nullptr;
} }
const auto savestates = Core::ListSaveStates(title_id); const auto savestates = Core::ListSaveStates(title_id, system.Movie().GetCurrentMovieID());
const jobjectArray array = const jobjectArray array =
env->NewObjectArray(static_cast<jsize>(savestates.size()), savestate_info_class, nullptr); env->NewObjectArray(static_cast<jsize>(savestates.size()), savestate_info_class, nullptr);
for (std::size_t i = 0; i < savestates.size(); ++i) { for (std::size_t i = 0; i < savestates.size(); ++i) {

View File

@ -5,20 +5,24 @@
#include "common/common_types.h" #include "common/common_types.h"
namespace AudioCore {
struct ADTSData { struct ADTSData {
u8 header_length; u8 header_length = 0;
bool MPEG2; bool mpeg2 = false;
u8 profile; u8 profile = 0;
u8 channels; u8 channels = 0;
u8 channel_idx; u8 channel_idx = 0;
u8 framecount; u8 framecount = 0;
u8 samplerate_idx; u8 samplerate_idx = 0;
u32 length; u32 length = 0;
u32 samplerate; u32 samplerate = 0;
}; };
ADTSData ParseADTS(const char* buffer); ADTSData ParseADTS(const u8* buffer);
// last two bytes of MF AAC decoder user data // last two bytes of MF AAC decoder user data
// see https://docs.microsoft.com/en-us/windows/desktop/medfound/aac-decoder#example-media-types // see https://docs.microsoft.com/en-us/windows/desktop/medfound/aac-decoder#example-media-types
u16 MFGetAACTag(const ADTSData& input); u16 MFGetAACTag(const ADTSData& input);
} // namespace AudioCore

View File

@ -3,44 +3,59 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <array> #include <array>
#include "adts.h" #include "adts.h"
#include "common/bit_field.h"
namespace AudioCore {
constexpr std::array<u32, 16> freq_table = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, constexpr std::array<u32, 16> freq_table = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
16000, 12000, 11025, 8000, 7350, 0, 0, 0}; 16000, 12000, 11025, 8000, 7350, 0, 0, 0};
constexpr std::array<u8, 8> channel_table = {0, 1, 2, 3, 4, 5, 6, 8}; constexpr std::array<u8, 8> channel_table = {0, 1, 2, 3, 4, 5, 6, 8};
ADTSData ParseADTS(const char* buffer) { struct ADTSHeader {
u32 tmp = 0; union {
ADTSData out; std::array<u8, 7> raw{};
BitFieldBE<52, 12, u64> sync_word;
BitFieldBE<51, 1, u64> mpeg2;
BitFieldBE<49, 2, u64> layer;
BitFieldBE<48, 1, u64> protection_absent;
BitFieldBE<46, 2, u64> profile;
BitFieldBE<42, 4, u64> samplerate_idx;
BitFieldBE<41, 1, u64> private_bit;
BitFieldBE<38, 3, u64> channel_idx;
BitFieldBE<37, 1, u64> originality;
BitFieldBE<36, 1, u64> home;
BitFieldBE<35, 1, u64> copyright_id;
BitFieldBE<34, 1, u64> copyright_id_start;
BitFieldBE<21, 13, u64> frame_length;
BitFieldBE<10, 11, u64> buffer_fullness;
BitFieldBE<8, 2, u64> frame_count;
};
};
ADTSData ParseADTS(const u8* buffer) {
ADTSHeader header;
memcpy(header.raw.data(), buffer, sizeof(header.raw));
// sync word 0xfff // sync word 0xfff
tmp = (buffer[0] << 8) | (buffer[1] & 0xf0); if (header.sync_word != 0xfff) {
if ((tmp & 0xffff) != 0xfff0) { return {};
out.length = 0;
return out;
} }
ADTSData out{};
// bit 16 = no CRC // bit 16 = no CRC
out.header_length = (buffer[1] & 0x1) ? 7 : 9; out.header_length = header.protection_absent ? 7 : 9;
out.MPEG2 = (buffer[1] >> 3) & 0x1; out.mpeg2 = static_cast<bool>(header.mpeg2);
// bit 17 to 18 // bit 17 to 18
out.profile = (buffer[2] >> 6) + 1; out.profile = static_cast<u8>(header.profile) + 1;
// bit 19 to 22 // bit 19 to 22
tmp = (buffer[2] >> 2) & 0xf; out.samplerate_idx = static_cast<u8>(header.samplerate_idx);
out.samplerate_idx = tmp; out.samplerate = header.samplerate_idx > 15 ? 0 : freq_table[header.samplerate_idx];
out.samplerate = (tmp > 15) ? 0 : freq_table[tmp];
// bit 24 to 26 // bit 24 to 26
tmp = ((buffer[2] & 0x1) << 2) | ((buffer[3] >> 6) & 0x3); out.channel_idx = static_cast<u8>(header.channel_idx);
out.channel_idx = tmp; out.channels = (header.channel_idx > 7) ? 0 : channel_table[header.channel_idx];
out.channels = (tmp > 7) ? 0 : channel_table[tmp];
// bit 55 to 56 // bit 55 to 56
out.framecount = (buffer[6] & 0x3) + 1; out.framecount = static_cast<u8>(header.frame_count + 1);
// bit 31 to 43 // bit 31 to 43
tmp = (buffer[3] & 0x3) << 11; out.length = static_cast<u32>(header.frame_length);
tmp |= (buffer[4] << 3) & 0x7f8;
tmp |= (buffer[5] >> 5) & 0x7;
out.length = tmp;
return out; return out;
} }
@ -61,3 +76,4 @@ u16 MFGetAACTag(const ADTSData& input) {
return tag; return tag;
} }
} // namespace AudioCore

View File

@ -24,7 +24,7 @@ private:
std::optional<BinaryMessage> Decode(const BinaryMessage& request); std::optional<BinaryMessage> Decode(const BinaryMessage& request);
void Clear(); void Clear();
bool InitializeDecoder(ADTSData& adts_header); bool InitializeDecoder(AudioCore::ADTSData& adts_header);
static OSStatus DataFunc(AudioConverterRef in_audio_converter, u32* io_number_data_packets, static OSStatus DataFunc(AudioConverterRef in_audio_converter, u32* io_number_data_packets,
AudioBufferList* io_data, AudioBufferList* io_data,
@ -33,7 +33,7 @@ private:
Memory::MemorySystem& memory; Memory::MemorySystem& memory;
ADTSData adts_config; AudioCore::ADTSData adts_config;
AudioStreamBasicDescription output_format = {}; AudioStreamBasicDescription output_format = {};
AudioConverterRef converter = nullptr; AudioConverterRef converter = nullptr;
@ -85,7 +85,11 @@ std::optional<BinaryMessage> AudioToolboxDecoder::Impl::ProcessRequest(
case DecoderCommand::EncodeDecode: { case DecoderCommand::EncodeDecode: {
return Decode(request); return Decode(request);
} }
case DecoderCommand::Unknown: { case DecoderCommand::Shutdown:
case DecoderCommand::SaveState:
case DecoderCommand::LoadState: {
LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}",
static_cast<u16>(request.header.cmd));
BinaryMessage response = request; BinaryMessage response = request;
response.header.result = ResultStatus::Success; response.header.result = ResultStatus::Success;
return response; return response;
@ -97,7 +101,7 @@ std::optional<BinaryMessage> AudioToolboxDecoder::Impl::ProcessRequest(
} }
} }
bool AudioToolboxDecoder::Impl::InitializeDecoder(ADTSData& adts_header) { bool AudioToolboxDecoder::Impl::InitializeDecoder(AudioCore::ADTSData& adts_header) {
if (converter) { if (converter) {
if (adts_config.channels == adts_header.channels && if (adts_config.channels == adts_header.channels &&
adts_config.samplerate == adts_header.samplerate) { adts_config.samplerate == adts_header.samplerate) {
@ -179,8 +183,9 @@ std::optional<BinaryMessage> AudioToolboxDecoder::Impl::Decode(const BinaryMessa
return {}; return {};
} }
auto data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); const auto data =
auto adts_header = ParseADTS(reinterpret_cast<const char*>(data)); memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR);
auto adts_header = AudioCore::ParseADTS(data);
curr_data = data + adts_header.header_length; curr_data = data + adts_header.header_length;
curr_data_len = request.decode_aac_request.size - adts_header.header_length; curr_data_len = request.decode_aac_request.size - adts_header.header_length;

View File

@ -42,7 +42,9 @@ std::optional<BinaryMessage> NullDecoder::ProcessRequest(const BinaryMessage& re
BinaryMessage response{}; BinaryMessage response{};
switch (request.header.cmd) { switch (request.header.cmd) {
case DecoderCommand::Init: case DecoderCommand::Init:
case DecoderCommand::Unknown: case DecoderCommand::Shutdown:
case DecoderCommand::SaveState:
case DecoderCommand::LoadState:
response = request; response = request;
response.header.result = ResultStatus::Success; response.header.result = ResultStatus::Success;
return response; return response;

View File

@ -14,9 +14,16 @@
namespace AudioCore::HLE { namespace AudioCore::HLE {
enum class DecoderCommand : u16 { enum class DecoderCommand : u16 {
/// Initializes the decoder.
Init = 0, Init = 0,
/// Decodes/encodes a data frame.
EncodeDecode = 1, EncodeDecode = 1,
Unknown = 2, // Probably UnInit /// Shuts down the decoder.
Shutdown = 2,
/// Loads the saved decoder state. Used for DSP wake.
LoadState = 3,
/// Saves the decoder state. Used for DSP sleep.
SaveState = 4,
}; };
enum class DecoderCodec : u16 { enum class DecoderCodec : u16 {

View File

@ -111,7 +111,11 @@ std::optional<BinaryMessage> FDKDecoder::Impl::ProcessRequest(const BinaryMessag
case DecoderCommand::EncodeDecode: { case DecoderCommand::EncodeDecode: {
return Decode(request); return Decode(request);
} }
case DecoderCommand::Unknown: { case DecoderCommand::Shutdown:
case DecoderCommand::SaveState:
case DecoderCommand::LoadState: {
LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}",
static_cast<u16>(request.header.cmd));
BinaryMessage response = request; BinaryMessage response = request;
response.header.result = ResultStatus::Success; response.header.result = ResultStatus::Success;
return response; return response;

View File

@ -80,7 +80,11 @@ std::optional<BinaryMessage> FFMPEGDecoder::Impl::ProcessRequest(const BinaryMes
case DecoderCommand::EncodeDecode: { case DecoderCommand::EncodeDecode: {
return Decode(request); return Decode(request);
} }
case DecoderCommand::Unknown: { case DecoderCommand::Shutdown:
case DecoderCommand::SaveState:
case DecoderCommand::LoadState: {
LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}",
static_cast<u16>(request.header.cmd));
BinaryMessage response = request; BinaryMessage response = request;
response.header.result = ResultStatus::Success; response.header.result = ResultStatus::Success;
return response; return response;

View File

@ -8,6 +8,7 @@
#include <boost/serialization/vector.hpp> #include <boost/serialization/vector.hpp>
#include <boost/serialization/weak_ptr.hpp> #include <boost/serialization/weak_ptr.hpp>
#include "audio_core/audio_types.h" #include "audio_core/audio_types.h"
#include "common/archives.h"
#ifdef HAVE_MF #ifdef HAVE_MF
#include "audio_core/hle/wmf_decoder.h" #include "audio_core/hle/wmf_decoder.h"
#elif HAVE_AUDIOTOOLBOX #elif HAVE_AUDIOTOOLBOX
@ -319,6 +320,10 @@ void DspHle::Impl::PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) {
pipe_data[static_cast<u32>(pipe_number)].resize(sizeof(value)); pipe_data[static_cast<u32>(pipe_number)].resize(sizeof(value));
std::memcpy(pipe_data[static_cast<u32>(pipe_number)].data(), &value, sizeof(value)); std::memcpy(pipe_data[static_cast<u32>(pipe_number)].data(), &value, sizeof(value));
} }
auto dsp = dsp_dsp.lock();
if (dsp) {
dsp->SignalInterrupt(InterruptType::Pipe, DspPipe::Binary);
}
break; break;
} }
default: default:
@ -461,8 +466,6 @@ void DspHle::Impl::AudioTickCallback(s64 cycles_late) {
// TODO(merry): Signal all the other interrupts as appropriate. // TODO(merry): Signal all the other interrupts as appropriate.
if (auto service = dsp_dsp.lock()) { if (auto service = dsp_dsp.lock()) {
service->SignalInterrupt(InterruptType::Pipe, DspPipe::Audio); service->SignalInterrupt(InterruptType::Pipe, DspPipe::Audio);
// HACK(merry): Added to prevent regressions. Will remove soon.
service->SignalInterrupt(InterruptType::Pipe, DspPipe::Binary);
} }
} }

View File

@ -27,7 +27,7 @@ public:
~Impl(); ~Impl();
std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request); std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request);
bool SetMediaType(const ADTSData& adts_data); bool SetMediaType(const AudioCore::ADTSData& adts_data);
private: private:
std::optional<BinaryMessage> Initalize(const BinaryMessage& request); std::optional<BinaryMessage> Initalize(const BinaryMessage& request);
@ -36,8 +36,8 @@ private:
Memory::MemorySystem& memory; Memory::MemorySystem& memory;
std::unique_ptr<AMediaCodec, AMediaCodecRelease> decoder; std::unique_ptr<AMediaCodec, AMediaCodecRelease> decoder;
// default: 2 channles, 48000 samplerate // default: 2 channles, 48000 samplerate
ADTSData mADTSData{ AudioCore::ADTSData mADTSData{
/*header_length*/ 7, /*MPEG2*/ false, /*profile*/ 2, /*header_length*/ 7, /*mpeg2*/ false, /*profile*/ 2,
/*channels*/ 2, /*channel_idx*/ 2, /*framecount*/ 0, /*channels*/ 2, /*channel_idx*/ 2, /*framecount*/ 0,
/*samplerate_idx*/ 3, /*length*/ 0, /*samplerate*/ 48000}; /*samplerate_idx*/ 3, /*length*/ 0, /*samplerate*/ 48000};
}; };
@ -54,7 +54,7 @@ std::optional<BinaryMessage> MediaNDKDecoder::Impl::Initalize(const BinaryMessag
return response; return response;
} }
bool MediaNDKDecoder::Impl::SetMediaType(const ADTSData& adts_data) { bool MediaNDKDecoder::Impl::SetMediaType(const AudioCore::ADTSData& adts_data) {
const char* mime = "audio/mp4a-latm"; const char* mime = "audio/mp4a-latm";
if (decoder && mADTSData.profile == adts_data.profile && if (decoder && mADTSData.profile == adts_data.profile &&
mADTSData.channel_idx == adts_data.channel_idx && mADTSData.channel_idx == adts_data.channel_idx &&
@ -110,7 +110,11 @@ std::optional<BinaryMessage> MediaNDKDecoder::Impl::ProcessRequest(const BinaryM
case DecoderCommand::EncodeDecode: { case DecoderCommand::EncodeDecode: {
return Decode(request); return Decode(request);
} }
case DecoderCommand::Unknown: { case DecoderCommand::Shutdown:
case DecoderCommand::SaveState:
case DecoderCommand::LoadState: {
LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}",
static_cast<u16>(request.header.cmd));
BinaryMessage response = request; BinaryMessage response = request;
response.header.result = ResultStatus::Success; response.header.result = ResultStatus::Success;
return response; return response;
@ -137,8 +141,9 @@ std::optional<BinaryMessage> MediaNDKDecoder::Impl::Decode(const BinaryMessage&
return response; return response;
} }
u8* data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); const u8* data =
ADTSData adts_data = ParseADTS(reinterpret_cast<const char*>(data)); memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR);
ADTSData adts_data = AudioCore::ParseADTS(data);
SetMediaType(adts_data); SetMediaType(adts_data);
response.decode_aac_response.sample_rate = GetSampleRateEnum(adts_data.samplerate); response.decode_aac_response.sample_rate = GetSampleRateEnum(adts_data.samplerate);
response.decode_aac_response.num_channels = adts_data.channels; response.decode_aac_response.num_channels = adts_data.channels;

View File

@ -23,7 +23,8 @@ private:
std::optional<BinaryMessage> Decode(const BinaryMessage& request); std::optional<BinaryMessage> Decode(const BinaryMessage& request);
MFOutputState DecodingLoop(ADTSData adts_header, std::array<std::vector<u8>, 2>& out_streams); MFOutputState DecodingLoop(AudioCore::ADTSData adts_header,
std::array<std::vector<u8>, 2>& out_streams);
bool transform_initialized = false; bool transform_initialized = false;
bool format_selected = false; bool format_selected = false;
@ -115,7 +116,11 @@ std::optional<BinaryMessage> WMFDecoder::Impl::ProcessRequest(const BinaryMessag
case DecoderCommand::EncodeDecode: { case DecoderCommand::EncodeDecode: {
return Decode(request); return Decode(request);
} }
case DecoderCommand::Unknown: { case DecoderCommand::Shutdown:
case DecoderCommand::SaveState:
case DecoderCommand::LoadState: {
LOG_WARNING(Audio_DSP, "Got unimplemented binary request: {}",
static_cast<u16>(request.header.cmd));
BinaryMessage response = request; BinaryMessage response = request;
response.header.result = ResultStatus::Success; response.header.result = ResultStatus::Success;
return response; return response;
@ -135,7 +140,7 @@ std::optional<BinaryMessage> WMFDecoder::Impl::Initalize(const BinaryMessage& re
return response; return response;
} }
MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header, MFOutputState WMFDecoder::Impl::DecodingLoop(AudioCore::ADTSData adts_header,
std::array<std::vector<u8>, 2>& out_streams) { std::array<std::vector<u8>, 2>& out_streams) {
std::optional<std::vector<f32>> output_buffer; std::optional<std::vector<f32>> output_buffer;
@ -206,14 +211,14 @@ std::optional<BinaryMessage> WMFDecoder::Impl::Decode(const BinaryMessage& reque
request.decode_aac_request.src_addr); request.decode_aac_request.src_addr);
return std::nullopt; return std::nullopt;
} }
u8* data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR); const u8* data =
memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR);
std::array<std::vector<u8>, 2> out_streams; std::array<std::vector<u8>, 2> out_streams;
unique_mfptr<IMFSample> sample; unique_mfptr<IMFSample> sample;
MFInputState input_status = MFInputState::OK; MFInputState input_status = MFInputState::OK;
MFOutputState output_status = MFOutputState::OK; MFOutputState output_status = MFOutputState::OK;
std::optional<ADTSMeta> adts_meta = std::optional<ADTSMeta> adts_meta = DetectMediaType(data, request.decode_aac_request.size);
DetectMediaType((char*)data, request.decode_aac_request.size);
if (!adts_meta) { if (!adts_meta) {
LOG_ERROR(Audio_DSP, "Unable to deduce decoding parameters from ADTS stream"); LOG_ERROR(Audio_DSP, "Unable to deduce decoding parameters from ADTS stream");

View File

@ -110,8 +110,9 @@ unique_mfptr<IMFSample> CreateSample(const void* data, DWORD len, DWORD alignmen
return sample; return sample;
} }
bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, bool SelectInputMediaType(IMFTransform* transform, int in_stream_id,
const UINT8* user_data, UINT32 user_data_len, GUID audio_format) { const AudioCore::ADTSData& adts, const UINT8* user_data,
UINT32 user_data_len, GUID audio_format) {
HRESULT hr = S_OK; HRESULT hr = S_OK;
unique_mfptr<IMFMediaType> t; unique_mfptr<IMFMediaType> t;
@ -190,12 +191,12 @@ bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audi
return false; return false;
} }
std::optional<ADTSMeta> DetectMediaType(char* buffer, std::size_t len) { std::optional<ADTSMeta> DetectMediaType(const u8* buffer, std::size_t len) {
if (len < 7) { if (len < 7) {
return std::nullopt; return std::nullopt;
} }
ADTSData tmp; AudioCore::ADTSData tmp;
ADTSMeta result; ADTSMeta result;
// see https://docs.microsoft.com/en-us/windows/desktop/api/mmreg/ns-mmreg-heaacwaveinfo_tag // see https://docs.microsoft.com/en-us/windows/desktop/api/mmreg/ns-mmreg-heaacwaveinfo_tag
// for the meaning of the byte array below // for the meaning of the byte array below
@ -207,7 +208,7 @@ std::optional<ADTSMeta> DetectMediaType(char* buffer, std::size_t len) {
UINT8 aac_tmp[] = {0x01, 0x00, 0xfe, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x00, 0x00}; UINT8 aac_tmp[] = {0x01, 0x00, 0xfe, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x00, 0x00};
uint16_t tag = 0; uint16_t tag = 0;
tmp = ParseADTS(buffer); tmp = AudioCore::ParseADTS(buffer);
if (tmp.length == 0) { if (tmp.length == 0) {
return std::nullopt; return std::nullopt;
} }
@ -215,7 +216,7 @@ std::optional<ADTSMeta> DetectMediaType(char* buffer, std::size_t len) {
tag = MFGetAACTag(tmp); tag = MFGetAACTag(tmp);
aac_tmp[12] |= (tag & 0xff00) >> 8; aac_tmp[12] |= (tag & 0xff00) >> 8;
aac_tmp[13] |= (tag & 0x00ff); aac_tmp[13] |= (tag & 0x00ff);
std::memcpy(&(result.ADTSHeader), &tmp, sizeof(ADTSData)); std::memcpy(&(result.ADTSHeader), &tmp, sizeof(AudioCore::ADTSData));
std::memcpy(&(result.AACTag), aac_tmp, 14); std::memcpy(&(result.AACTag), aac_tmp, 14);
return result; return result;
} }

View File

@ -99,7 +99,7 @@ void ReportError(std::string msg, HRESULT hr);
// data type for transferring ADTS metadata between functions // data type for transferring ADTS metadata between functions
struct ADTSMeta { struct ADTSMeta {
ADTSData ADTSHeader; AudioCore::ADTSData ADTSHeader;
u8 AACTag[14]; u8 AACTag[14];
}; };
@ -110,10 +110,10 @@ bool InitMFDLL();
unique_mfptr<IMFTransform> MFDecoderInit(GUID audio_format = MFAudioFormat_AAC); unique_mfptr<IMFTransform> MFDecoderInit(GUID audio_format = MFAudioFormat_AAC);
unique_mfptr<IMFSample> CreateSample(const void* data, DWORD len, DWORD alignment = 1, unique_mfptr<IMFSample> CreateSample(const void* data, DWORD len, DWORD alignment = 1,
LONGLONG duration = 0); LONGLONG duration = 0);
bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts, bool SelectInputMediaType(IMFTransform* transform, int in_stream_id,
const UINT8* user_data, UINT32 user_data_len, const AudioCore::ADTSData& adts, const UINT8* user_data,
GUID audio_format = MFAudioFormat_AAC); UINT32 user_data_len, GUID audio_format = MFAudioFormat_AAC);
std::optional<ADTSMeta> DetectMediaType(char* buffer, std::size_t len); std::optional<ADTSMeta> DetectMediaType(const u8* buffer, std::size_t len);
bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id,
GUID audio_format = MFAudioFormat_PCM); GUID audio_format = MFAudioFormat_PCM);
void MFFlush(IMFTransform* transform); void MFFlush(IMFTransform* transform);

View File

@ -19,7 +19,6 @@
#include "common/detached_tasks.h" #include "common/detached_tasks.h"
#include "common/file_util.h" #include "common/file_util.h"
#include "common/logging/backend.h" #include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
@ -28,18 +27,15 @@
#include "core/core.h" #include "core/core.h"
#include "core/dumping/backend.h" #include "core/dumping/backend.h"
#include "core/dumping/ffmpeg_backend.h" #include "core/dumping/ffmpeg_backend.h"
#include "core/file_sys/cia_container.h"
#include "core/frontend/applets/default_applets.h" #include "core/frontend/applets/default_applets.h"
#include "core/frontend/framebuffer_layout.h" #include "core/frontend/framebuffer_layout.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/service/am/am.h" #include "core/hle/service/am/am.h"
#include "core/hle/service/cfg/cfg.h" #include "core/hle/service/cfg/cfg.h"
#include "core/loader/loader.h"
#include "core/movie.h" #include "core/movie.h"
#include "core/telemetry_session.h"
#include "input_common/main.h" #include "input_common/main.h"
#include "network/network.h" #include "network/network.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "video_core/video_core.h"
#undef _UNICODE #undef _UNICODE
#include <getopt.h> #include <getopt.h>
@ -331,7 +327,7 @@ int main(int argc, char** argv) {
} }
auto& system = Core::System::GetInstance(); auto& system = Core::System::GetInstance();
auto& movie = Core::Movie::GetInstance(); auto& movie = system.Movie();
if (!movie_record.empty()) { if (!movie_record.empty()) {
movie.PrepareForRecording(); movie.PrepareForRecording();

View File

@ -143,7 +143,7 @@ void Config::ReadValues() {
ReadSetting("Renderer", Settings::values.use_vsync_new); ReadSetting("Renderer", Settings::values.use_vsync_new);
ReadSetting("Renderer", Settings::values.texture_filter); ReadSetting("Renderer", Settings::values.texture_filter);
ReadSetting("Renderer", Settings::values.mono_render_option); ReadSetting("Renderer", Settings::values.swap_eyes);
ReadSetting("Renderer", Settings::values.render_3d); ReadSetting("Renderer", Settings::values.render_3d);
ReadSetting("Renderer", Settings::values.factor_3d); ReadSetting("Renderer", Settings::values.factor_3d);
ReadSetting("Renderer", Settings::values.pp_shader_name); ReadSetting("Renderer", Settings::values.pp_shader_name);

View File

@ -128,16 +128,54 @@ void EmuWindow_SDL2::InitializeSDL2() {
SDL_SetMainReady(); SDL_SetMainReady();
} }
u32 EmuWindow_SDL2::GetEventWindowId(const SDL_Event& event) const {
switch (event.type) {
case SDL_WINDOWEVENT:
return event.window.windowID;
case SDL_KEYDOWN:
case SDL_KEYUP:
return event.key.windowID;
case SDL_MOUSEMOTION:
return event.motion.windowID;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
return event.button.windowID;
case SDL_MOUSEWHEEL:
return event.wheel.windowID;
case SDL_FINGERDOWN:
case SDL_FINGERMOTION:
case SDL_FINGERUP:
return event.tfinger.windowID;
case SDL_TEXTEDITING:
return event.edit.windowID;
case SDL_TEXTEDITING_EXT:
return event.editExt.windowID;
case SDL_TEXTINPUT:
return event.text.windowID;
case SDL_DROPBEGIN:
case SDL_DROPFILE:
case SDL_DROPTEXT:
case SDL_DROPCOMPLETE:
return event.drop.windowID;
case SDL_USEREVENT:
return event.user.windowID;
default:
// Event is not for any particular window, so we can just pretend it's for this one.
return render_window_id;
}
}
void EmuWindow_SDL2::PollEvents() { void EmuWindow_SDL2::PollEvents() {
SDL_Event event; SDL_Event event;
std::vector<SDL_Event> other_window_events; std::vector<SDL_Event> other_window_events;
// SDL_PollEvent returns 0 when there are no more events in the event queue // SDL_PollEvent returns 0 when there are no more events in the event queue
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
if (event.window.windowID != render_window_id) { if (GetEventWindowId(event) != render_window_id) {
other_window_events.push_back(event); other_window_events.push_back(event);
continue; continue;
} }
switch (event.type) { switch (event.type) {
case SDL_WINDOWEVENT: case SDL_WINDOWEVENT:
switch (event.window.event) { switch (event.window.event) {

View File

@ -8,6 +8,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
union SDL_Event;
struct SDL_Window; struct SDL_Window;
namespace Core { namespace Core {
@ -35,6 +36,9 @@ public:
void RequestClose(); void RequestClose();
protected: protected:
/// Gets the ID of the window an event originated from.
u32 GetEventWindowId(const SDL_Event& event) const;
/// Called by PollEvents when a key is pressed or released. /// Called by PollEvents when a key is pressed or released.
void OnKeyEvent(int key, u8 state); void OnKeyEvent(int key, u8 state);

View File

@ -67,6 +67,5 @@ void QtMiiSelector::OpenDialog() {
dialog.return_code, index); dialog.return_code, index);
const auto mii_data = dialog.miis.at(index); const auto mii_data = dialog.miis.at(index);
Finalize(dialog.return_code, Finalize(dialog.return_code, dialog.return_code == 0 ? std::move(mii_data) : Mii::MiiData{});
dialog.return_code == 0 ? std::move(mii_data) : HLE::Applets::MiiData{});
} }

View File

@ -24,7 +24,7 @@ private:
QVBoxLayout* layout; QVBoxLayout* layout;
QtMiiSelector* mii_selector; QtMiiSelector* mii_selector;
u32 return_code = 0; u32 return_code = 0;
std::vector<HLE::Applets::MiiData> miis; std::vector<Mii::MiiData> miis;
friend class QtMiiSelector; friend class QtMiiSelector;
}; };

View File

@ -387,10 +387,6 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread_, Core::Sys
bool is_secondary_) bool is_secondary_)
: QWidget(parent_), EmuWindow(is_secondary_), emu_thread(emu_thread_), system{system_} { : QWidget(parent_), EmuWindow(is_secondary_), emu_thread(emu_thread_), system{system_} {
setWindowTitle(QStringLiteral("Citra %1 | %2-%3")
.arg(QString::fromUtf8(Common::g_build_name),
QString::fromUtf8(Common::g_scm_branch),
QString::fromUtf8(Common::g_scm_desc)));
setAttribute(Qt::WA_AcceptTouchEvents); setAttribute(Qt::WA_AcceptTouchEvents);
auto layout = new QHBoxLayout(this); auto layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
@ -586,6 +582,14 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
OnFramebufferSizeChanged(); OnFramebufferSizeChanged();
} }
void GRenderWindow::moveEvent(QMoveEvent* event) {
QWidget::moveEvent(event);
if (is_secondary) {
const auto screen_pos = Common::MakeVec(pos().x(), pos().y());
SetScreenPos(screen_pos);
}
}
bool GRenderWindow::InitRenderTarget() { bool GRenderWindow::InitRenderTarget() {
{ {
// Create a dummy render widget so that Qt // Create a dummy render widget so that Qt

View File

@ -136,6 +136,7 @@ public:
void closeEvent(QCloseEvent* event) override; void closeEvent(QCloseEvent* event) override;
void resizeEvent(QResizeEvent* event) override; void resizeEvent(QResizeEvent* event) override;
void moveEvent(QMoveEvent* event) override;
void keyPressEvent(QKeyEvent* event) override; void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override;

View File

@ -9,6 +9,7 @@
#include "citra_qt/compatdb.h" #include "citra_qt/compatdb.h"
#include "common/telemetry.h" #include "common/telemetry.h"
#include "core/core.h" #include "core/core.h"
#include "core/telemetry_session.h"
#include "ui_compatdb.h" #include "ui_compatdb.h"
CompatDB::CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent) CompatDB::CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent)

View File

@ -29,7 +29,7 @@ Config::~Config() {
const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G, Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G,
Qt::Key_F, Qt::Key_H, Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N, Qt::Key_F, Qt::Key_H, Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N,
Qt::Key_O, Qt::Key_P, Qt::Key_1, Qt::Key_2, Qt::Key_B, Qt::Key_O, Qt::Key_P, Qt::Key_1, Qt::Key_2, Qt::Key_B, Qt::Key_V,
}; };
const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
@ -505,9 +505,9 @@ void Config::ReadLayoutValues() {
ReadGlobalSetting(Settings::values.swap_screen); ReadGlobalSetting(Settings::values.swap_screen);
ReadGlobalSetting(Settings::values.upright_screen); ReadGlobalSetting(Settings::values.upright_screen);
ReadGlobalSetting(Settings::values.large_screen_proportion); ReadGlobalSetting(Settings::values.large_screen_proportion);
ReadBasicSetting(Settings::values.swap_eyes);
if (global) { if (global) {
ReadBasicSetting(Settings::values.mono_render_option);
ReadBasicSetting(Settings::values.custom_layout); ReadBasicSetting(Settings::values.custom_layout);
ReadBasicSetting(Settings::values.custom_top_left); ReadBasicSetting(Settings::values.custom_top_left);
ReadBasicSetting(Settings::values.custom_top_top); ReadBasicSetting(Settings::values.custom_top_top);
@ -772,6 +772,11 @@ void Config::ReadUIGameListValues() {
ReadBasicSetting(UISettings::values.game_list_hide_no_icon); ReadBasicSetting(UISettings::values.game_list_hide_no_icon);
ReadBasicSetting(UISettings::values.game_list_single_line_mode); ReadBasicSetting(UISettings::values.game_list_single_line_mode);
ReadBasicSetting(UISettings::values.show_compat_column);
ReadBasicSetting(UISettings::values.show_region_column);
ReadBasicSetting(UISettings::values.show_type_column);
ReadBasicSetting(UISettings::values.show_size_column);
qt_config->endGroup(); qt_config->endGroup();
} }
@ -1012,9 +1017,9 @@ void Config::SaveLayoutValues() {
WriteGlobalSetting(Settings::values.swap_screen); WriteGlobalSetting(Settings::values.swap_screen);
WriteGlobalSetting(Settings::values.upright_screen); WriteGlobalSetting(Settings::values.upright_screen);
WriteGlobalSetting(Settings::values.large_screen_proportion); WriteGlobalSetting(Settings::values.large_screen_proportion);
WriteBasicSetting(Settings::values.swap_eyes);
if (global) { if (global) {
WriteBasicSetting(Settings::values.mono_render_option);
WriteBasicSetting(Settings::values.custom_layout); WriteBasicSetting(Settings::values.custom_layout);
WriteBasicSetting(Settings::values.custom_top_left); WriteBasicSetting(Settings::values.custom_top_left);
WriteBasicSetting(Settings::values.custom_top_top); WriteBasicSetting(Settings::values.custom_top_top);
@ -1230,6 +1235,11 @@ void Config::SaveUIGameListValues() {
WriteBasicSetting(UISettings::values.game_list_hide_no_icon); WriteBasicSetting(UISettings::values.game_list_hide_no_icon);
WriteBasicSetting(UISettings::values.game_list_single_line_mode); WriteBasicSetting(UISettings::values.game_list_single_line_mode);
WriteBasicSetting(UISettings::values.show_compat_column);
WriteBasicSetting(UISettings::values.show_region_column);
WriteBasicSetting(UISettings::values.show_type_column);
WriteBasicSetting(UISettings::values.show_size_column);
qt_config->endGroup(); qt_config->endGroup();
} }

View File

@ -21,68 +21,8 @@
<item> <item>
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>-1</number>
</property> </property>
<widget class="ConfigureGeneral" name="generalTab">
<attribute name="title">
<string>General</string>
</attribute>
</widget>
<widget class="ConfigureSystem" name="systemTab">
<attribute name="title">
<string>System</string>
</attribute>
</widget>
<widget class="ConfigureInput" name="inputTab">
<attribute name="title">
<string>Input</string>
</attribute>
</widget>
<widget class="ConfigureHotkeys" name="hotkeysTab">
<attribute name="title">
<string>Hotkeys</string>
</attribute>
</widget>
<widget class="ConfigureGraphics" name="graphicsTab">
<attribute name="title">
<string>Graphics</string>
</attribute>
</widget>
<widget class="ConfigureEnhancements" name="enhancementsTab">
<attribute name="title">
<string>Enhancements</string>
</attribute>
</widget>
<widget class="ConfigureAudio" name="audioTab">
<attribute name="title">
<string>Audio</string>
</attribute>
</widget>
<widget class="ConfigureCamera" name="cameraTab">
<attribute name="title">
<string>Camera</string>
</attribute>
</widget>
<widget class="ConfigureDebug" name="debugTab">
<attribute name="title">
<string>Debug</string>
</attribute>
</widget>
<widget class="ConfigureStorage" name="storageTab">
<attribute name="title">
<string>Storage</string>
</attribute>
</widget>
<widget class="ConfigureWeb" name="webTab">
<attribute name="title">
<string>Web</string>
</attribute>
</widget>
<widget class="ConfigureUi" name="uiTab">
<attribute name="title">
<string>UI</string>
</attribute>
</widget>
</widget> </widget>
</item> </item>
</layout> </layout>

View File

@ -10,14 +10,13 @@
#include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configuration_shared.h"
#include "citra_qt/configuration/configure_audio.h" #include "citra_qt/configuration/configure_audio.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h"
#include "ui_configure_audio.h" #include "ui_configure_audio.h"
#if defined(__APPLE__) #if defined(__APPLE__)
#include "common/apple_authorization.h" #include "common/apple_authorization.h"
#endif #endif
ConfigureAudio::ConfigureAudio(QWidget* parent) ConfigureAudio::ConfigureAudio(bool is_powered_on, QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureAudio>()) { : QWidget(parent), ui(std::make_unique<Ui::ConfigureAudio>()) {
ui->setupUi(this); ui->setupUi(this);
@ -27,8 +26,7 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
AudioCore::GetSinkName(static_cast<AudioCore::SinkType>(type)).data())); AudioCore::GetSinkName(static_cast<AudioCore::SinkType>(type)).data()));
} }
const bool is_running = Core::System::GetInstance().IsPoweredOn(); ui->emulation_combo_box->setEnabled(!is_powered_on);
ui->emulation_combo_box->setEnabled(!is_running);
connect(ui->volume_slider, &QSlider::valueChanged, this, connect(ui->volume_slider, &QSlider::valueChanged, this,
&ConfigureAudio::SetVolumeIndicatorText); &ConfigureAudio::SetVolumeIndicatorText);

View File

@ -19,7 +19,7 @@ class ConfigureAudio : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureAudio(QWidget* parent = nullptr); explicit ConfigureAudio(bool is_powered_on, QWidget* parent = nullptr);
~ConfigureAudio() override; ~ConfigureAudio() override;
void ApplyConfiguration(); void ApplyConfiguration();

View File

@ -47,8 +47,7 @@ ConfigureCamera::~ConfigureCamera() {
} }
void ConfigureCamera::ConnectEvents() { void ConfigureCamera::ConnectEvents() {
connect(ui->image_source, connect(ui->image_source, qOverload<int>(&QComboBox::currentIndexChanged), this,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
[this](int index) { [this](int index) {
StopPreviewing(); StopPreviewing();
UpdateImageSourceUI(); UpdateImageSourceUI();
@ -58,36 +57,33 @@ void ConfigureCamera::ConnectEvents() {
} }
#endif #endif
}); });
connect(ui->camera_selection, connect(ui->camera_selection, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this] { StopPreviewing();
StopPreviewing(); if (GetCameraSelection() != current_selected) {
if (GetCameraSelection() != current_selected) { RecordConfig();
RecordConfig(); }
} if (ui->camera_selection->currentIndex() == 1) {
if (ui->camera_selection->currentIndex() == 1) { ui->camera_mode->setCurrentIndex(1); // Double
ui->camera_mode->setCurrentIndex(1); // Double if (camera_name[0] == camera_name[2] && camera_config[0] == camera_config[2]) {
if (camera_name[0] == camera_name[2] && camera_config[0] == camera_config[2]) { ui->camera_mode->setCurrentIndex(0); // Single
ui->camera_mode->setCurrentIndex(0); // Single }
} }
} UpdateCameraMode();
UpdateCameraMode(); SetConfiguration();
SetConfiguration(); });
}); connect(ui->camera_mode, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
connect(ui->camera_mode, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), StopPreviewing();
this, [this] { ui->camera_position_label->setVisible(ui->camera_mode->currentIndex() == 1);
StopPreviewing(); ui->camera_position->setVisible(ui->camera_mode->currentIndex() == 1);
ui->camera_position_label->setVisible(ui->camera_mode->currentIndex() == 1); current_selected = GetCameraSelection();
ui->camera_position->setVisible(ui->camera_mode->currentIndex() == 1); });
current_selected = GetCameraSelection(); connect(ui->camera_position, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
}); StopPreviewing();
connect(ui->camera_position, if (GetCameraSelection() != current_selected) {
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this] { RecordConfig();
StopPreviewing(); }
if (GetCameraSelection() != current_selected) { SetConfiguration();
RecordConfig(); });
}
SetConfiguration();
});
connect(ui->toolButton, &QToolButton::clicked, this, &ConfigureCamera::OnToolButtonClicked); connect(ui->toolButton, &QToolButton::clicked, this, &ConfigureCamera::OnToolButtonClicked);
connect(ui->preview_button, &QPushButton::clicked, this, [this] { StartPreviewing(); }); connect(ui->preview_button, &QPushButton::clicked, this, [this] { StartPreviewing(); });
connect(ui->prompt_before_load, &QCheckBox::stateChanged, this, [this](int state) { connect(ui->prompt_before_load, &QCheckBox::stateChanged, this, [this](int state) {

View File

@ -9,11 +9,9 @@
#include "core/cheats/cheat_base.h" #include "core/cheats/cheat_base.h"
#include "core/cheats/cheats.h" #include "core/cheats/cheats.h"
#include "core/cheats/gateway_cheat.h" #include "core/cheats/gateway_cheat.h"
#include "core/core.h"
#include "core/hle/kernel/process.h"
#include "ui_configure_cheats.h" #include "ui_configure_cheats.h"
ConfigureCheats::ConfigureCheats(u64 title_id_, QWidget* parent) ConfigureCheats::ConfigureCheats(Core::System& system, u64 title_id_, QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureCheats>()), title_id{title_id_} { : QWidget(parent), ui(std::make_unique<Ui::ConfigureCheats>()), title_id{title_id_} {
// Setup gui control settings // Setup gui control settings
ui->setupUi(this); ui->setupUi(this);
@ -36,7 +34,7 @@ ConfigureCheats::ConfigureCheats(u64 title_id_, QWidget* parent)
[this] { SaveCheat(ui->tableCheats->currentRow()); }); [this] { SaveCheat(ui->tableCheats->currentRow()); });
connect(ui->buttonDelete, &QPushButton::clicked, this, &ConfigureCheats::OnDeleteCheat); connect(ui->buttonDelete, &QPushButton::clicked, this, &ConfigureCheats::OnDeleteCheat);
cheat_engine = std::make_unique<Cheats::CheatEngine>(title_id, Core::System::GetInstance()); cheat_engine = std::make_unique<Cheats::CheatEngine>(title_id, system);
LoadCheats(); LoadCheats();
} }

View File

@ -5,6 +5,7 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <QWidget>
#include "common/common_types.h" #include "common/common_types.h"
namespace Cheats { namespace Cheats {
@ -12,6 +13,10 @@ class CheatBase;
class CheatEngine; class CheatEngine;
} // namespace Cheats } // namespace Cheats
namespace Core {
class System;
}
namespace Ui { namespace Ui {
class ConfigureCheats; class ConfigureCheats;
} // namespace Ui } // namespace Ui
@ -20,7 +25,7 @@ class ConfigureCheats : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureCheats(u64 title_id_, QWidget* parent = nullptr); explicit ConfigureCheats(Core::System& system, u64 title_id, QWidget* parent = nullptr);
~ConfigureCheats(); ~ConfigureCheats();
bool ApplyConfiguration(); bool ApplyConfiguration();

View File

@ -11,7 +11,6 @@
#include "common/file_util.h" #include "common/file_util.h"
#include "common/logging/backend.h" #include "common/logging/backend.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h"
#include "ui_configure_debug.h" #include "ui_configure_debug.h"
// The QSlider doesn't have an easy way to set a custom step amount, // The QSlider doesn't have an easy way to set a custom step amount,
@ -25,8 +24,8 @@ static constexpr int SettingsToSlider(int value) {
return (value - 5) / 5; return (value - 5) / 5;
} }
ConfigureDebug::ConfigureDebug(QWidget* parent) ConfigureDebug::ConfigureDebug(bool is_powered_on_, QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureDebug>()) { : QWidget(parent), ui(std::make_unique<Ui::ConfigureDebug>()), is_powered_on{is_powered_on_} {
ui->setupUi(this); ui->setupUi(this);
SetConfiguration(); SetConfiguration();
@ -35,7 +34,6 @@ ConfigureDebug::ConfigureDebug(QWidget* parent)
QDesktopServices::openUrl(QUrl::fromLocalFile(path)); QDesktopServices::openUrl(QUrl::fromLocalFile(path));
}); });
const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
ui->toggle_cpu_jit->setEnabled(!is_powered_on); ui->toggle_cpu_jit->setEnabled(!is_powered_on);
ui->toggle_renderer_debug->setEnabled(!is_powered_on); ui->toggle_renderer_debug->setEnabled(!is_powered_on);
@ -59,7 +57,7 @@ void ConfigureDebug::SetConfiguration() {
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub.GetValue()); ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub.GetValue());
ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub.GetValue()); ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub.GetValue());
ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port.GetValue()); ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port.GetValue());
ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn()); ui->toggle_console->setEnabled(!is_powered_on);
ui->toggle_console->setChecked(UISettings::values.show_console.GetValue()); ui->toggle_console->setChecked(UISettings::values.show_console.GetValue());
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue())); ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue()));
ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue()); ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue());

View File

@ -15,7 +15,7 @@ class ConfigureDebug : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureDebug(QWidget* parent = nullptr); explicit ConfigureDebug(bool is_powered_on, QWidget* parent = nullptr);
~ConfigureDebug() override; ~ConfigureDebug() override;
void ApplyConfiguration(); void ApplyConfiguration();
@ -25,4 +25,5 @@ public:
private: private:
std::unique_ptr<Ui::ConfigureDebug> ui; std::unique_ptr<Ui::ConfigureDebug> ui;
bool is_powered_on;
}; };

View File

@ -4,8 +4,19 @@
#include <map> #include <map>
#include <QListWidgetItem> #include <QListWidgetItem>
#include "citra_qt/configuration/config.h" #include "citra_qt/configuration/configure_audio.h"
#include "citra_qt/configuration/configure_camera.h"
#include "citra_qt/configuration/configure_debug.h"
#include "citra_qt/configuration/configure_dialog.h" #include "citra_qt/configuration/configure_dialog.h"
#include "citra_qt/configuration/configure_enhancements.h"
#include "citra_qt/configuration/configure_general.h"
#include "citra_qt/configuration/configure_graphics.h"
#include "citra_qt/configuration/configure_hotkeys.h"
#include "citra_qt/configuration/configure_input.h"
#include "citra_qt/configuration/configure_storage.h"
#include "citra_qt/configuration/configure_system.h"
#include "citra_qt/configuration/configure_ui.h"
#include "citra_qt/configuration/configure_web.h"
#include "citra_qt/hotkeys.h" #include "citra_qt/hotkeys.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h" #include "core/core.h"
@ -14,16 +25,41 @@
ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Core::System& system_, ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Core::System& system_,
bool enable_web_config) bool enable_web_config)
: QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, registry{registry_}, : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, registry{registry_},
system{system_} { system{system_}, is_powered_on{system.IsPoweredOn()},
general_tab{std::make_unique<ConfigureGeneral>(this)},
system_tab{std::make_unique<ConfigureSystem>(system, this)},
input_tab{std::make_unique<ConfigureInput>(this)},
hotkeys_tab{std::make_unique<ConfigureHotkeys>(this)},
graphics_tab{std::make_unique<ConfigureGraphics>(is_powered_on, this)},
enhancements_tab{std::make_unique<ConfigureEnhancements>(this)},
audio_tab{std::make_unique<ConfigureAudio>(is_powered_on, this)},
camera_tab{std::make_unique<ConfigureCamera>(this)},
debug_tab{std::make_unique<ConfigureDebug>(is_powered_on, this)},
storage_tab{std::make_unique<ConfigureStorage>(is_powered_on, this)},
web_tab{std::make_unique<ConfigureWeb>(this)}, ui_tab{std::make_unique<ConfigureUi>(this)} {
Settings::SetConfiguringGlobal(true); Settings::SetConfiguringGlobal(true);
ui->setupUi(this); ui->setupUi(this);
ui->hotkeysTab->Populate(registry);
ui->webTab->SetWebServiceConfigEnabled(enable_web_config); ui->tabWidget->addTab(general_tab.get(), tr("General"));
ui->tabWidget->addTab(system_tab.get(), tr("System"));
ui->tabWidget->addTab(input_tab.get(), tr("Input"));
ui->tabWidget->addTab(hotkeys_tab.get(), tr("Hotkeys"));
ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics"));
ui->tabWidget->addTab(enhancements_tab.get(), tr("Enhancements"));
ui->tabWidget->addTab(audio_tab.get(), tr("Audio"));
ui->tabWidget->addTab(camera_tab.get(), tr("Camera"));
ui->tabWidget->addTab(debug_tab.get(), tr("Debug"));
ui->tabWidget->addTab(storage_tab.get(), tr("Storage"));
ui->tabWidget->addTab(web_tab.get(), tr("Web"));
ui->tabWidget->addTab(ui_tab.get(), tr("UI"));
hotkeys_tab->Populate(registry);
web_tab->SetWebServiceConfigEnabled(enable_web_config);
PopulateSelectionList(); PopulateSelectionList();
connect(ui->uiTab, &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged); connect(ui_tab.get(), &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged);
connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, connect(ui->selectorList, &QListWidget::itemSelectionChanged, this,
&ConfigureDialog::UpdateVisibleTabs); &ConfigureDialog::UpdateVisibleTabs);
@ -31,46 +67,46 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Cor
ui->selectorList->setCurrentRow(0); ui->selectorList->setCurrentRow(0);
// Set up used key list synchronisation // Set up used key list synchronisation
connect(ui->inputTab, &ConfigureInput::InputKeysChanged, ui->hotkeysTab, connect(input_tab.get(), &ConfigureInput::InputKeysChanged, hotkeys_tab.get(),
&ConfigureHotkeys::OnInputKeysChanged); &ConfigureHotkeys::OnInputKeysChanged);
connect(ui->hotkeysTab, &ConfigureHotkeys::HotkeysChanged, ui->inputTab, connect(hotkeys_tab.get(), &ConfigureHotkeys::HotkeysChanged, input_tab.get(),
&ConfigureInput::OnHotkeysChanged); &ConfigureInput::OnHotkeysChanged);
// Synchronise lists upon initialisation // Synchronise lists upon initialisation
ui->inputTab->EmitInputKeysChanged(); input_tab->EmitInputKeysChanged();
ui->hotkeysTab->EmitHotkeysChanged(); hotkeys_tab->EmitHotkeysChanged();
} }
ConfigureDialog::~ConfigureDialog() = default; ConfigureDialog::~ConfigureDialog() = default;
void ConfigureDialog::SetConfiguration() { void ConfigureDialog::SetConfiguration() {
ui->generalTab->SetConfiguration(); general_tab->SetConfiguration();
ui->systemTab->SetConfiguration(); system_tab->SetConfiguration();
ui->inputTab->LoadConfiguration(); input_tab->LoadConfiguration();
ui->graphicsTab->SetConfiguration(); graphics_tab->SetConfiguration();
ui->enhancementsTab->SetConfiguration(); enhancements_tab->SetConfiguration();
ui->audioTab->SetConfiguration(); audio_tab->SetConfiguration();
ui->cameraTab->SetConfiguration(); camera_tab->SetConfiguration();
ui->debugTab->SetConfiguration(); debug_tab->SetConfiguration();
ui->webTab->SetConfiguration(); web_tab->SetConfiguration();
ui->uiTab->SetConfiguration(); ui_tab->SetConfiguration();
ui->storageTab->SetConfiguration(); storage_tab->SetConfiguration();
} }
void ConfigureDialog::ApplyConfiguration() { void ConfigureDialog::ApplyConfiguration() {
ui->generalTab->ApplyConfiguration(); general_tab->ApplyConfiguration();
ui->systemTab->ApplyConfiguration(); system_tab->ApplyConfiguration();
ui->inputTab->ApplyConfiguration(); input_tab->ApplyConfiguration();
ui->inputTab->ApplyProfile(); input_tab->ApplyProfile();
ui->hotkeysTab->ApplyConfiguration(registry); hotkeys_tab->ApplyConfiguration(registry);
ui->graphicsTab->ApplyConfiguration(); graphics_tab->ApplyConfiguration();
ui->enhancementsTab->ApplyConfiguration(); enhancements_tab->ApplyConfiguration();
ui->audioTab->ApplyConfiguration(); audio_tab->ApplyConfiguration();
ui->cameraTab->ApplyConfiguration(); camera_tab->ApplyConfiguration();
ui->debugTab->ApplyConfiguration(); debug_tab->ApplyConfiguration();
ui->webTab->ApplyConfiguration(); web_tab->ApplyConfiguration();
ui->uiTab->ApplyConfiguration(); ui_tab->ApplyConfiguration();
ui->storageTab->ApplyConfiguration(); storage_tab->ApplyConfiguration();
system.ApplySettings(); system.ApplySettings();
Settings::LogSettings(); Settings::LogSettings();
} }
@ -81,11 +117,11 @@ void ConfigureDialog::PopulateSelectionList() {
ui->selectorList->clear(); ui->selectorList->clear();
const std::array<std::pair<QString, QList<QWidget*>>, 5> items{ const std::array<std::pair<QString, QList<QWidget*>>, 5> items{
{{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}}, {{tr("General"), {general_tab.get(), web_tab.get(), debug_tab.get(), ui_tab.get()}},
{tr("System"), {ui->systemTab, ui->cameraTab, ui->storageTab}}, {tr("System"), {system_tab.get(), camera_tab.get(), storage_tab.get()}},
{tr("Graphics"), {ui->enhancementsTab, ui->graphicsTab}}, {tr("Graphics"), {enhancements_tab.get(), graphics_tab.get()}},
{tr("Audio"), {ui->audioTab}}, {tr("Audio"), {audio_tab.get()}},
{tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}}; {tr("Controls"), {input_tab.get(), hotkeys_tab.get()}}}};
for (const auto& entry : items) { for (const auto& entry : items) {
auto* const item = new QListWidgetItem(entry.first); auto* const item = new QListWidgetItem(entry.first);
@ -112,18 +148,18 @@ void ConfigureDialog::RetranslateUI() {
ui->selectorList->setCurrentRow(old_row); ui->selectorList->setCurrentRow(old_row);
ui->tabWidget->setCurrentIndex(old_index); ui->tabWidget->setCurrentIndex(old_index);
ui->generalTab->RetranslateUI(); general_tab->RetranslateUI();
ui->systemTab->RetranslateUI(); system_tab->RetranslateUI();
ui->inputTab->RetranslateUI(); input_tab->RetranslateUI();
ui->hotkeysTab->RetranslateUI(); hotkeys_tab->RetranslateUI();
ui->graphicsTab->RetranslateUI(); graphics_tab->RetranslateUI();
ui->enhancementsTab->RetranslateUI(); enhancements_tab->RetranslateUI();
ui->audioTab->RetranslateUI(); audio_tab->RetranslateUI();
ui->cameraTab->RetranslateUI(); camera_tab->RetranslateUI();
ui->debugTab->RetranslateUI(); debug_tab->RetranslateUI();
ui->webTab->RetranslateUI(); web_tab->RetranslateUI();
ui->uiTab->RetranslateUI(); ui_tab->RetranslateUI();
ui->storageTab->RetranslateUI(); storage_tab->RetranslateUI();
} }
void ConfigureDialog::UpdateVisibleTabs() { void ConfigureDialog::UpdateVisibleTabs() {
@ -131,18 +167,18 @@ void ConfigureDialog::UpdateVisibleTabs() {
if (items.isEmpty()) if (items.isEmpty())
return; return;
const std::map<QWidget*, QString> widgets = {{ui->generalTab, tr("General")}, const std::map<QWidget*, QString> widgets = {{general_tab.get(), tr("General")},
{ui->systemTab, tr("System")}, {system_tab.get(), tr("System")},
{ui->inputTab, tr("Input")}, {input_tab.get(), tr("Input")},
{ui->hotkeysTab, tr("Hotkeys")}, {hotkeys_tab.get(), tr("Hotkeys")},
{ui->enhancementsTab, tr("Enhancements")}, {enhancements_tab.get(), tr("Enhancements")},
{ui->graphicsTab, tr("Advanced")}, {graphics_tab.get(), tr("Advanced")},
{ui->audioTab, tr("Audio")}, {audio_tab.get(), tr("Audio")},
{ui->cameraTab, tr("Camera")}, {camera_tab.get(), tr("Camera")},
{ui->debugTab, tr("Debug")}, {debug_tab.get(), tr("Debug")},
{ui->storageTab, tr("Storage")}, {storage_tab.get(), tr("Storage")},
{ui->webTab, tr("Web")}, {web_tab.get(), tr("Web")},
{ui->uiTab, tr("UI")}}; {ui_tab.get(), tr("UI")}};
ui->tabWidget->clear(); ui->tabWidget->clear();

View File

@ -17,6 +17,19 @@ namespace Core {
class System; class System;
} }
class ConfigureGeneral;
class ConfigureSystem;
class ConfigureInput;
class ConfigureHotkeys;
class ConfigureGraphics;
class ConfigureEnhancements;
class ConfigureAudio;
class ConfigureCamera;
class ConfigureDebug;
class ConfigureStorage;
class ConfigureWeb;
class ConfigureUi;
class ConfigureDialog : public QDialog { class ConfigureDialog : public QDialog {
Q_OBJECT Q_OBJECT
@ -42,4 +55,18 @@ private:
std::unique_ptr<Ui::ConfigureDialog> ui; std::unique_ptr<Ui::ConfigureDialog> ui;
HotkeyRegistry& registry; HotkeyRegistry& registry;
Core::System& system; Core::System& system;
bool is_powered_on;
std::unique_ptr<ConfigureGeneral> general_tab;
std::unique_ptr<ConfigureSystem> system_tab;
std::unique_ptr<ConfigureInput> input_tab;
std::unique_ptr<ConfigureHotkeys> hotkeys_tab;
std::unique_ptr<ConfigureGraphics> graphics_tab;
std::unique_ptr<ConfigureEnhancements> enhancements_tab;
std::unique_ptr<ConfigureAudio> audio_tab;
std::unique_ptr<ConfigureCamera> camera_tab;
std::unique_ptr<ConfigureDebug> debug_tab;
std::unique_ptr<ConfigureStorage> storage_tab;
std::unique_ptr<ConfigureWeb> web_tab;
std::unique_ptr<ConfigureUi> ui_tab;
}; };

View File

@ -22,8 +22,7 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent)
const bool res_scale_enabled = graphics_api != Settings::GraphicsAPI::Software; const bool res_scale_enabled = graphics_api != Settings::GraphicsAPI::Software;
ui->resolution_factor_combobox->setEnabled(res_scale_enabled); ui->resolution_factor_combobox->setEnabled(res_scale_enabled);
connect(ui->render_3d_combobox, connect(ui->render_3d_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
[this](int currentIndex) { [this](int currentIndex) {
updateShaders(static_cast<Settings::StereoRenderOption>(currentIndex)); updateShaders(static_cast<Settings::StereoRenderOption>(currentIndex));
}); });
@ -75,9 +74,8 @@ void ConfigureEnhancements::SetConfiguration() {
ui->render_3d_combobox->setCurrentIndex( ui->render_3d_combobox->setCurrentIndex(
static_cast<int>(Settings::values.render_3d.GetValue())); static_cast<int>(Settings::values.render_3d.GetValue()));
ui->factor_3d->setValue(Settings::values.factor_3d.GetValue()); ui->factor_3d->setValue(Settings::values.factor_3d.GetValue());
ui->mono_rendering_eye->setCurrentIndex(
static_cast<int>(Settings::values.mono_render_option.GetValue()));
updateShaders(Settings::values.render_3d.GetValue()); updateShaders(Settings::values.render_3d.GetValue());
ui->toggle_swap_eyes->setChecked(Settings::values.swap_eyes.GetValue());
ui->toggle_linear_filter->setChecked(Settings::values.filter_mode.GetValue()); ui->toggle_linear_filter->setChecked(Settings::values.filter_mode.GetValue());
ui->toggle_swap_screen->setChecked(Settings::values.swap_screen.GetValue()); ui->toggle_swap_screen->setChecked(Settings::values.swap_screen.GetValue());
ui->toggle_upright_screen->setChecked(Settings::values.upright_screen.GetValue()); ui->toggle_upright_screen->setChecked(Settings::values.upright_screen.GetValue());
@ -99,8 +97,7 @@ void ConfigureEnhancements::updateShaders(Settings::StereoRenderOption stereo_op
ui->shader_combobox->clear(); ui->shader_combobox->clear();
ui->shader_combobox->setEnabled(true); ui->shader_combobox->setEnabled(true);
if (stereo_option == Settings::StereoRenderOption::Interlaced || if (stereo_option == Settings::StereoRenderOption::Interlaced) {
stereo_option == Settings::StereoRenderOption::ReverseInterlaced) {
ui->shader_combobox->addItem(QStringLiteral("horizontal (builtin)")); ui->shader_combobox->addItem(QStringLiteral("horizontal (builtin)"));
ui->shader_combobox->setCurrentIndex(0); ui->shader_combobox->setCurrentIndex(0);
ui->shader_combobox->setEnabled(false); ui->shader_combobox->setEnabled(false);
@ -136,8 +133,6 @@ void ConfigureEnhancements::ApplyConfiguration() {
Settings::values.render_3d = Settings::values.render_3d =
static_cast<Settings::StereoRenderOption>(ui->render_3d_combobox->currentIndex()); static_cast<Settings::StereoRenderOption>(ui->render_3d_combobox->currentIndex());
Settings::values.factor_3d = ui->factor_3d->value(); Settings::values.factor_3d = ui->factor_3d->value();
Settings::values.mono_render_option =
static_cast<Settings::MonoRenderOption>(ui->mono_rendering_eye->currentIndex());
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph) { if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph) {
Settings::values.anaglyph_shader_name = Settings::values.anaglyph_shader_name =
ui->shader_combobox->itemText(ui->shader_combobox->currentIndex()).toStdString(); ui->shader_combobox->itemText(ui->shader_combobox->currentIndex()).toStdString();
@ -147,6 +142,8 @@ void ConfigureEnhancements::ApplyConfiguration() {
} }
Settings::values.large_screen_proportion = ui->large_screen_proportion->value(); Settings::values.large_screen_proportion = ui->large_screen_proportion->value();
ConfigurationShared::ApplyPerGameSetting(&Settings::values.swap_eyes, ui->toggle_swap_eyes,
swap_eyes);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.filter_mode, ConfigurationShared::ApplyPerGameSetting(&Settings::values.filter_mode,
ui->toggle_linear_filter, linear_filter); ui->toggle_linear_filter, linear_filter);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.texture_filter, ConfigurationShared::ApplyPerGameSetting(&Settings::values.texture_filter,
@ -175,6 +172,7 @@ void ConfigureEnhancements::SetupPerGameUI() {
if (Settings::IsConfiguringGlobal()) { if (Settings::IsConfiguringGlobal()) {
ui->widget_resolution->setEnabled(Settings::values.resolution_factor.UsingGlobal()); ui->widget_resolution->setEnabled(Settings::values.resolution_factor.UsingGlobal());
ui->widget_texture_filter->setEnabled(Settings::values.texture_filter.UsingGlobal()); ui->widget_texture_filter->setEnabled(Settings::values.texture_filter.UsingGlobal());
ui->toggle_swap_eyes->setEnabled(Settings::values.swap_eyes.UsingGlobal());
ui->toggle_linear_filter->setEnabled(Settings::values.filter_mode.UsingGlobal()); ui->toggle_linear_filter->setEnabled(Settings::values.filter_mode.UsingGlobal());
ui->toggle_swap_screen->setEnabled(Settings::values.swap_screen.UsingGlobal()); ui->toggle_swap_screen->setEnabled(Settings::values.swap_screen.UsingGlobal());
ui->toggle_upright_screen->setEnabled(Settings::values.upright_screen.UsingGlobal()); ui->toggle_upright_screen->setEnabled(Settings::values.upright_screen.UsingGlobal());
@ -194,6 +192,8 @@ void ConfigureEnhancements::SetupPerGameUI() {
linear_filter); linear_filter);
ConfigurationShared::SetColoredTristate(ui->toggle_swap_screen, Settings::values.swap_screen, ConfigurationShared::SetColoredTristate(ui->toggle_swap_screen, Settings::values.swap_screen,
swap_screen); swap_screen);
ConfigurationShared::SetColoredTristate(ui->toggle_swap_eyes, Settings::values.swap_eyes,
swap_eyes);
ConfigurationShared::SetColoredTristate(ui->toggle_upright_screen, ConfigurationShared::SetColoredTristate(ui->toggle_upright_screen,
Settings::values.upright_screen, upright_screen); Settings::values.upright_screen, upright_screen);
ConfigurationShared::SetColoredTristate(ui->toggle_dump_textures, ConfigurationShared::SetColoredTristate(ui->toggle_dump_textures,

View File

@ -45,5 +45,6 @@ private:
ConfigurationShared::CheckState custom_textures; ConfigurationShared::CheckState custom_textures;
ConfigurationShared::CheckState preload_textures; ConfigurationShared::CheckState preload_textures;
ConfigurationShared::CheckState async_custom_loading; ConfigurationShared::CheckState async_custom_loading;
ConfigurationShared::CheckState swap_eyes;
QColor bg_color; QColor bg_color;
}; };

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>440</width> <width>440</width>
<height>748</height> <height>781</height>
</rect> </rect>
</property> </property>
<property name="minimumSize"> <property name="minimumSize">
@ -199,11 +199,11 @@
<string>xBRZ</string> <string>xBRZ</string>
</property> </property>
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>MMPX</string> <string>MMPX</string>
</property> </property>
</item> </item>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -239,6 +239,11 @@
<string>Side by Side</string> <string>Side by Side</string>
</property> </property>
</item> </item>
<item>
<property name="text">
<string>Top Bottom</string>
</property>
</item>
<item> <item>
<property name="text"> <property name="text">
<string>Anaglyph</string> <string>Anaglyph</string>
@ -249,11 +254,6 @@
<string>Interlaced</string> <string>Interlaced</string>
</property> </property>
</item> </item>
<item>
<property name="text">
<string>Reverse Interlaced</string>
</property>
</item>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -288,26 +288,12 @@
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_9"> <layout class="QHBoxLayout" name="horizontalLayout_9">
<item> <item>
<widget class="QLabel" name="label_6"> <widget class="QCheckBox" name="toggle_swap_eyes">
<property name="text"> <property name="text">
<string>Eye to Render in Monoscopic Mode</string> <string>Swap Eyes</string>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QComboBox" name="mono_rendering_eye">
<item>
<property name="text">
<string>Left Eye (default)</string>
</property>
</item>
<item>
<property name="text">
<string>Right Eye</string>
</property>
</item>
</widget>
</item>
</layout> </layout>
</item> </item>
</layout> </layout>
@ -539,7 +525,6 @@
<tabstop>texture_filter_combobox</tabstop> <tabstop>texture_filter_combobox</tabstop>
<tabstop>render_3d_combobox</tabstop> <tabstop>render_3d_combobox</tabstop>
<tabstop>factor_3d</tabstop> <tabstop>factor_3d</tabstop>
<tabstop>mono_rendering_eye</tabstop>
<tabstop>layout_combobox</tabstop> <tabstop>layout_combobox</tabstop>
<tabstop>toggle_swap_screen</tabstop> <tabstop>toggle_swap_screen</tabstop>
<tabstop>toggle_upright_screen</tabstop> <tabstop>toggle_upright_screen</tabstop>

View File

@ -6,14 +6,14 @@
#include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configuration_shared.h"
#include "citra_qt/configuration/configure_graphics.h" #include "citra_qt/configuration/configure_graphics.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h"
#include "ui_configure_graphics.h" #include "ui_configure_graphics.h"
ConfigureGraphics::ConfigureGraphics(QWidget* parent) ConfigureGraphics::ConfigureGraphics(bool is_powered_on, QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureGraphics>()) { : QWidget(parent), ui(std::make_unique<Ui::ConfigureGraphics>()) {
ui->setupUi(this); ui->setupUi(this);
ui->toggle_vsync_new->setEnabled(!Core::System::GetInstance().IsPoweredOn()); ui->toggle_vsync_new->setEnabled(!is_powered_on);
ui->graphics_api_combo->setEnabled(!is_powered_on);
// Set the index to -1 to ensure the below lambda is called with setCurrentIndex // Set the index to -1 to ensure the below lambda is called with setCurrentIndex
ui->graphics_api_combo->setCurrentIndex(-1); ui->graphics_api_combo->setCurrentIndex(-1);
@ -29,9 +29,10 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
}); });
connect(ui->toggle_hw_shader, &QCheckBox::toggled, this, [this] { connect(ui->toggle_hw_shader, &QCheckBox::toggled, this, [this] {
const bool enabled = ui->toggle_hw_shader->isEnabled();
const bool checked = ui->toggle_hw_shader->isChecked(); const bool checked = ui->toggle_hw_shader->isChecked();
ui->hw_shader_group->setEnabled(checked); ui->hw_shader_group->setEnabled(checked && enabled);
ui->toggle_disk_shader_cache->setEnabled(checked); ui->toggle_disk_shader_cache->setEnabled(checked && enabled);
}); });
SetupPerGameUI(); SetupPerGameUI();

View File

@ -19,7 +19,7 @@ class ConfigureGraphics : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureGraphics(QWidget* parent = nullptr); explicit ConfigureGraphics(bool is_powered_on, QWidget* parent = nullptr);
~ConfigureGraphics() override; ~ConfigureGraphics() override;
void ApplyConfiguration(); void ApplyConfiguration();

View File

@ -19,6 +19,9 @@
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </property>
<property name="accessibleName">
<string>Graphics</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QGroupBox" name="apiBox"> <widget class="QGroupBox" name="apiBox">

View File

@ -9,7 +9,6 @@
#include "citra_qt/configuration/configure_hotkeys.h" #include "citra_qt/configuration/configure_hotkeys.h"
#include "citra_qt/hotkeys.h" #include "citra_qt/hotkeys.h"
#include "citra_qt/util/sequence_dialog/sequence_dialog.h" #include "citra_qt/util/sequence_dialog/sequence_dialog.h"
#include "common/settings.h"
#include "ui_configure_hotkeys.h" #include "ui_configure_hotkeys.h"
constexpr int name_column = 0; constexpr int name_column = 0;
@ -189,9 +188,9 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) {
QAction* clear = context_menu.addAction(tr("Clear")); QAction* clear = context_menu.addAction(tr("Clear"));
const auto hotkey_index = index.sibling(index.row(), hotkey_column); const auto hotkey_index = index.sibling(index.row(), hotkey_column);
connect(restore_default, &QAction::triggered, connect(restore_default, &QAction::triggered, this,
[this, hotkey_index] { RestoreHotkey(hotkey_index); }); [this, hotkey_index] { RestoreHotkey(hotkey_index); });
connect(clear, &QAction::triggered, connect(clear, &QAction::triggered, this,
[this, hotkey_index] { model->setData(hotkey_index, QString{}); }); [this, hotkey_index] { model->setData(hotkey_index, QString{}); });
context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location)); context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location));

View File

@ -162,7 +162,7 @@ ConfigureInput::ConfigureInput(QWidget* parent)
ui->buttonDpadUp, ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight, ui->buttonDpadUp, ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight,
ui->buttonL, ui->buttonR, ui->buttonStart, ui->buttonSelect, ui->buttonL, ui->buttonR, ui->buttonStart, ui->buttonSelect,
ui->buttonDebug, ui->buttonGpio14, ui->buttonZL, ui->buttonZR, ui->buttonDebug, ui->buttonGpio14, ui->buttonZL, ui->buttonZR,
ui->buttonHome, ui->buttonHome, ui->buttonPower,
}; };
analog_map_buttons = {{ analog_map_buttons = {{

View File

@ -305,6 +305,24 @@
</layout> </layout>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<layout class="QVBoxLayout" name="verticalLayout_34">
<item>
<widget class="QLabel" name="label_37">
<property name="text">
<string>Power:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonPower">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QVBoxLayout" name="verticalLayout_28"> <layout class="QVBoxLayout" name="verticalLayout_28">
<item> <item>
<widget class="QLabel" name="label_36"> <widget class="QLabel" name="label_36">
@ -340,7 +358,7 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="2" column="0"> <item row="3" column="0">
<layout class="QVBoxLayout" name="verticalLayout_32"> <layout class="QVBoxLayout" name="verticalLayout_32">
<item> <item>
<widget class="QLabel" name="label_40"> <widget class="QLabel" name="label_40">

View File

@ -27,16 +27,18 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString
Core::System& system_) Core::System& system_)
: QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()),
filename{file_name.toStdString()}, title_id{title_id_}, system{system_} { filename{file_name.toStdString()}, title_id{title_id_}, system{system_} {
const auto config_file_name = title_id == 0 ? filename : fmt::format("{:016X}", title_id); const auto config_file_name = title_id == 0 ? std::string(FileUtil::GetFilename(filename))
: fmt::format("{:016X}", title_id);
game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig); game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig);
audio_tab = std::make_unique<ConfigureAudio>(this); const bool is_powered_on = system.IsPoweredOn();
audio_tab = std::make_unique<ConfigureAudio>(is_powered_on, this);
general_tab = std::make_unique<ConfigureGeneral>(this); general_tab = std::make_unique<ConfigureGeneral>(this);
enhancements_tab = std::make_unique<ConfigureEnhancements>(this); enhancements_tab = std::make_unique<ConfigureEnhancements>(this);
graphics_tab = std::make_unique<ConfigureGraphics>(this); graphics_tab = std::make_unique<ConfigureGraphics>(is_powered_on, this);
system_tab = std::make_unique<ConfigureSystem>(this); system_tab = std::make_unique<ConfigureSystem>(system, this);
debug_tab = std::make_unique<ConfigureDebug>(this); debug_tab = std::make_unique<ConfigureDebug>(is_powered_on, this);
cheat_tab = std::make_unique<ConfigureCheats>(title_id, this); cheat_tab = std::make_unique<ConfigureCheats>(system, title_id, this);
ui->setupUi(this); ui->setupUi(this);

View File

@ -6,12 +6,12 @@
#include <QFileDialog> #include <QFileDialog>
#include <QUrl> #include <QUrl>
#include "citra_qt/configuration/configure_storage.h" #include "citra_qt/configuration/configure_storage.h"
#include "common/file_util.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h"
#include "ui_configure_storage.h" #include "ui_configure_storage.h"
ConfigureStorage::ConfigureStorage(QWidget* parent) ConfigureStorage::ConfigureStorage(bool is_powered_on_, QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureStorage>()) { : QWidget(parent), ui(std::make_unique<Ui::ConfigureStorage>()), is_powered_on{is_powered_on_} {
ui->setupUi(this); ui->setupUi(this);
SetConfiguration(); SetConfiguration();
@ -74,7 +74,7 @@ void ConfigureStorage::SetConfiguration() {
ui->toggle_virtual_sd->setChecked(Settings::values.use_virtual_sd.GetValue()); ui->toggle_virtual_sd->setChecked(Settings::values.use_virtual_sd.GetValue());
ui->toggle_custom_storage->setChecked(Settings::values.use_custom_storage.GetValue()); ui->toggle_custom_storage->setChecked(Settings::values.use_custom_storage.GetValue());
ui->storage_group->setEnabled(!Core::System::GetInstance().IsPoweredOn()); ui->storage_group->setEnabled(!is_powered_on);
} }
void ConfigureStorage::ApplyConfiguration() { void ConfigureStorage::ApplyConfiguration() {

View File

@ -15,7 +15,7 @@ class ConfigureStorage : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureStorage(QWidget* parent = nullptr); explicit ConfigureStorage(bool is_powered_on, QWidget* parent = nullptr);
~ConfigureStorage() override; ~ConfigureStorage() override;
void ApplyConfiguration(); void ApplyConfiguration();
@ -23,4 +23,5 @@ public:
void SetConfiguration(); void SetConfiguration();
std::unique_ptr<Ui::ConfigureStorage> ui; std::unique_ptr<Ui::ConfigureStorage> ui;
bool is_powered_on;
}; };

View File

@ -223,14 +223,12 @@ static const std::array<const char*, 187> country_names = {
QT_TRANSLATE_NOOP("ConfigureSystem", "Bermuda"), // 180-186 QT_TRANSLATE_NOOP("ConfigureSystem", "Bermuda"), // 180-186
}; };
ConfigureSystem::ConfigureSystem(QWidget* parent) ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureSystem>()) { : QWidget(parent), ui(std::make_unique<Ui::ConfigureSystem>()), system{system_} {
ui->setupUi(this); ui->setupUi(this);
connect(ui->combo_birthmonth, connect(ui->combo_birthmonth, qOverload<int>(&QComboBox::currentIndexChanged), this,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&ConfigureSystem::UpdateBirthdayComboBox); &ConfigureSystem::UpdateBirthdayComboBox);
connect(ui->combo_init_clock, connect(ui->combo_init_clock, qOverload<int>(&QComboBox::currentIndexChanged), this,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&ConfigureSystem::UpdateInitTime); &ConfigureSystem::UpdateInitTime);
connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
&ConfigureSystem::RefreshConsoleID); &ConfigureSystem::RefreshConsoleID);
@ -280,7 +278,7 @@ ConfigureSystem::ConfigureSystem(QWidget* parent)
ConfigureSystem::~ConfigureSystem() = default; ConfigureSystem::~ConfigureSystem() = default;
void ConfigureSystem::SetConfiguration() { void ConfigureSystem::SetConfiguration() {
enabled = !Core::System::GetInstance().IsPoweredOn(); enabled = !system.IsPoweredOn();
ui->combo_init_clock->setCurrentIndex(static_cast<u8>(Settings::values.init_clock.GetValue())); ui->combo_init_clock->setCurrentIndex(static_cast<u8>(Settings::values.init_clock.GetValue()));
QDateTime date_time; QDateTime date_time;
@ -296,7 +294,7 @@ void ConfigureSystem::SetConfiguration() {
ui->edit_init_time_offset_time->setTime(time); ui->edit_init_time_offset_time->setTime(time);
if (!enabled) { if (!enabled) {
cfg = Service::CFG::GetModule(Core::System::GetInstance()); cfg = Service::CFG::GetModule(system);
ASSERT_MSG(cfg, "CFG Module missing!"); ASSERT_MSG(cfg, "CFG Module missing!");
ReadSystemSettings(); ReadSystemSettings();
ui->group_system_settings->setEnabled(false); ui->group_system_settings->setEnabled(false);

View File

@ -16,6 +16,10 @@ namespace ConfigurationShared {
enum class CheckState; enum class CheckState;
} }
namespace Core {
class System;
}
namespace Service { namespace Service {
namespace CFG { namespace CFG {
class Module; class Module;
@ -26,7 +30,7 @@ class ConfigureSystem : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureSystem(QWidget* parent = nullptr); explicit ConfigureSystem(Core::System& system, QWidget* parent = nullptr);
~ConfigureSystem() override; ~ConfigureSystem() override;
void ApplyConfiguration(); void ApplyConfiguration();
@ -45,8 +49,10 @@ private:
void DownloadFromNUS(); void DownloadFromNUS();
ConfigurationShared::CheckState is_new_3ds; private:
std::unique_ptr<Ui::ConfigureSystem> ui; std::unique_ptr<Ui::ConfigureSystem> ui;
Core::System& system;
ConfigurationShared::CheckState is_new_3ds;
bool enabled = false; bool enabled = false;
std::shared_ptr<Service::CFG::Module> cfg; std::shared_ptr<Service::CFG::Module> cfg;

View File

@ -167,8 +167,7 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
const auto format = texture.format; const auto format = texture.format;
const auto info = Pica::Texture::TextureInfo::FromPicaRegister(config, format); const auto info = Pica::Texture::TextureInfo::FromPicaRegister(config, format);
const u8* src = const u8* src = system.Memory().GetPhysicalPointer(config.GetPhysicalAddress());
Core::System::GetInstance().Memory().GetPhysicalPointer(config.GetPhysicalAddress());
new_info_widget = new TextureInfoWidget(src, info); new_info_widget = new TextureInfoWidget(src, info);
} }
if (command_info_widget) { if (command_info_widget) {
@ -182,8 +181,8 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
} }
#undef COMMAND_IN_RANGE #undef COMMAND_IN_RANGE
GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) GPUCommandListWidget::GPUCommandListWidget(Core::System& system_, QWidget* parent)
: QDockWidget(tr("Pica Command List"), parent) { : QDockWidget(tr("Pica Command List"), parent), system{system_} {
setObjectName(QStringLiteral("Pica Command List")); setObjectName(QStringLiteral("Pica Command List"));
GPUCommandListModel* model = new GPUCommandListModel(this); GPUCommandListModel* model = new GPUCommandListModel(this);

View File

@ -7,11 +7,14 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QDockWidget> #include <QDockWidget>
#include "video_core/debug_utils/debug_utils.h" #include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu_debugger.h"
class QPushButton; class QPushButton;
class QTreeView; class QTreeView;
namespace Core {
class System;
}
class GPUCommandListModel : public QAbstractListModel { class GPUCommandListModel : public QAbstractListModel {
Q_OBJECT Q_OBJECT
@ -39,7 +42,7 @@ class GPUCommandListWidget : public QDockWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit GPUCommandListWidget(QWidget* parent = nullptr); explicit GPUCommandListWidget(Core::System& system, QWidget* parent = nullptr);
public slots: public slots:
void OnToggleTracing(); void OnToggleTracing();
@ -54,7 +57,7 @@ signals:
private: private:
std::unique_ptr<Pica::DebugUtils::PicaTrace> pica_trace; std::unique_ptr<Pica::DebugUtils::PicaTrace> pica_trace;
Core::System& system;
QTreeView* list_widget; QTreeView* list_widget;
QWidget* command_info_widget; QWidget* command_info_widget;
QPushButton* toggle_tracing; QPushButton* toggle_tracing;

View File

@ -15,8 +15,6 @@
#include "citra_qt/debugger/graphics/graphics_surface.h" #include "citra_qt/debugger/graphics/graphics_surface.h"
#include "citra_qt/util/spinbox.h" #include "citra_qt/util/spinbox.h"
#include "common/color.h" #include "common/color.h"
#include "core/core.h"
#include "core/hw/gpu.h"
#include "core/memory.h" #include "core/memory.h"
#include "video_core/pica_state.h" #include "video_core/pica_state.h"
#include "video_core/regs_framebuffer.h" #include "video_core/regs_framebuffer.h"
@ -51,9 +49,10 @@ void SurfacePicture::mouseMoveEvent(QMouseEvent* event) {
mousePressEvent(event); mousePressEvent(event);
} }
GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, GraphicsSurfaceWidget::GraphicsSurfaceWidget(Memory::MemorySystem& memory_,
std::shared_ptr<Pica::DebugContext> debug_context,
QWidget* parent) QWidget* parent)
: BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent), : BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent), memory{memory_},
surface_source(Source::ColorBuffer) { surface_source(Source::ColorBuffer) {
setObjectName(QStringLiteral("PicaSurface")); setObjectName(QStringLiteral("PicaSurface"));
@ -290,57 +289,57 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
return; return;
} }
u8* buffer = Core::System::GetInstance().Memory().GetPhysicalPointer(surface_address); const u8* buffer = memory.GetPhysicalPointer(surface_address);
if (buffer == nullptr) { if (!buffer) {
surface_info_label->setText(tr("(unable to access pixel data)")); surface_info_label->setText(tr("(unable to access pixel data)"));
surface_info_label->setAlignment(Qt::AlignCenter); surface_info_label->setAlignment(Qt::AlignCenter);
return; return;
} }
unsigned nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format); const u32 nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format);
unsigned stride = nibbles_per_pixel * surface_width / 2; const u32 stride = nibbles_per_pixel * surface_width / 2;
const bool nibble_mode = (nibbles_per_pixel == 1);
unsigned bytes_per_pixel; const u32 bytes_per_pixel = [&] {
bool nibble_mode = (nibbles_per_pixel == 1); if (nibble_mode) {
if (nibble_mode) { // As nibbles are contained in a byte we still need to access one byte per nibble
// As nibbles are contained in a byte we still need to access one byte per nibble return 1u;
bytes_per_pixel = 1; } else {
} else { return nibbles_per_pixel / 2;
bytes_per_pixel = nibbles_per_pixel / 2; }
} }();
const u32 coarse_y = y & ~7; const u32 coarse_y = y & ~7;
u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride; const u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
const u8* pixel = buffer + (nibble_mode ? (offset / 2) : offset); const u8* pixel = buffer + (nibble_mode ? (offset / 2) : offset);
auto GetText = [offset](Format format, const u8* pixel) { const auto get_text = [offset](Format format, const u8* pixel) {
switch (format) { switch (format) {
case Format::RGBA8: { case Format::RGBA8: {
auto value = Common::Color::DecodeRGBA8(pixel) / 255.0f; const auto value = Common::Color::DecodeRGBA8(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4") return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2), .arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2)); QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2));
} }
case Format::RGB8: { case Format::RGB8: {
auto value = Common::Color::DecodeRGB8(pixel) / 255.0f; const auto value = Common::Color::DecodeRGB8(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2, Blue: %3") return QStringLiteral("Red: %1, Green: %2, Blue: %3")
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2), .arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
QString::number(value.b(), 'f', 2)); QString::number(value.b(), 'f', 2));
} }
case Format::RGB5A1: { case Format::RGB5A1: {
auto value = Common::Color::DecodeRGB5A1(pixel) / 255.0f; const auto value = Common::Color::DecodeRGB5A1(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4") return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2), .arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2)); QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2));
} }
case Format::RGB565: { case Format::RGB565: {
auto value = Common::Color::DecodeRGB565(pixel) / 255.0f; const auto value = Common::Color::DecodeRGB565(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2, Blue: %3") return QStringLiteral("Red: %1, Green: %2, Blue: %3")
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2), .arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
QString::number(value.b(), 'f', 2)); QString::number(value.b(), 'f', 2));
} }
case Format::RGBA4: { case Format::RGBA4: {
auto value = Common::Color::DecodeRGBA4(pixel) / 255.0f; const auto value = Common::Color::DecodeRGBA4(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4") return QStringLiteral("Red: %1, Green: %2, Blue: %3, Alpha: %4")
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2), .arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2)); QString::number(value.b(), 'f', 2), QString::number(value.a(), 'f', 2));
@ -348,7 +347,7 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
case Format::IA8: case Format::IA8:
return QStringLiteral("Index: %1, Alpha: %2").arg(pixel[0], pixel[1]); return QStringLiteral("Index: %1, Alpha: %2").arg(pixel[0], pixel[1]);
case Format::RG8: { case Format::RG8: {
auto value = Common::Color::DecodeRG8(pixel) / 255.0f; const auto value = Common::Color::DecodeRG8(pixel) / 255.0f;
return QStringLiteral("Red: %1, Green: %2") return QStringLiteral("Red: %1, Green: %2")
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2)); .arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2));
} }
@ -359,11 +358,11 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
case Format::IA4: case Format::IA4:
return QStringLiteral("Index: %1, Alpha: %2").arg(*pixel & 0xF, (*pixel & 0xF0) >> 4); return QStringLiteral("Index: %1, Alpha: %2").arg(*pixel & 0xF, (*pixel & 0xF0) >> 4);
case Format::I4: { case Format::I4: {
u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; const u8 i = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
return QStringLiteral("Index: %1").arg(i); return QStringLiteral("Index: %1").arg(i);
} }
case Format::A4: { case Format::A4: {
u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF; const u8 a = (*pixel >> ((offset % 2) ? 4 : 0)) & 0xF;
return QStringLiteral("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2)); return QStringLiteral("Alpha: %1").arg(QString::number(a / 15.0f, 'f', 2));
} }
case Format::ETC1: case Format::ETC1:
@ -371,17 +370,17 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
// TODO: Display block information or channel values? // TODO: Display block information or channel values?
return QStringLiteral("Compressed data"); return QStringLiteral("Compressed data");
case Format::D16: { case Format::D16: {
auto value = Common::Color::DecodeD16(pixel); const auto value = Common::Color::DecodeD16(pixel);
return QStringLiteral("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4)); return QStringLiteral("Depth: %1").arg(QString::number(value / (float)0xFFFF, 'f', 4));
} }
case Format::D24: { case Format::D24: {
auto value = Common::Color::DecodeD24(pixel); const auto value = Common::Color::DecodeD24(pixel);
return QStringLiteral("Depth: %1") return QStringLiteral("Depth: %1")
.arg(QString::number(value / (float)0xFFFFFF, 'f', 4)); .arg(QString::number(value / (float)0xFFFFFF, 'f', 4));
} }
case Format::D24X8: case Format::D24X8:
case Format::X24S8: { case Format::X24S8: {
auto values = Common::Color::DecodeD24S8(pixel); const auto values = Common::Color::DecodeD24S8(pixel);
return QStringLiteral("Depth: %1, Stencil: %2") return QStringLiteral("Depth: %1, Stencil: %2")
.arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4), values[1]); .arg(QString::number(values[0] / (float)0xFFFFFF, 'f', 4), values[1]);
} }
@ -398,13 +397,13 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
if (nibble_mode) { if (nibble_mode) {
nibble_index += (offset % 2) ? 0 : 1; nibble_index += (offset % 2) ? 0 : 1;
} }
u8 byte = pixel[nibble_index / 2]; const u8 byte = pixel[nibble_index / 2];
u8 nibble = (byte >> ((nibble_index % 2) ? 0 : 4)) & 0xF; const u8 nibble = (byte >> ((nibble_index % 2) ? 0 : 4)) & 0xF;
nibbles.append(QString::number(nibble, 16).toUpper()); nibbles.append(QString::number(nibble, 16).toUpper());
} }
surface_info_label->setText( surface_info_label->setText(
QStringLiteral("Raw: 0x%3\n(%4)").arg(nibbles, GetText(surface_format, pixel))); QStringLiteral("Raw: 0x%3\n(%4)").arg(nibbles, get_text(surface_format, pixel)));
surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
} }
@ -546,9 +545,9 @@ void GraphicsSurfaceWidget::OnUpdate() {
// TODO: Implement a good way to visualize alpha components! // TODO: Implement a good way to visualize alpha components!
QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
u8* buffer = Core::System::GetInstance().Memory().GetPhysicalPointer(surface_address); const u8* buffer = memory.GetPhysicalPointer(surface_address);
if (buffer == nullptr) { if (!buffer) {
surface_picture_label->hide(); surface_picture_label->hide();
surface_info_label->setText(tr("(invalid surface address)")); surface_info_label->setText(tr("(invalid surface address)"));
surface_info_label->setAlignment(Qt::AlignCenter); surface_info_label->setAlignment(Qt::AlignCenter);
@ -682,9 +681,8 @@ void GraphicsSurfaceWidget::SaveSurface() {
tr("Failed to save surface data to file '%1'").arg(filename)); tr("Failed to save surface data to file '%1'").arg(filename));
} }
} else if (selected_filter == bin_filter) { } else if (selected_filter == bin_filter) {
const u8* const buffer = const u8* const buffer = memory.GetPhysicalPointer(surface_address);
Core::System::GetInstance().Memory().GetPhysicalPointer(surface_address); ASSERT_MSG(buffer, "Memory not accessible");
ASSERT_MSG(buffer != nullptr, "Memory not accessible");
QFile file{filename}; QFile file{filename};
if (!file.open(QIODevice::WriteOnly)) { if (!file.open(QIODevice::WriteOnly)) {

View File

@ -14,6 +14,10 @@ class CSpinBox;
class GraphicsSurfaceWidget; class GraphicsSurfaceWidget;
namespace Memory {
class MemorySystem;
}
class SurfacePicture : public QLabel { class SurfacePicture : public QLabel {
Q_OBJECT Q_OBJECT
@ -72,7 +76,8 @@ class GraphicsSurfaceWidget : public BreakPointObserverDock {
static unsigned int NibblesPerPixel(Format format); static unsigned int NibblesPerPixel(Format format);
public: public:
explicit GraphicsSurfaceWidget(std::shared_ptr<Pica::DebugContext> debug_context, explicit GraphicsSurfaceWidget(Memory::MemorySystem& memory,
std::shared_ptr<Pica::DebugContext> debug_context,
QWidget* parent = nullptr); QWidget* parent = nullptr);
void Pick(int x, int y); void Pick(int x, int y);
@ -95,6 +100,7 @@ private:
void SaveSurface(); void SaveSurface();
Memory::MemorySystem& memory;
QComboBox* surface_source_list; QComboBox* surface_source_list;
CSpinBox* surface_address_control; CSpinBox* surface_address_control;
QSpinBox* surface_width_control; QSpinBox* surface_width_control;

View File

@ -16,13 +16,13 @@
#include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/sm.h"
#include "ui_recorder.h" #include "ui_recorder.h"
IPCRecorderWidget::IPCRecorderWidget(QWidget* parent) IPCRecorderWidget::IPCRecorderWidget(Core::System& system_, QWidget* parent)
: QDockWidget(parent), ui(std::make_unique<Ui::IPCRecorder>()) { : QDockWidget(parent), ui(std::make_unique<Ui::IPCRecorder>()), system{system_} {
ui->setupUi(this); ui->setupUi(this);
qRegisterMetaType<IPCDebugger::RequestRecord>(); qRegisterMetaType<IPCDebugger::RequestRecord>();
connect(ui->enabled, &QCheckBox::stateChanged, connect(ui->enabled, &QCheckBox::stateChanged, this,
[this](int new_state) { SetEnabled(new_state == Qt::Checked); }); [this](int new_state) { SetEnabled(new_state == Qt::Checked); });
connect(ui->clearButton, &QPushButton::clicked, this, &IPCRecorderWidget::Clear); connect(ui->clearButton, &QPushButton::clicked, this, &IPCRecorderWidget::Clear);
connect(ui->filter, &QLineEdit::textChanged, this, &IPCRecorderWidget::ApplyFilterToAll); connect(ui->filter, &QLineEdit::textChanged, this, &IPCRecorderWidget::ApplyFilterToAll);
@ -90,7 +90,7 @@ void IPCRecorderWidget::OnEntryUpdated(IPCDebugger::RequestRecord record) {
(record.status == IPCDebugger::RequestStatus::Handled && (record.status == IPCDebugger::RequestStatus::Handled &&
record.translated_reply_cmdbuf[1] != RESULT_SUCCESS.raw)) { // Unimplemented / Error record.translated_reply_cmdbuf[1] != RESULT_SUCCESS.raw)) { // Unimplemented / Error
auto* item = ui->main->invisibleRootItem()->child(row_id); auto item = ui->main->invisibleRootItem()->child(row_id);
for (int column = 0; column < item->columnCount(); ++column) { for (int column = 0; column < item->columnCount(); ++column) {
item->setBackground(column, QBrush(QColor::fromRgb(255, 0, 0))); item->setBackground(column, QBrush(QColor::fromRgb(255, 0, 0)));
} }
@ -100,11 +100,11 @@ void IPCRecorderWidget::OnEntryUpdated(IPCDebugger::RequestRecord record) {
} }
void IPCRecorderWidget::SetEnabled(bool enabled) { void IPCRecorderWidget::SetEnabled(bool enabled) {
if (!Core::System::GetInstance().IsPoweredOn()) { if (!system.IsPoweredOn()) {
return; return;
} }
auto& ipc_recorder = Core::System::GetInstance().Kernel().GetIPCRecorder(); auto& ipc_recorder = system.Kernel().GetIPCRecorder();
ipc_recorder.SetEnabled(enabled); ipc_recorder.SetEnabled(enabled);
if (enabled) { if (enabled) {
@ -123,10 +123,10 @@ void IPCRecorderWidget::Clear() {
} }
QString IPCRecorderWidget::GetServiceName(const IPCDebugger::RequestRecord& record) const { QString IPCRecorderWidget::GetServiceName(const IPCDebugger::RequestRecord& record) const {
if (Core::System::GetInstance().IsPoweredOn() && record.client_port.id != -1) { if (system.IsPoweredOn() && record.client_port.id != -1) {
const auto service_name = const Service::SM::ServiceManager& sm = system.ServiceManager();
Core::System::GetInstance().ServiceManager().GetServiceNameByPortId( const u32 port_id = static_cast<u32>(record.client_port.id);
static_cast<u32>(record.client_port.id)); const auto service_name = sm.GetServiceNameByPortId(port_id);
if (!service_name.empty()) { if (!service_name.empty()) {
return QString::fromStdString(service_name); return QString::fromStdString(service_name);

View File

@ -14,11 +14,15 @@ namespace Ui {
class IPCRecorder; class IPCRecorder;
} }
namespace Core {
class System;
}
class IPCRecorderWidget : public QDockWidget { class IPCRecorderWidget : public QDockWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit IPCRecorderWidget(QWidget* parent = nullptr); explicit IPCRecorderWidget(Core::System& system, QWidget* parent = nullptr);
~IPCRecorderWidget(); ~IPCRecorderWidget();
void OnEmulationStarting(); void OnEmulationStarting();
@ -37,9 +41,10 @@ private:
QString GetFunctionName(const IPCDebugger::RequestRecord& record) const; QString GetFunctionName(const IPCDebugger::RequestRecord& record) const;
void OpenRecordDialog(QTreeWidgetItem* item, int column); void OpenRecordDialog(QTreeWidgetItem* item, int column);
private:
std::unique_ptr<Ui::IPCRecorder> ui; std::unique_ptr<Ui::IPCRecorder> ui;
IPCDebugger::CallbackHandle handle; IPCDebugger::CallbackHandle handle;
Core::System& system;
// The offset between record id and row id, assuming record ids are assigned // The offset between record id and row id, assuming record ids are assigned
// continuously and only the 'Clear' action can be performed, this is enough. // continuously and only the 'Clear' action can be performed, this is enough.
// The initial value is 1, which means record 1 = row 0. // The initial value is 1, which means record 1 = row 0.

View File

@ -9,8 +9,8 @@
#include "core/core.h" #include "core/core.h"
#include "ui_registers.h" #include "ui_registers.h"
RegistersWidget::RegistersWidget(QWidget* parent) RegistersWidget::RegistersWidget(const Core::System& system_, QWidget* parent)
: QDockWidget(parent), cpu_regs_ui(std::make_unique<Ui::ARMRegisters>()) { : QDockWidget(parent), cpu_regs_ui(std::make_unique<Ui::ARMRegisters>()), system{system_} {
cpu_regs_ui->setupUi(this); cpu_regs_ui->setupUi(this);
tree = cpu_regs_ui->treeWidget; tree = cpu_regs_ui->treeWidget;
@ -62,17 +62,21 @@ RegistersWidget::RegistersWidget(QWidget* parent)
RegistersWidget::~RegistersWidget() = default; RegistersWidget::~RegistersWidget() = default;
void RegistersWidget::OnDebugModeEntered() { void RegistersWidget::OnDebugModeEntered() {
if (!Core::System::GetInstance().IsPoweredOn()) if (!system.IsPoweredOn()) {
return; return;
}
// Todo: Handle all cores // TODO: Handle all cores
for (int i = 0; i < core_registers->childCount(); ++i) const ARM_Interface& core = system.GetCore(0);
for (int i = 0; i < core_registers->childCount(); ++i) {
core_registers->child(i)->setText( core_registers->child(i)->setText(
1, QStringLiteral("0x%1").arg(Core::GetCore(0).GetReg(i), 8, 16, QLatin1Char('0'))); 1, QStringLiteral("0x%1").arg(core.GetReg(i), 8, 16, QLatin1Char('0')));
}
for (int i = 0; i < vfp_registers->childCount(); ++i) for (int i = 0; i < vfp_registers->childCount(); ++i) {
vfp_registers->child(i)->setText( vfp_registers->child(i)->setText(
1, QStringLiteral("0x%1").arg(Core::GetCore(0).GetVFPReg(i), 8, 16, QLatin1Char('0'))); 1, QStringLiteral("0x%1").arg(core.GetVFPReg(i), 8, 16, QLatin1Char('0')));
}
UpdateCPSRValues(); UpdateCPSRValues();
UpdateVFPSystemRegisterValues(); UpdateVFPSystemRegisterValues();
@ -86,24 +90,29 @@ void RegistersWidget::OnEmulationStarting(EmuThread* emu_thread) {
void RegistersWidget::OnEmulationStopping() { void RegistersWidget::OnEmulationStopping() {
// Reset widget text // Reset widget text
for (int i = 0; i < core_registers->childCount(); ++i) for (int i = 0; i < core_registers->childCount(); ++i) {
core_registers->child(i)->setText(1, QString{}); core_registers->child(i)->setText(1, QString{});
}
for (int i = 0; i < vfp_registers->childCount(); ++i) for (int i = 0; i < vfp_registers->childCount(); ++i) {
vfp_registers->child(i)->setText(1, QString{}); vfp_registers->child(i)->setText(1, QString{});
}
for (int i = 0; i < cpsr->childCount(); ++i) for (int i = 0; i < cpsr->childCount(); ++i) {
cpsr->child(i)->setText(1, QString{}); cpsr->child(i)->setText(1, QString{});
}
cpsr->setText(1, QString{}); cpsr->setText(1, QString{});
// FPSCR // FPSCR
for (int i = 0; i < vfp_system_registers->child(0)->childCount(); ++i) for (int i = 0; i < vfp_system_registers->child(0)->childCount(); ++i) {
vfp_system_registers->child(0)->child(i)->setText(1, QString{}); vfp_system_registers->child(0)->child(i)->setText(1, QString{});
}
// FPEXC // FPEXC
for (int i = 0; i < vfp_system_registers->child(1)->childCount(); ++i) for (int i = 0; i < vfp_system_registers->child(1)->childCount(); ++i) {
vfp_system_registers->child(1)->child(i)->setText(1, QString{}); vfp_system_registers->child(1)->child(i)->setText(1, QString{});
}
vfp_system_registers->child(0)->setText(1, QString{}); vfp_system_registers->child(0)->setText(1, QString{});
vfp_system_registers->child(1)->setText(1, QString{}); vfp_system_registers->child(1)->setText(1, QString{});
@ -130,8 +139,8 @@ void RegistersWidget::CreateCPSRChildren() {
} }
void RegistersWidget::UpdateCPSRValues() { void RegistersWidget::UpdateCPSRValues() {
// Todo: Handle all cores // TODO: Handle all cores
const u32 cpsr_val = Core::GetCore(0).GetCPSR(); const u32 cpsr_val = system.GetCore(0).GetCPSR();
cpsr->setText(1, QStringLiteral("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0'))); cpsr->setText(1, QStringLiteral("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0')));
cpsr->child(0)->setText( cpsr->child(0)->setText(
@ -193,9 +202,10 @@ void RegistersWidget::CreateVFPSystemRegisterChildren() {
} }
void RegistersWidget::UpdateVFPSystemRegisterValues() { void RegistersWidget::UpdateVFPSystemRegisterValues() {
// Todo: handle all cores // TODO: handle all cores
const u32 fpscr_val = Core::GetCore(0).GetVFPSystemReg(VFP_FPSCR); const ARM_Interface& core = system.GetCore(0);
const u32 fpexc_val = Core::GetCore(0).GetVFPSystemReg(VFP_FPEXC); const u32 fpscr_val = core.GetVFPSystemReg(VFP_FPSCR);
const u32 fpexc_val = core.GetVFPSystemReg(VFP_FPEXC);
QTreeWidgetItem* const fpscr = vfp_system_registers->child(0); QTreeWidgetItem* const fpscr = vfp_system_registers->child(0);
fpscr->setText(1, QStringLiteral("0x%1").arg(fpscr_val, 8, 16, QLatin1Char('0'))); fpscr->setText(1, QStringLiteral("0x%1").arg(fpscr_val, 8, 16, QLatin1Char('0')));

View File

@ -15,11 +15,15 @@ namespace Ui {
class ARMRegisters; class ARMRegisters;
} }
namespace Core {
class System;
}
class RegistersWidget : public QDockWidget { class RegistersWidget : public QDockWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit RegistersWidget(QWidget* parent = nullptr); explicit RegistersWidget(const Core::System& system, QWidget* parent = nullptr);
~RegistersWidget(); ~RegistersWidget();
public slots: public slots:
@ -37,7 +41,7 @@ private:
void UpdateVFPSystemRegisterValues(); void UpdateVFPSystemRegisterValues();
std::unique_ptr<Ui::ARMRegisters> cpu_regs_ui; std::unique_ptr<Ui::ARMRegisters> cpu_regs_ui;
const Core::System& system;
QTreeWidget* tree; QTreeWidget* tree;
QTreeWidgetItem* core_registers; QTreeWidgetItem* core_registers;

View File

@ -5,11 +5,10 @@
#include <array> #include <array>
#include "citra_qt/debugger/wait_tree.h" #include "citra_qt/debugger/wait_tree.h"
#include "citra_qt/uisettings.h" #include "citra_qt/uisettings.h"
#include "citra_qt/util/util.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/settings.h"
#include "core/hle/kernel/event.h" #include "core/hle/kernel/event.h"
#include "core/hle/kernel/mutex.h" #include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/semaphore.h" #include "core/hle/kernel/semaphore.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h" #include "core/hle/kernel/timer.h"
@ -76,13 +75,13 @@ std::size_t WaitTreeItem::Row() const {
return row; return row;
} }
std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() { std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList(
const u32 num_cores = Core::GetNumCores(); Core::System& system) {
const u32 num_cores = system.GetNumCores();
std::vector<std::unique_ptr<WaitTreeThread>> item_list; std::vector<std::unique_ptr<WaitTreeThread>> item_list;
item_list.reserve(num_cores); item_list.reserve(num_cores);
for (u32 i = 0; i < num_cores; ++i) { for (u32 i = 0; i < num_cores; ++i) {
const auto& threads = const auto threads = system.Kernel().GetThreadManager(i).GetThreadList();
Core::System::GetInstance().Kernel().GetThreadManager(i).GetThreadList();
item_list.reserve(item_list.size() + threads.size()); item_list.reserve(item_list.size() + threads.size());
for (std::size_t j = 0; j < threads.size(); ++j) { for (std::size_t j = 0; j < threads.size(); ++j) {
item_list.push_back(std::make_unique<WaitTreeThread>(*threads[j])); item_list.push_back(std::make_unique<WaitTreeThread>(*threads[j]));
@ -436,11 +435,12 @@ void WaitTreeModel::ClearItems() {
thread_items.clear(); thread_items.clear();
} }
void WaitTreeModel::InitItems() { void WaitTreeModel::InitItems(Core::System& system) {
thread_items = WaitTreeItem::MakeThreadItemList(); thread_items = WaitTreeItem::MakeThreadItemList(system);
} }
WaitTreeWidget::WaitTreeWidget(QWidget* parent) : QDockWidget(tr("Wait Tree"), parent) { WaitTreeWidget::WaitTreeWidget(Core::System& system_, QWidget* parent)
: QDockWidget(tr("Wait Tree"), parent), system{system_} {
setObjectName(QStringLiteral("WaitTreeWidget")); setObjectName(QStringLiteral("WaitTreeWidget"));
view = new QTreeView(this); view = new QTreeView(this);
view->setHeaderHidden(true); view->setHeaderHidden(true);
@ -449,9 +449,10 @@ WaitTreeWidget::WaitTreeWidget(QWidget* parent) : QDockWidget(tr("Wait Tree"), p
} }
void WaitTreeWidget::OnDebugModeEntered() { void WaitTreeWidget::OnDebugModeEntered() {
if (!Core::System::GetInstance().IsPoweredOn()) if (!system.IsPoweredOn()) {
return; return;
model->InitItems(); }
model->InitItems(system);
view->setModel(model); view->setModel(model);
setEnabled(true); setEnabled(true);
} }

View File

@ -23,6 +23,10 @@ class Thread;
class Timer; class Timer;
} // namespace Kernel } // namespace Kernel
namespace Core {
class System;
}
class WaitTreeThread; class WaitTreeThread;
class WaitTreeItem : public QObject { class WaitTreeItem : public QObject {
@ -39,7 +43,7 @@ public:
WaitTreeItem* Parent() const; WaitTreeItem* Parent() const;
std::span<const std::unique_ptr<WaitTreeItem>> Children() const; std::span<const std::unique_ptr<WaitTreeItem>> Children() const;
std::size_t Row() const; std::size_t Row() const;
static std::vector<std::unique_ptr<WaitTreeThread>> MakeThreadItemList(); static std::vector<std::unique_ptr<WaitTreeThread>> MakeThreadItemList(Core::System& system);
private: private:
std::size_t row; std::size_t row;
@ -166,7 +170,7 @@ public:
int columnCount(const QModelIndex& parent) const override; int columnCount(const QModelIndex& parent) const override;
void ClearItems(); void ClearItems();
void InitItems(); void InitItems(Core::System& system);
private: private:
std::vector<std::unique_ptr<WaitTreeThread>> thread_items; std::vector<std::unique_ptr<WaitTreeThread>> thread_items;
@ -176,7 +180,7 @@ class WaitTreeWidget : public QDockWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit WaitTreeWidget(QWidget* parent = nullptr); explicit WaitTreeWidget(Core::System& system, QWidget* parent = nullptr);
public slots: public slots:
void OnDebugModeEntered(); void OnDebugModeEntered();
@ -188,4 +192,5 @@ public slots:
private: private:
QTreeView* view; QTreeView* view;
WaitTreeModel* model; WaitTreeModel* model;
Core::System& system;
}; };

View File

@ -9,10 +9,11 @@
#include "citra_qt/uisettings.h" #include "citra_qt/uisettings.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "core/core.h" #include "core/core.h"
#include "core/loader/loader.h"
namespace DiscordRPC { namespace DiscordRPC {
DiscordImpl::DiscordImpl() { DiscordImpl::DiscordImpl(const Core::System& system_) : system{system_} {
DiscordEventHandlers handlers{}; DiscordEventHandlers handlers{};
// The number is the client ID for Citra, it's used for images and the // The number is the client ID for Citra, it's used for images and the
@ -34,12 +35,15 @@ void DiscordImpl::Update() {
std::chrono::system_clock::now().time_since_epoch()) std::chrono::system_clock::now().time_since_epoch())
.count(); .count();
std::string title; std::string title;
if (Core::System::GetInstance().IsPoweredOn()) const bool is_powered_on = system.IsPoweredOn();
Core::System::GetInstance().GetAppLoader().ReadTitle(title); if (is_powered_on) {
system.GetAppLoader().ReadTitle(title);
}
DiscordRichPresence presence{}; DiscordRichPresence presence{};
presence.largeImageKey = "citra"; presence.largeImageKey = "citra";
presence.largeImageText = "Citra is an emulator for the Nintendo 3DS"; presence.largeImageText = "Citra is an emulator for the Nintendo 3DS";
if (Core::System::GetInstance().IsPoweredOn()) { if (is_powered_on) {
presence.state = title.c_str(); presence.state = title.c_str();
presence.details = "Currently in game"; presence.details = "Currently in game";
} else { } else {

View File

@ -6,15 +6,22 @@
#include "citra_qt/discord.h" #include "citra_qt/discord.h"
namespace Core {
class System;
}
namespace DiscordRPC { namespace DiscordRPC {
class DiscordImpl : public DiscordInterface { class DiscordImpl : public DiscordInterface {
public: public:
DiscordImpl(); DiscordImpl(const Core::System& system);
~DiscordImpl() override; ~DiscordImpl() override;
void Pause() override; void Pause() override;
void Update() override; void Update() override;
private:
const Core::System& system;
}; };
} // namespace DiscordRPC } // namespace DiscordRPC

View File

@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QActionGroup>
#include <QApplication> #include <QApplication>
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
@ -307,6 +308,9 @@ GameList::GameList(GMainWindow* parent) : QWidget{parent} {
tree_view->setEditTriggers(QHeaderView::NoEditTriggers); tree_view->setEditTriggers(QHeaderView::NoEditTriggers);
tree_view->setContextMenuPolicy(Qt::CustomContextMenu); tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
tree_view->setStyleSheet(QStringLiteral("QTreeView{ border: none; }")); tree_view->setStyleSheet(QStringLiteral("QTreeView{ border: none; }"));
tree_view->header()->setContextMenuPolicy(Qt::CustomContextMenu);
UpdateColumnVisibility();
item_model->insertColumns(0, COLUMN_COUNT); item_model->insertColumns(0, COLUMN_COUNT);
RetranslateUI(); RetranslateUI();
@ -317,6 +321,8 @@ GameList::GameList(GMainWindow* parent) : QWidget{parent} {
connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded); connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded);
connect(tree_view, &QTreeView::collapsed, this, &GameList::OnItemExpanded); connect(tree_view, &QTreeView::collapsed, this, &GameList::OnItemExpanded);
connect(tree_view->header(), &QHeaderView::customContextMenuRequested, this,
&GameList::PopupHeaderContextMenu);
// We must register all custom types with the Qt Automoc system so that we are able to use // We must register all custom types with the Qt Automoc system so that we are able to use
// it with signals/slots. In this case, QList falls under the umbrells of custom types. // it with signals/slots. In this case, QList falls under the umbrells of custom types.
@ -471,6 +477,41 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
} }
void GameList::PopupHeaderContextMenu(const QPoint& menu_location) {
const QModelIndex item = tree_view->indexAt(menu_location);
if (!item.isValid()) {
return;
}
QMenu context_menu;
static const QMap<QString, Settings::Setting<bool>*> columns{
{tr("Compatibility"), &UISettings::values.show_compat_column},
{tr("Region"), &UISettings::values.show_region_column},
{tr("File type"), &UISettings::values.show_type_column},
{tr("Size"), &UISettings::values.show_size_column}};
QActionGroup* column_group = new QActionGroup(this);
column_group->setExclusive(false);
for (const auto& key : columns.keys()) {
QAction* action = column_group->addAction(context_menu.addAction(key));
action->setCheckable(true);
action->setChecked(columns[key]->GetValue());
connect(action, &QAction::toggled, [this, key](bool value) {
*columns[key] = !columns[key]->GetValue();
UpdateColumnVisibility();
});
}
context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
}
void GameList::UpdateColumnVisibility() {
tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat_column);
tree_view->setColumnHidden(COLUMN_REGION, !UISettings::values.show_region_column);
tree_view->setColumnHidden(COLUMN_FILE_TYPE, !UISettings::values.show_type_column);
tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size_column);
}
void ForEachOpenGLCacheFile(u64 program_id, auto func) { void ForEachOpenGLCacheFile(u64 program_id, auto func) {
for (const std::string_view cache_type : {"separable", "conventional"}) { for (const std::string_view cache_type : {"separable", "conventional"}) {
const std::string path = fmt::format("{}opengl/precompiled/{}/{:016X}.bin", const std::string path = fmt::format("{}opengl/precompiled/{}/{:016X}.bin",
@ -750,6 +791,10 @@ QStandardItemModel* GameList::GetModel() const {
void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) { void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
tree_view->setEnabled(false); tree_view->setEnabled(false);
// Update the columns in case UISettings has changed
UpdateColumnVisibility();
// Delete any rows that might already exist if we're repopulating // Delete any rows that might already exist if we're repopulating
item_model->removeRows(0, item_model->rowCount()); item_model->removeRows(0, item_model->rowCount());
search_field->clear(); search_field->clear();

View File

@ -104,9 +104,11 @@ private:
void DonePopulating(const QStringList& watch_list); void DonePopulating(const QStringList& watch_list);
void PopupContextMenu(const QPoint& menu_location); void PopupContextMenu(const QPoint& menu_location);
void PopupHeaderContextMenu(const QPoint& menu_location);
void AddGamePopup(QMenu& context_menu, const QString& path, u64 program_id, u64 extdata_id); void AddGamePopup(QMenu& context_menu, const QString& path, u64 program_id, u64 extdata_id);
void AddCustomDirPopup(QMenu& context_menu, QModelIndex selected); void AddCustomDirPopup(QMenu& context_menu, QModelIndex selected);
void AddPermDirPopup(QMenu& context_menu, QModelIndex selected); void AddPermDirPopup(QMenu& context_menu, QModelIndex selected);
void UpdateColumnVisibility();
QString FindGameByProgramID(QStandardItem* current_item, u64 program_id, int role); QString FindGameByProgramID(QStandardItem* current_item, u64 program_id, int role);

View File

@ -14,6 +14,7 @@
#include <QtGui> #include <QtGui>
#include <QtWidgets> #include <QtWidgets>
#include <fmt/format.h> #include <fmt/format.h>
#include "core/telemetry_session.h"
#ifdef __APPLE__ #ifdef __APPLE__
#include <unistd.h> // for chdir #include <unistd.h> // for chdir
#endif #endif
@ -73,7 +74,6 @@
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/string_util.h"
#if CITRA_ARCH(x86_64) #if CITRA_ARCH(x86_64)
#include "common/x64/cpu_detect.h" #include "common/x64/cpu_detect.h"
#endif #endif
@ -173,7 +173,7 @@ static QString PrettyProductName() {
} }
GMainWindow::GMainWindow(Core::System& system_) GMainWindow::GMainWindow(Core::System& system_)
: ui{std::make_unique<Ui::MainWindow>()}, system{system_}, movie{Core::Movie::GetInstance()}, : ui{std::make_unique<Ui::MainWindow>()}, system{system_}, movie{system.Movie()},
config{std::make_unique<Config>()}, emu_thread{nullptr} { config{std::make_unique<Config>()}, emu_thread{nullptr} {
Common::Log::Initialize(); Common::Log::Initialize();
Common::Log::Start(); Common::Log::Start();
@ -353,8 +353,8 @@ void GMainWindow::InitializeWidgets() {
}); });
InputCommon::Init(); InputCommon::Init();
multiplayer_state = new MultiplayerState(this, game_list->GetModel(), ui->action_Leave_Room, multiplayer_state = new MultiplayerState(system, this, game_list->GetModel(),
ui->action_Show_Room); ui->action_Leave_Room, ui->action_Show_Room);
multiplayer_state->setVisible(false); multiplayer_state->setVisible(false);
#if ENABLE_QT_UPDATER #if ENABLE_QT_UPDATER
@ -434,7 +434,7 @@ void GMainWindow::InitializeDebugWidgets() {
debug_menu->addAction(microProfileDialog->toggleViewAction()); debug_menu->addAction(microProfileDialog->toggleViewAction());
#endif #endif
registersWidget = new RegistersWidget(this); registersWidget = new RegistersWidget(system, this);
addDockWidget(Qt::RightDockWidgetArea, registersWidget); addDockWidget(Qt::RightDockWidgetArea, registersWidget);
registersWidget->hide(); registersWidget->hide();
debug_menu->addAction(registersWidget->toggleViewAction()); debug_menu->addAction(registersWidget->toggleViewAction());
@ -448,7 +448,7 @@ void GMainWindow::InitializeDebugWidgets() {
graphicsWidget->hide(); graphicsWidget->hide();
debug_menu->addAction(graphicsWidget->toggleViewAction()); debug_menu->addAction(graphicsWidget->toggleViewAction());
graphicsCommandsWidget = new GPUCommandListWidget(this); graphicsCommandsWidget = new GPUCommandListWidget(system, this);
addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
graphicsCommandsWidget->hide(); graphicsCommandsWidget->hide();
debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
@ -472,7 +472,7 @@ void GMainWindow::InitializeDebugWidgets() {
connect(this, &GMainWindow::EmulationStopping, graphicsTracingWidget, connect(this, &GMainWindow::EmulationStopping, graphicsTracingWidget,
&GraphicsTracingWidget::OnEmulationStopping); &GraphicsTracingWidget::OnEmulationStopping);
waitTreeWidget = new WaitTreeWidget(this); waitTreeWidget = new WaitTreeWidget(system, this);
addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
waitTreeWidget->hide(); waitTreeWidget->hide();
debug_menu->addAction(waitTreeWidget->toggleViewAction()); debug_menu->addAction(waitTreeWidget->toggleViewAction());
@ -490,7 +490,7 @@ void GMainWindow::InitializeDebugWidgets() {
connect(this, &GMainWindow::EmulationStopping, waitTreeWidget, connect(this, &GMainWindow::EmulationStopping, waitTreeWidget,
[this] { lleServiceModulesWidget->setDisabled(false); }); [this] { lleServiceModulesWidget->setDisabled(false); });
ipcRecorderWidget = new IPCRecorderWidget(this); ipcRecorderWidget = new IPCRecorderWidget(system, this);
addDockWidget(Qt::RightDockWidgetArea, ipcRecorderWidget); addDockWidget(Qt::RightDockWidgetArea, ipcRecorderWidget);
ipcRecorderWidget->hide(); ipcRecorderWidget->hide();
debug_menu->addAction(ipcRecorderWidget->toggleViewAction()); debug_menu->addAction(ipcRecorderWidget->toggleViewAction());
@ -627,6 +627,8 @@ void GMainWindow::InitializeHotkeys() {
Settings::values.frame_limit.SetGlobal(!Settings::values.frame_limit.UsingGlobal()); Settings::values.frame_limit.SetGlobal(!Settings::values.frame_limit.UsingGlobal());
UpdateStatusBar(); UpdateStatusBar();
}); });
connect_shortcut(QStringLiteral("Swap Eyes"),
[&] { Settings::values.swap_eyes = !Settings::values.swap_eyes; });
connect_shortcut(QStringLiteral("Toggle Texture Dumping"), connect_shortcut(QStringLiteral("Toggle Texture Dumping"),
[&] { Settings::values.dump_textures = !Settings::values.dump_textures; }); [&] { Settings::values.dump_textures = !Settings::values.dump_textures; });
connect_shortcut(QStringLiteral("Toggle Custom Textures"), connect_shortcut(QStringLiteral("Toggle Custom Textures"),
@ -1171,20 +1173,18 @@ void GMainWindow::BootGame(const QString& filename) {
movie.PrepareForPlayback(movie_playback_path.toStdString()); movie.PrepareForPlayback(movie_playback_path.toStdString());
} }
u64 title_id{0};
const std::string path = filename.toStdString(); const std::string path = filename.toStdString();
const auto loader = Loader::GetLoader(path); const auto loader = Loader::GetLoader(path);
if (loader && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success) { u64 title_id{0};
// Load per game settings loader->ReadProgramId(title_id);
const std::string name{FileUtil::GetFilename(filename.toStdString())};
const std::string config_file_name =
title_id == 0 ? name : fmt::format("{:016X}", title_id);
Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
system.ApplySettings();
LOG_INFO(Frontend, "Using per game config file for title id {}", config_file_name); // Load per game settings
} const std::string name{FileUtil::GetFilename(filename.toStdString())};
const std::string config_file_name = title_id == 0 ? name : fmt::format("{:016X}", title_id);
LOG_INFO(Frontend, "Loading per game config file for title {}", config_file_name);
Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
system.ApplySettings();
Settings::LogSettings(); Settings::LogSettings();
@ -1415,7 +1415,7 @@ void GMainWindow::UpdateSaveStates() {
if (system.GetAppLoader().ReadProgramId(title_id) != Loader::ResultStatus::Success) { if (system.GetAppLoader().ReadProgramId(title_id) != Loader::ResultStatus::Success) {
return; return;
} }
auto savestates = Core::ListSaveStates(title_id); auto savestates = Core::ListSaveStates(title_id, movie.GetCurrentMovieID());
for (u32 i = 0; i < Core::SaveStateSlotCount; ++i) { for (u32 i = 0; i < Core::SaveStateSlotCount; ++i) {
actions_load_state[i]->setEnabled(false); actions_load_state[i]->setEnabled(false);
actions_load_state[i]->setText(tr("Slot %1").arg(i + 1)); actions_load_state[i]->setText(tr("Slot %1").arg(i + 1));
@ -2048,7 +2048,6 @@ void GMainWindow::OnLoadAmiibo() {
return; return;
} }
Core::System& system{Core::System::GetInstance()};
Service::SM::ServiceManager& sm = system.ServiceManager(); Service::SM::ServiceManager& sm = system.ServiceManager();
auto nfc = sm.GetService<Service::NFC::Module::Interface>("nfc:u"); auto nfc = sm.GetService<Service::NFC::Module::Interface>("nfc:u");
if (nfc == nullptr) { if (nfc == nullptr) {
@ -2120,14 +2119,15 @@ void GMainWindow::OnToggleFilterBar() {
} }
void GMainWindow::OnCreateGraphicsSurfaceViewer() { void GMainWindow::OnCreateGraphicsSurfaceViewer() {
auto graphicsSurfaceViewerWidget = new GraphicsSurfaceWidget(Pica::g_debug_context, this); auto graphicsSurfaceViewerWidget =
new GraphicsSurfaceWidget(system.Memory(), Pica::g_debug_context, this);
addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceViewerWidget); addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceViewerWidget);
// TODO: Maybe graphicsSurfaceViewerWidget->setFloating(true); // TODO: Maybe graphicsSurfaceViewerWidget->setFloating(true);
graphicsSurfaceViewerWidget->show(); graphicsSurfaceViewerWidget->show();
} }
void GMainWindow::OnRecordMovie() { void GMainWindow::OnRecordMovie() {
MovieRecordDialog dialog(this); MovieRecordDialog dialog(this, system);
if (dialog.exec() != QDialog::Accepted) { if (dialog.exec() != QDialog::Accepted) {
return; return;
} }
@ -2144,7 +2144,7 @@ void GMainWindow::OnRecordMovie() {
} }
void GMainWindow::OnPlayMovie() { void GMainWindow::OnPlayMovie() {
MoviePlayDialog dialog(this, game_list); MoviePlayDialog dialog(this, game_list, system);
if (dialog.exec() != QDialog::Accepted) { if (dialog.exec() != QDialog::Accepted) {
return; return;
} }
@ -2484,18 +2484,24 @@ void GMainWindow::OnMouseActivity() {
ShowMouseCursor(); ShowMouseCursor();
} }
void GMainWindow::mouseMoveEvent([[maybe_unused]] QMouseEvent* event) { void GMainWindow::mouseMoveEvent(QMouseEvent*) {
OnMouseActivity(); OnMouseActivity();
} }
void GMainWindow::mousePressEvent([[maybe_unused]] QMouseEvent* event) { void GMainWindow::mousePressEvent(QMouseEvent*) {
OnMouseActivity(); OnMouseActivity();
} }
void GMainWindow::mouseReleaseEvent([[maybe_unused]] QMouseEvent* event) { void GMainWindow::mouseReleaseEvent(QMouseEvent*) {
OnMouseActivity(); OnMouseActivity();
} }
void GMainWindow::moveEvent(QMoveEvent* event) {
QMainWindow::moveEvent(event);
const auto screen_pos = Common::MakeVec(pos().x(), pos().y());
render_window->SetScreenPos(screen_pos);
}
void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) { void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) {
QString status_message; QString status_message;
@ -2785,9 +2791,13 @@ void GMainWindow::UpdateWindowTitle() {
const QString full_name = QString::fromUtf8(Common::g_build_fullname); const QString full_name = QString::fromUtf8(Common::g_build_fullname);
if (game_title.isEmpty()) { if (game_title.isEmpty()) {
setWindowTitle(tr("Citra %1").arg(full_name)); setWindowTitle(QStringLiteral("Citra %1").arg(full_name));
} else { } else {
setWindowTitle(tr("Citra %1| %2").arg(full_name, game_title)); setWindowTitle(QStringLiteral("Citra %1 | %2").arg(full_name, game_title));
render_window->setWindowTitle(
QStringLiteral("Citra %1 | %2 | %3").arg(full_name, game_title, tr("Primary Window")));
secondary_window->setWindowTitle(QStringLiteral("Citra %1 | %2 | %3")
.arg(full_name, game_title, tr("Secondary Window")));
} }
} }
@ -2845,7 +2855,7 @@ void GMainWindow::RetranslateStatusBar() {
void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
#ifdef USE_DISCORD_PRESENCE #ifdef USE_DISCORD_PRESENCE
if (state) { if (state) {
discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>(); discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>(system);
} else { } else {
discord_rpc = std::make_unique<DiscordRPC::NullImpl>(); discord_rpc = std::make_unique<DiscordRPC::NullImpl>();
} }

View File

@ -374,6 +374,7 @@ protected:
void mouseMoveEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override;
void mousePressEvent(QMouseEvent* event) override; void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override;
void moveEvent(QMoveEvent* event) override;
}; };
Q_DECLARE_METATYPE(std::size_t); Q_DECLARE_METATYPE(std::size_t);

View File

@ -15,8 +15,9 @@
#include "core/movie.h" #include "core/movie.h"
#include "ui_movie_play_dialog.h" #include "ui_movie_play_dialog.h"
MoviePlayDialog::MoviePlayDialog(QWidget* parent, GameList* game_list_) MoviePlayDialog::MoviePlayDialog(QWidget* parent, GameList* game_list_, const Core::System& system_)
: QDialog(parent), ui(std::make_unique<Ui::MoviePlayDialog>()), game_list(game_list_) { : QDialog(parent),
ui(std::make_unique<Ui::MoviePlayDialog>()), game_list{game_list_}, system{system_} {
ui->setupUi(this); ui->setupUi(this);
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
@ -26,10 +27,10 @@ MoviePlayDialog::MoviePlayDialog(QWidget* parent, GameList* game_list_)
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &MoviePlayDialog::accept); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &MoviePlayDialog::accept);
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &MoviePlayDialog::reject); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &MoviePlayDialog::reject);
if (Core::System::GetInstance().IsPoweredOn()) { if (system.IsPoweredOn()) {
QString note_text; QString note_text;
note_text = tr("Current running game will be stopped."); note_text = tr("Current running game will be stopped.");
if (Core::Movie::GetInstance().GetPlayMode() == Core::Movie::PlayMode::Recording) { if (system.Movie().GetPlayMode() == Core::Movie::PlayMode::Recording) {
note_text.append(tr("<br>Current recording will be discarded.")); note_text.append(tr("<br>Current recording will be discarded."));
} }
ui->note2Label->setText(note_text); ui->note2Label->setText(note_text);
@ -43,7 +44,7 @@ QString MoviePlayDialog::GetMoviePath() const {
} }
QString MoviePlayDialog::GetGamePath() const { QString MoviePlayDialog::GetGamePath() const {
const auto metadata = Core::Movie::GetInstance().GetMovieMetadata(GetMoviePath().toStdString()); const auto metadata = system.Movie().GetMovieMetadata(GetMoviePath().toStdString());
return game_list->FindGameByProgramID(metadata.program_id, GameListItemPath::FullPathRole); return game_list->FindGameByProgramID(metadata.program_id, GameListItemPath::FullPathRole);
} }
@ -67,9 +68,10 @@ void MoviePlayDialog::UpdateUIDisplay() {
ui->lengthLineEdit->clear(); ui->lengthLineEdit->clear();
ui->note1Label->setVisible(true); ui->note1Label->setVisible(true);
const auto& movie = system.Movie();
const auto path = GetMoviePath().toStdString(); const auto path = GetMoviePath().toStdString();
const auto validation_result = Core::Movie::GetInstance().ValidateMovie(path); const auto validation_result = movie.ValidateMovie(path);
if (validation_result == Core::Movie::ValidationResult::Invalid) { if (validation_result == Core::Movie::ValidationResult::Invalid) {
ui->note1Label->setText(tr("Invalid movie file.")); ui->note1Label->setText(tr("Invalid movie file."));
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
@ -94,7 +96,7 @@ void MoviePlayDialog::UpdateUIDisplay() {
UNREACHABLE(); UNREACHABLE();
} }
const auto metadata = Core::Movie::GetInstance().GetMovieMetadata(path); const auto metadata = movie.GetMovieMetadata(path);
// Format game title // Format game title
const auto title = const auto title =

View File

@ -11,11 +11,15 @@ namespace Ui {
class MoviePlayDialog; class MoviePlayDialog;
} }
namespace Core {
class System;
}
class MoviePlayDialog : public QDialog { class MoviePlayDialog : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit MoviePlayDialog(QWidget* parent, GameList* game_list); explicit MoviePlayDialog(QWidget* parent, GameList* game_list, const Core::System& system);
~MoviePlayDialog() override; ~MoviePlayDialog() override;
QString GetMoviePath() const; QString GetMoviePath() const;
@ -27,4 +31,5 @@ private:
std::unique_ptr<Ui::MoviePlayDialog> ui; std::unique_ptr<Ui::MoviePlayDialog> ui;
GameList* game_list; GameList* game_list;
const Core::System& system;
}; };

View File

@ -10,8 +10,8 @@
#include "core/movie.h" #include "core/movie.h"
#include "ui_movie_record_dialog.h" #include "ui_movie_record_dialog.h"
MovieRecordDialog::MovieRecordDialog(QWidget* parent) MovieRecordDialog::MovieRecordDialog(QWidget* parent, const Core::System& system_)
: QDialog(parent), ui(std::make_unique<Ui::MovieRecordDialog>()) { : QDialog(parent), ui(std::make_unique<Ui::MovieRecordDialog>()), system{system_} {
ui->setupUi(this); ui->setupUi(this);
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
@ -23,9 +23,9 @@ MovieRecordDialog::MovieRecordDialog(QWidget* parent)
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &MovieRecordDialog::reject); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &MovieRecordDialog::reject);
QString note_text; QString note_text;
if (Core::System::GetInstance().IsPoweredOn()) { if (system.IsPoweredOn()) {
note_text = tr("Current running game will be restarted."); note_text = tr("Current running game will be restarted.");
if (Core::Movie::GetInstance().GetPlayMode() == Core::Movie::PlayMode::Recording) { if (system.Movie().GetPlayMode() == Core::Movie::PlayMode::Recording) {
note_text.append(tr("<br>Current recording will be discarded.")); note_text.append(tr("<br>Current recording will be discarded."));
} }
} else { } else {

View File

@ -9,11 +9,15 @@ namespace Ui {
class MovieRecordDialog; class MovieRecordDialog;
} }
namespace Core {
class System;
}
class MovieRecordDialog : public QDialog { class MovieRecordDialog : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit MovieRecordDialog(QWidget* parent); explicit MovieRecordDialog(QWidget* parent, const Core::System& system);
~MovieRecordDialog() override; ~MovieRecordDialog() override;
QString GetPath() const; QString GetPath() const;
@ -24,4 +28,5 @@ private:
void UpdateUIDisplay(); void UpdateUIDisplay();
std::unique_ptr<Ui::MovieRecordDialog> ui; std::unique_ptr<Ui::MovieRecordDialog> ui;
const Core::System& system;
}; };

View File

@ -9,10 +9,8 @@
#include <QString> #include <QString>
#include <QtConcurrent/QtConcurrentRun> #include <QtConcurrent/QtConcurrentRun>
#include "citra_qt/main.h" #include "citra_qt/main.h"
#include "citra_qt/multiplayer/client_room.h"
#include "citra_qt/multiplayer/direct_connect.h" #include "citra_qt/multiplayer/direct_connect.h"
#include "citra_qt/multiplayer/message.h" #include "citra_qt/multiplayer/message.h"
#include "citra_qt/multiplayer/state.h"
#include "citra_qt/multiplayer/validation.h" #include "citra_qt/multiplayer/validation.h"
#include "citra_qt/uisettings.h" #include "citra_qt/uisettings.h"
#include "core/hle/service/cfg/cfg.h" #include "core/hle/service/cfg/cfg.h"
@ -22,9 +20,9 @@
enum class ConnectionType : u8 { TraversalServer, IP }; enum class ConnectionType : u8 { TraversalServer, IP };
DirectConnectWindow::DirectConnectWindow(QWidget* parent) DirectConnectWindow::DirectConnectWindow(Core::System& system_, QWidget* parent)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
ui(std::make_unique<Ui::DirectConnect>()) { ui(std::make_unique<Ui::DirectConnect>()), system{system_} {
ui->setupUi(this); ui->setupUi(this);
@ -91,7 +89,7 @@ void DirectConnectWindow::Connect() {
if (auto room_member = Network::GetRoomMember().lock()) { if (auto room_member = Network::GetRoomMember().lock()) {
auto port = UISettings::values.port.toUInt(); auto port = UISettings::values.port.toUInt();
room_member->Join(ui->nickname->text().toStdString(), room_member->Join(ui->nickname->text().toStdString(),
Service::CFG::GetConsoleIdHash(Core::System::GetInstance()), Service::CFG::GetConsoleIdHash(system),
ui->ip->text().toStdString().c_str(), port, 0, ui->ip->text().toStdString().c_str(), port, 0,
Network::NoPreferredMac, ui->password->text().toStdString().c_str()); Network::NoPreferredMac, ui->password->text().toStdString().c_str());
} }

View File

@ -13,11 +13,15 @@ namespace Ui {
class DirectConnect; class DirectConnect;
} }
namespace Core {
class System;
}
class DirectConnectWindow : public QDialog { class DirectConnectWindow : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit DirectConnectWindow(QWidget* parent = nullptr); explicit DirectConnectWindow(Core::System& system, QWidget* parent = nullptr);
~DirectConnectWindow(); ~DirectConnectWindow();
void RetranslateUi(); void RetranslateUi();
@ -39,5 +43,6 @@ private:
QFutureWatcher<void>* watcher; QFutureWatcher<void>* watcher;
std::unique_ptr<Ui::DirectConnect> ui; std::unique_ptr<Ui::DirectConnect> ui;
Core::System& system;
Validation validation; Validation validation;
}; };

View File

@ -12,7 +12,6 @@
#include <QTime> #include <QTime>
#include <QtConcurrent/QtConcurrentRun> #include <QtConcurrent/QtConcurrentRun>
#include "citra_qt/game_list_p.h" #include "citra_qt/game_list_p.h"
#include "citra_qt/main.h"
#include "citra_qt/multiplayer/host_room.h" #include "citra_qt/multiplayer/host_room.h"
#include "citra_qt/multiplayer/message.h" #include "citra_qt/multiplayer/message.h"
#include "citra_qt/multiplayer/state.h" #include "citra_qt/multiplayer/state.h"
@ -27,10 +26,10 @@
#include "web_service/verify_user_jwt.h" #include "web_service/verify_user_jwt.h"
#endif #endif
HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list, HostRoomWindow::HostRoomWindow(Core::System& system_, QWidget* parent, QStandardItemModel* list,
std::shared_ptr<Network::AnnounceMultiplayerSession> session) std::shared_ptr<Network::AnnounceMultiplayerSession> session)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
ui(std::make_unique<Ui::HostRoom>()), announce_multiplayer_session(session) { ui(std::make_unique<Ui::HostRoom>()), system{system_}, announce_multiplayer_session(session) {
ui->setupUi(this); ui->setupUi(this);
// set up validation for all of the fields // set up validation for all of the fields
@ -196,9 +195,8 @@ void HostRoomWindow::Host() {
} }
} }
#endif #endif
member->Join(ui->username->text().toStdString(), member->Join(ui->username->text().toStdString(), Service::CFG::GetConsoleIdHash(system),
Service::CFG::GetConsoleIdHash(Core::System::GetInstance()), "127.0.0.1", port, "127.0.0.1", port, 0, Network::NoPreferredMac, password, token);
0, Network::NoPreferredMac, password, token);
// Store settings // Store settings
UISettings::values.room_nickname = ui->username->text(); UISettings::values.room_nickname = ui->username->text();

View File

@ -9,14 +9,16 @@
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QStandardItemModel> #include <QStandardItemModel>
#include <QVariant> #include <QVariant>
#include "citra_qt/multiplayer/chat_room.h"
#include "citra_qt/multiplayer/validation.h" #include "citra_qt/multiplayer/validation.h"
#include "network/network.h"
namespace Ui { namespace Ui {
class HostRoom; class HostRoom;
} }
namespace Core {
class System;
}
namespace Network { namespace Network {
class AnnounceMultiplayerSession; class AnnounceMultiplayerSession;
} }
@ -34,7 +36,7 @@ class HostRoomWindow : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit HostRoomWindow(QWidget* parent, QStandardItemModel* list, explicit HostRoomWindow(Core::System& system, QWidget* parent, QStandardItemModel* list,
std::shared_ptr<Network::AnnounceMultiplayerSession> session); std::shared_ptr<Network::AnnounceMultiplayerSession> session);
~HostRoomWindow(); ~HostRoomWindow();
@ -50,6 +52,7 @@ private:
std::unique_ptr<Network::VerifyUser::Backend> CreateVerifyBackend(bool use_validation) const; std::unique_ptr<Network::VerifyUser::Backend> CreateVerifyBackend(bool use_validation) const;
std::unique_ptr<Ui::HostRoom> ui; std::unique_ptr<Ui::HostRoom> ui;
Core::System& system;
std::weak_ptr<Network::AnnounceMultiplayerSession> announce_multiplayer_session; std::weak_ptr<Network::AnnounceMultiplayerSession> announce_multiplayer_session;
QStandardItemModel* game_list; QStandardItemModel* game_list;
ComboBoxProxyModel* proxy; ComboBoxProxyModel* proxy;

View File

@ -7,11 +7,9 @@
#include <QtConcurrent/QtConcurrentRun> #include <QtConcurrent/QtConcurrentRun>
#include "citra_qt/game_list_p.h" #include "citra_qt/game_list_p.h"
#include "citra_qt/main.h" #include "citra_qt/main.h"
#include "citra_qt/multiplayer/client_room.h"
#include "citra_qt/multiplayer/lobby.h" #include "citra_qt/multiplayer/lobby.h"
#include "citra_qt/multiplayer/lobby_p.h" #include "citra_qt/multiplayer/lobby_p.h"
#include "citra_qt/multiplayer/message.h" #include "citra_qt/multiplayer/message.h"
#include "citra_qt/multiplayer/state.h"
#include "citra_qt/multiplayer/validation.h" #include "citra_qt/multiplayer/validation.h"
#include "citra_qt/uisettings.h" #include "citra_qt/uisettings.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -23,10 +21,10 @@
#include "web_service/web_backend.h" #include "web_service/web_backend.h"
#endif #endif
Lobby::Lobby(QWidget* parent, QStandardItemModel* list, Lobby::Lobby(Core::System& system_, QWidget* parent, QStandardItemModel* list,
std::shared_ptr<Network::AnnounceMultiplayerSession> session) std::shared_ptr<Network::AnnounceMultiplayerSession> session)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
ui(std::make_unique<Ui::Lobby>()), announce_multiplayer_session(session) { ui(std::make_unique<Ui::Lobby>()), system{system_}, announce_multiplayer_session(session) {
ui->setupUi(this); ui->setupUi(this);
// setup the watcher for background connections // setup the watcher for background connections
@ -152,8 +150,8 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
const std::string verify_UID = const std::string verify_UID =
proxy->data(connection_index, LobbyItemHost::HostVerifyUIDRole).toString().toStdString(); proxy->data(connection_index, LobbyItemHost::HostVerifyUIDRole).toString().toStdString();
// attempt to connect in a different thread // Attempt to connect in a different thread
QFuture<void> f = QtConcurrent::run([nickname, ip, port, password, verify_UID] { QFuture<void> f = QtConcurrent::run([this, nickname, ip, port, password, verify_UID] {
std::string token; std::string token;
#ifdef ENABLE_WEB_SERVICE #ifdef ENABLE_WEB_SERVICE
if (!NetSettings::values.citra_username.empty() && if (!NetSettings::values.citra_username.empty() &&
@ -170,8 +168,8 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
} }
#endif #endif
if (auto room_member = Network::GetRoomMember().lock()) { if (auto room_member = Network::GetRoomMember().lock()) {
room_member->Join(nickname, Service::CFG::GetConsoleIdHash(Core::System::GetInstance()), room_member->Join(nickname, Service::CFG::GetConsoleIdHash(system), ip.c_str(), port, 0,
ip.c_str(), port, 0, Network::NoPreferredMac, password, token); Network::NoPreferredMac, password, token);
} }
}); });
watcher->setFuture(f); watcher->setFuture(f);

View File

@ -12,12 +12,16 @@
#include "citra_qt/multiplayer/validation.h" #include "citra_qt/multiplayer/validation.h"
#include "common/announce_multiplayer_room.h" #include "common/announce_multiplayer_room.h"
#include "network/announce_multiplayer_session.h" #include "network/announce_multiplayer_session.h"
#include "network/network.h" #include "network/room_member.h"
namespace Ui { namespace Ui {
class Lobby; class Lobby;
} }
namespace Core {
class System;
}
class LobbyModel; class LobbyModel;
class LobbyFilterProxyModel; class LobbyFilterProxyModel;
@ -29,7 +33,7 @@ class Lobby : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit Lobby(QWidget* parent, QStandardItemModel* list, explicit Lobby(Core::System& system, QWidget* parent, QStandardItemModel* list,
std::shared_ptr<Network::AnnounceMultiplayerSession> session); std::shared_ptr<Network::AnnounceMultiplayerSession> session);
~Lobby() override; ~Lobby() override;
@ -84,7 +88,9 @@ private:
*/ */
QString PasswordPrompt(); QString PasswordPrompt();
private:
std::unique_ptr<Ui::Lobby> ui; std::unique_ptr<Ui::Lobby> ui;
Core::System& system;
QStandardItemModel* model{}; QStandardItemModel* model{};
QStandardItemModel* game_list{}; QStandardItemModel* game_list{};

View File

@ -17,9 +17,10 @@
#include "citra_qt/util/clickable_label.h" #include "citra_qt/util/clickable_label.h"
#include "common/logging/log.h" #include "common/logging/log.h"
MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_list_model, MultiplayerState::MultiplayerState(Core::System& system_, QWidget* parent,
QAction* leave_room, QAction* show_room) QStandardItemModel* game_list_model, QAction* leave_room,
: QWidget(parent), game_list_model(game_list_model), leave_room(leave_room), QAction* show_room)
: QWidget(parent), system{system_}, game_list_model(game_list_model), leave_room(leave_room),
show_room(show_room) { show_room(show_room) {
if (auto member = Network::GetRoomMember().lock()) { if (auto member = Network::GetRoomMember().lock()) {
// register the network structs to use in slots and signals // register the network structs to use in slots and signals
@ -203,14 +204,14 @@ static void BringWidgetToFront(QWidget* widget) {
void MultiplayerState::OnViewLobby() { void MultiplayerState::OnViewLobby() {
if (lobby == nullptr) { if (lobby == nullptr) {
lobby = new Lobby(this, game_list_model, announce_multiplayer_session); lobby = new Lobby(system, this, game_list_model, announce_multiplayer_session);
} }
BringWidgetToFront(lobby); BringWidgetToFront(lobby);
} }
void MultiplayerState::OnCreateRoom() { void MultiplayerState::OnCreateRoom() {
if (host_room == nullptr) { if (host_room == nullptr) {
host_room = new HostRoomWindow(this, game_list_model, announce_multiplayer_session); host_room = new HostRoomWindow(system, this, game_list_model, announce_multiplayer_session);
} }
BringWidgetToFront(host_room); BringWidgetToFront(host_room);
} }
@ -274,7 +275,7 @@ void MultiplayerState::OnOpenNetworkRoom() {
void MultiplayerState::OnDirectConnectToRoom() { void MultiplayerState::OnDirectConnectToRoom() {
if (direct_connect == nullptr) { if (direct_connect == nullptr) {
direct_connect = new DirectConnectWindow(this); direct_connect = new DirectConnectWindow(system, this);
} }
BringWidgetToFront(direct_connect); BringWidgetToFront(direct_connect);
} }

View File

@ -15,12 +15,16 @@ class ClientRoomWindow;
class DirectConnectWindow; class DirectConnectWindow;
class ClickableLabel; class ClickableLabel;
namespace Core {
class System;
}
class MultiplayerState : public QWidget { class MultiplayerState : public QWidget {
Q_OBJECT; Q_OBJECT;
public: public:
explicit MultiplayerState(QWidget* parent, QStandardItemModel* game_list, QAction* leave_room, explicit MultiplayerState(Core::System& system, QWidget* parent, QStandardItemModel* game_list,
QAction* show_room); QAction* leave_room, QAction* show_room);
~MultiplayerState(); ~MultiplayerState();
/** /**
@ -71,6 +75,7 @@ signals:
void AnnounceFailed(const Common::WebResult&); void AnnounceFailed(const Common::WebResult&);
private: private:
Core::System& system;
Lobby* lobby = nullptr; Lobby* lobby = nullptr;
HostRoomWindow* host_room = nullptr; HostRoomWindow* host_room = nullptr;
ClientRoomWindow* client_room = nullptr; ClientRoomWindow* client_room = nullptr;

View File

@ -97,6 +97,12 @@ struct Values {
Settings::Setting<bool> game_list_hide_no_icon{false, "hideNoIcon"}; Settings::Setting<bool> game_list_hide_no_icon{false, "hideNoIcon"};
Settings::Setting<bool> game_list_single_line_mode{false, "singleLineMode"}; Settings::Setting<bool> game_list_single_line_mode{false, "singleLineMode"};
// Compatibility List
Settings::Setting<bool> show_compat_column{true, "show_compat_column"};
Settings::Setting<bool> show_region_column{true, "show_region_column"};
Settings::Setting<bool> show_type_column{true, "show_type_column"};
Settings::Setting<bool> show_size_column{true, "show_size_column"};
Settings::Setting<u16> screenshot_resolution_factor{0, "screenshot_resolution_factor"}; Settings::Setting<u16> screenshot_resolution_factor{0, "screenshot_resolution_factor"};
Settings::SwitchableSetting<std::string> screenshot_path{"", "screenshotPath"}; Settings::SwitchableSetting<std::string> screenshot_path{"", "screenshotPath"};

View File

@ -130,11 +130,9 @@ bool UpdaterPrivate::StartUpdateCheck() {
main_process->setProgram(tool_info.absoluteFilePath()); main_process->setProgram(tool_info.absoluteFilePath());
main_process->setArguments({QStringLiteral("--checkupdates"), QStringLiteral("-v")}); main_process->setArguments({QStringLiteral("--checkupdates"), QStringLiteral("-v")});
connect(main_process, connect(main_process, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this,
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this,
&UpdaterPrivate::UpdaterReady, Qt::QueuedConnection); &UpdaterPrivate::UpdaterReady, Qt::QueuedConnection);
connect(main_process, connect(main_process, qOverload<QProcess::ProcessError>(&QProcess::errorOccurred), this,
static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::errorOccurred), this,
&UpdaterPrivate::UpdaterError, Qt::QueuedConnection); &UpdaterPrivate::UpdaterError, Qt::QueuedConnection);
main_process->start(QIODevice::ReadOnly); main_process->start(QIODevice::ReadOnly);
@ -156,7 +154,7 @@ void UpdaterPrivate::StopUpdateCheck(int delay, bool async) {
QTimer* timer = new QTimer(this); QTimer* timer = new QTimer(this);
timer->setSingleShot(true); timer->setSingleShot(true);
connect(timer, &QTimer::timeout, [this, timer]() { connect(timer, &QTimer::timeout, this, [this, timer]() {
StopUpdateCheck(0, false); StopUpdateCheck(0, false);
timer->deleteLater(); timer->deleteLater();
}); });

View File

@ -176,6 +176,9 @@ endif()
if (CITRA_USE_PRECOMPILED_HEADERS) if (CITRA_USE_PRECOMPILED_HEADERS)
target_precompile_headers(citra_common PRIVATE precompiled_headers.h) target_precompile_headers(citra_common PRIVATE precompiled_headers.h)
endif() endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND CMAKE_CXX_COMPILER_ID STREQUAL GNU)
target_link_libraries(citra_common PRIVATE backtrace dl) find_library(BACKTRACE_LIBRARY backtrace)
if (BACKTRACE_LIBRARY AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND CMAKE_CXX_COMPILER_ID STREQUAL GNU)
target_link_libraries(citra_common PRIVATE ${BACKTRACE_LIBRARY} dl)
target_compile_definitions(citra_common PRIVATE CITRA_LINUX_GCC_BACKTRACE)
endif() endif()

View File

@ -13,12 +13,11 @@
#define _SH_DENYWR 0 #define _SH_DENYWR 0
#endif #endif
#if defined(__linux__) && defined(__GNUG__) && !defined(__clang__) #ifdef CITRA_LINUX_GCC_BACKTRACE
#define BOOST_STACKTRACE_USE_BACKTRACE #define BOOST_STACKTRACE_USE_BACKTRACE
#include <boost/stacktrace.hpp> #include <boost/stacktrace.hpp>
#undef BOOST_STACKTRACE_USE_BACKTRACE #undef BOOST_STACKTRACE_USE_BACKTRACE
#include <signal.h> #include <signal.h>
#define CITRA_LINUX_GCC_BACKTRACE
#endif #endif
#include "common/bounded_threadsafe_queue.h" #include "common/bounded_threadsafe_queue.h"

View File

@ -84,7 +84,7 @@ void LogSettings() {
log_setting("Renderer_TextureFilter", GetTextureFilterName(values.texture_filter.GetValue())); log_setting("Renderer_TextureFilter", GetTextureFilterName(values.texture_filter.GetValue()));
log_setting("Stereoscopy_Render3d", values.render_3d.GetValue()); log_setting("Stereoscopy_Render3d", values.render_3d.GetValue());
log_setting("Stereoscopy_Factor3d", values.factor_3d.GetValue()); log_setting("Stereoscopy_Factor3d", values.factor_3d.GetValue());
log_setting("Stereoscopy_MonoRenderOption", values.mono_render_option.GetValue()); log_setting("Stereoscopy_SwapEyes", values.swap_eyes.GetValue());
if (values.render_3d.GetValue() == StereoRenderOption::Anaglyph) { if (values.render_3d.GetValue() == StereoRenderOption::Anaglyph) {
log_setting("Renderer_AnaglyphShader", values.anaglyph_shader_name.GetValue()); log_setting("Renderer_AnaglyphShader", values.anaglyph_shader_name.GetValue());
} }

View File

@ -49,19 +49,12 @@ enum class LayoutOption : u32 {
enum class StereoRenderOption : u32 { enum class StereoRenderOption : u32 {
Off = 0, Off = 0,
SideBySide = 1, SideBySide = 1,
Anaglyph = 2, TopBottom = 2,
Interlaced = 3, Anaglyph = 3,
ReverseInterlaced = 4, Interlaced = 4,
CardboardVR = 5 CardboardVR = 5
}; };
// Which eye to render when 3d is off. 800px wide mode could be added here in the future, when
// implemented
enum class MonoRenderOption : u32 {
LeftEye = 0,
RightEye = 1,
};
enum class AudioEmulation : u32 { enum class AudioEmulation : u32 {
HLE = 0, HLE = 0,
LLE = 1, LLE = 1,
@ -100,13 +93,14 @@ enum Values {
ZR, ZR,
Home, Home,
Power,
NumButtons, NumButtons,
}; };
constexpr int BUTTON_HID_BEGIN = A; constexpr int BUTTON_HID_BEGIN = A;
constexpr int BUTTON_IR_BEGIN = ZL; constexpr int BUTTON_IR_BEGIN = ZL;
constexpr int BUTTON_NS_BEGIN = Home; constexpr int BUTTON_NS_BEGIN = Power;
constexpr int BUTTON_HID_END = BUTTON_IR_BEGIN; constexpr int BUTTON_HID_END = BUTTON_IR_BEGIN;
constexpr int BUTTON_IR_END = BUTTON_NS_BEGIN; constexpr int BUTTON_IR_END = BUTTON_NS_BEGIN;
@ -134,6 +128,7 @@ static const std::array<const char*, NumButtons> mapping = {{
"button_zl", "button_zl",
"button_zr", "button_zr",
"button_home", "button_home",
"button_power",
}}; }};
} // namespace NativeButton } // namespace NativeButton
@ -465,8 +460,7 @@ struct Values {
SwitchableSetting<StereoRenderOption> render_3d{StereoRenderOption::Off, "render_3d"}; SwitchableSetting<StereoRenderOption> render_3d{StereoRenderOption::Off, "render_3d"};
SwitchableSetting<u32> factor_3d{0, "factor_3d"}; SwitchableSetting<u32> factor_3d{0, "factor_3d"};
SwitchableSetting<MonoRenderOption> mono_render_option{MonoRenderOption::LeftEye, SwitchableSetting<bool> swap_eyes{false, "swap_eyes"};
"mono_render_option"};
Setting<u32> cardboard_screen_size{85, "cardboard_screen_size"}; Setting<u32> cardboard_screen_size{85, "cardboard_screen_size"};
Setting<s32> cardboard_x_shift{0, "cardboard_x_shift"}; Setting<s32> cardboard_x_shift{0, "cardboard_x_shift"};

View File

@ -62,12 +62,29 @@ public:
return SlotId{index}; return SlotId{index};
} }
template <typename... Args>
[[nodiscard]] SlotId swap_and_insert(SlotId existing_id, Args&&... args) noexcept {
const u32 index = FreeValueIndex();
T& existing_value = values[existing_id.index].object;
new (&values[index].object) T(std::move(existing_value));
existing_value.~T();
new (&values[existing_id.index].object) T(std::forward<Args>(args)...);
SetStorageBit(index);
return SlotId{index};
}
void erase(SlotId id) noexcept { void erase(SlotId id) noexcept {
values[id.index].object.~T(); values[id.index].object.~T();
free_list.push_back(id.index); free_list.push_back(id.index);
ResetStorageBit(id.index); ResetStorageBit(id.index);
} }
size_t size() const noexcept {
return values_capacity - free_list.size();
}
private: private:
struct NonTrivialDummy { struct NonTrivialDummy {
NonTrivialDummy() noexcept {} NonTrivialDummy() noexcept {}
@ -93,7 +110,7 @@ private:
return ((stored_bitset[index / 64] >> (index % 64)) & 1) != 0; return ((stored_bitset[index / 64] >> (index % 64)) & 1) != 0;
} }
void ValidateIndex(SlotId id) const noexcept { void ValidateIndex([[maybe_unused]] SlotId id) const noexcept {
DEBUG_ASSERT(id); DEBUG_ASSERT(id);
DEBUG_ASSERT(id.index / 64 < stored_bitset.size()); DEBUG_ASSERT(id.index / 64 < stored_bitset.size());
DEBUG_ASSERT(((stored_bitset[id.index / 64] >> (id.index % 64)) & 1) != 0); DEBUG_ASSERT(((stored_bitset[id.index / 64] >> (id.index % 64)) & 1) != 0);

View File

@ -184,6 +184,8 @@ add_library(citra_core STATIC
hle/kernel/wait_object.h hle/kernel/wait_object.h
hle/lock.cpp hle/lock.cpp
hle/lock.h hle/lock.h
hle/mii.h
hle/mii.cpp
hle/result.h hle/result.h
hle/romfs.cpp hle/romfs.cpp
hle/romfs.h hle/romfs.h
@ -245,6 +247,8 @@ add_library(citra_core STATIC
hle/service/cam/cam_s.h hle/service/cam/cam_s.h
hle/service/cam/cam_u.cpp hle/service/cam/cam_u.cpp
hle/service/cam/cam_u.h hle/service/cam/cam_u.h
hle/service/cam/y2r_u.cpp
hle/service/cam/y2r_u.h
hle/service/cecd/cecd.cpp hle/service/cecd/cecd.cpp
hle/service/cecd/cecd.h hle/service/cecd/cecd.h
hle/service/cecd/cecd_ndm.cpp hle/service/cecd/cecd_ndm.cpp
@ -255,6 +259,8 @@ add_library(citra_core STATIC
hle/service/cecd/cecd_u.h hle/service/cecd/cecd_u.h
hle/service/cfg/cfg.cpp hle/service/cfg/cfg.cpp
hle/service/cfg/cfg.h hle/service/cfg/cfg.h
hle/service/cfg/cfg_defaults.cpp
hle/service/cfg/cfg_defaults.h
hle/service/cfg/cfg_i.cpp hle/service/cfg/cfg_i.cpp
hle/service/cfg/cfg_i.h hle/service/cfg/cfg_i.h
hle/service/cfg/cfg_nor.cpp hle/service/cfg/cfg_nor.cpp
@ -275,8 +281,8 @@ add_library(citra_core STATIC
hle/service/dlp/dlp_srvr.h hle/service/dlp/dlp_srvr.h
hle/service/dsp/dsp_dsp.cpp hle/service/dsp/dsp_dsp.cpp
hle/service/dsp/dsp_dsp.h hle/service/dsp/dsp_dsp.h
hle/service/err_f.cpp hle/service/err/err_f.cpp
hle/service/err_f.h hle/service/err/err_f.h
hle/service/frd/frd.cpp hle/service/frd/frd.cpp
hle/service/frd/frd.h hle/service/frd/frd.h
hle/service/frd/frd_a.cpp hle/service/frd/frd_a.cpp
@ -303,8 +309,8 @@ add_library(citra_core STATIC
hle/service/hid/hid_spvr.h hle/service/hid/hid_spvr.h
hle/service/hid/hid_user.cpp hle/service/hid/hid_user.cpp
hle/service/hid/hid_user.h hle/service/hid/hid_user.h
hle/service/http_c.cpp hle/service/http/http_c.cpp
hle/service/http_c.h hle/service/http/http_c.h
hle/service/ir/extra_hid.cpp hle/service/ir/extra_hid.cpp
hle/service/ir/extra_hid.h hle/service/ir/extra_hid.h
hle/service/ir/ir.cpp hle/service/ir/ir.cpp
@ -319,8 +325,8 @@ add_library(citra_core STATIC
hle/service/ldr_ro/cro_helper.h hle/service/ldr_ro/cro_helper.h
hle/service/ldr_ro/ldr_ro.cpp hle/service/ldr_ro/ldr_ro.cpp
hle/service/ldr_ro/ldr_ro.h hle/service/ldr_ro/ldr_ro.h
hle/service/mic_u.cpp hle/service/mic/mic_u.cpp
hle/service/mic_u.h hle/service/mic/mic_u.h
hle/service/mvd/mvd.cpp hle/service/mvd/mvd.cpp
hle/service/mvd/mvd.h hle/service/mvd/mvd.h
hle/service/mvd/mvd_std.cpp hle/service/mvd/mvd_std.cpp
@ -417,12 +423,10 @@ add_library(citra_core STATIC
hle/service/sm/sm.h hle/service/sm/sm.h
hle/service/sm/srv.cpp hle/service/sm/srv.cpp
hle/service/sm/srv.h hle/service/sm/srv.h
hle/service/soc_u.cpp hle/service/soc/soc_u.cpp
hle/service/soc_u.h hle/service/soc/soc_u.h
hle/service/ssl_c.cpp hle/service/ssl/ssl_c.cpp
hle/service/ssl_c.h hle/service/ssl/ssl_c.h
hle/service/y2r_u.cpp
hle/service/y2r_u.h
hw/aes/arithmetic128.cpp hw/aes/arithmetic128.cpp
hw/aes/arithmetic128.h hw/aes/arithmetic128.h
hw/aes/ccm.cpp hw/aes/ccm.cpp
@ -457,14 +461,6 @@ add_library(citra_core STATIC
perf_stats.cpp perf_stats.cpp
perf_stats.h perf_stats.h
precompiled_headers.h precompiled_headers.h
rpc/packet.cpp
rpc/packet.h
rpc/rpc_server.cpp
rpc/rpc_server.h
rpc/server.cpp
rpc/server.h
rpc/udp_server.cpp
rpc/udp_server.h
savestate.cpp savestate.cpp
savestate.h savestate.h
system_titles.cpp system_titles.cpp
@ -479,18 +475,32 @@ add_library(citra_core STATIC
create_target_directory_groups(citra_core) create_target_directory_groups(citra_core)
target_link_libraries(citra_core PUBLIC citra_common PRIVATE audio_core network video_core) target_link_libraries(citra_core PUBLIC citra_common PRIVATE audio_core network video_core)
target_link_libraries(citra_core PRIVATE Boost::boost Boost::serialization Boost::iostreams) target_link_libraries(citra_core PRIVATE Boost::boost Boost::serialization Boost::iostreams httplib)
target_link_libraries(citra_core PUBLIC dds-ktx PRIVATE cryptopp fmt::fmt lodepng open_source_archives) target_link_libraries(citra_core PUBLIC dds-ktx PRIVATE cryptopp fmt::fmt lodepng open_source_archives)
set_target_properties(citra_core PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO}) set_target_properties(citra_core PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
if (ENABLE_WEB_SERVICE) if (ENABLE_WEB_SERVICE)
target_compile_definitions(citra_core PRIVATE -DENABLE_WEB_SERVICE -DCPPHTTPLIB_OPENSSL_SUPPORT) target_compile_definitions(citra_core PRIVATE -DENABLE_WEB_SERVICE)
target_link_libraries(citra_core PRIVATE web_service ${OPENSSL_LIBS} httplib) target_link_libraries(citra_core PRIVATE web_service)
if (ANDROID) if (ANDROID)
target_link_libraries(citra_core PRIVATE ifaddrs) target_link_libraries(citra_core PRIVATE ifaddrs)
endif() endif()
endif() endif()
if (ENABLE_SCRIPTING)
target_compile_definitions(citra_core PRIVATE -DENABLE_SCRIPTING)
target_sources(citra_core PRIVATE
rpc/packet.cpp
rpc/packet.h
rpc/rpc_server.cpp
rpc/rpc_server.h
rpc/server.cpp
rpc/server.h
rpc/udp_server.cpp
rpc/udp_server.h
)
endif()
if ("x86_64" IN_LIST ARCHITECTURE OR "arm64" IN_LIST ARCHITECTURE) if ("x86_64" IN_LIST ARCHITECTURE OR "arm64" IN_LIST ARCHITECTURE)
target_sources(citra_core PRIVATE target_sources(citra_core PRIVATE
arm/dynarmic/arm_dynarmic.cpp arm/dynarmic/arm_dynarmic.cpp

View File

@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <exception>
#include <memory>
#include <stdexcept> #include <stdexcept>
#include <utility> #include <utility>
#include <boost/serialization/array.hpp> #include <boost/serialization/array.hpp>
@ -16,6 +14,8 @@
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/arm/exclusive_monitor.h" #include "core/arm/exclusive_monitor.h"
#include "core/hle/service/cam/cam.h" #include "core/hle/service/cam/cam.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/ir/ir_user.h"
#if CITRA_ARCH(x86_64) || CITRA_ARCH(arm64) #if CITRA_ARCH(x86_64) || CITRA_ARCH(arm64)
#include "core/arm/dynarmic/arm_dynarmic.h" #include "core/arm/dynarmic/arm_dynarmic.h"
#endif #endif
@ -35,10 +35,8 @@
#include "core/hle/service/cam/cam.h" #include "core/hle/service/cam/cam.h"
#include "core/hle/service/fs/archive.h" #include "core/hle/service/fs/archive.h"
#include "core/hle/service/gsp/gsp.h" #include "core/hle/service/gsp/gsp.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/ir/ir_rst.h" #include "core/hle/service/ir/ir_rst.h"
#include "core/hle/service/ir/ir_user.h" #include "core/hle/service/mic/mic_u.h"
#include "core/hle/service/mic_u.h"
#include "core/hle/service/plgldr/plgldr.h" #include "core/hle/service/plgldr/plgldr.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/sm.h"
@ -47,7 +45,10 @@
#include "core/hw/lcd.h" #include "core/hw/lcd.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/movie.h" #include "core/movie.h"
#include "core/rpc/rpc_server.h" #ifdef ENABLE_SCRIPTING
#include "core/rpc/server.h"
#endif
#include "core/telemetry_session.h"
#include "network/network.h" #include "network/network.h"
#include "video_core/custom_textures/custom_tex_manager.h" #include "video_core/custom_textures/custom_tex_manager.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
@ -72,6 +73,8 @@ Core::Timing& Global() {
return System::GetInstance().CoreTiming(); return System::GetInstance().CoreTiming();
} }
System::System() : movie{*this} {}
System::~System() = default; System::~System() = default;
System::ResultStatus System::RunLoop(bool tight_loop) { System::ResultStatus System::RunLoop(bool tight_loop) {
@ -85,7 +88,7 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
if (thread && running_core) { if (thread && running_core) {
running_core->SaveContext(thread->context); running_core->SaveContext(thread->context);
} }
GDBStub::HandlePacket(); GDBStub::HandlePacket(*this);
// If the loop is halted and we want to step, use a tiny (1) number of instructions to // If the loop is halted and we want to step, use a tiny (1) number of instructions to
// execute. Otherwise, get out of the loop function. // execute. Otherwise, get out of the loop function.
@ -259,14 +262,13 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
return ResultStatus::ErrorGetLoader; return ResultStatus::ErrorGetLoader;
} }
std::pair<std::optional<u32>, Loader::ResultStatus> system_mode =
app_loader->LoadKernelSystemMode();
if (system_mode.second != Loader::ResultStatus::Success) { auto memory_mode = app_loader->LoadKernelMemoryMode();
if (memory_mode.second != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!", LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
static_cast<int>(system_mode.second)); static_cast<int>(memory_mode.second));
switch (system_mode.second) { switch (memory_mode.second) {
case Loader::ResultStatus::ErrorEncrypted: case Loader::ResultStatus::ErrorEncrypted:
return ResultStatus::ErrorLoader_ErrorEncrypted; return ResultStatus::ErrorLoader_ErrorEncrypted;
case Loader::ResultStatus::ErrorInvalidFormat: case Loader::ResultStatus::ErrorInvalidFormat:
@ -278,15 +280,15 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
} }
} }
ASSERT(system_mode.first); ASSERT(memory_mode.first);
auto n3ds_mode = app_loader->LoadKernelN3dsMode(); auto n3ds_hw_caps = app_loader->LoadNew3dsHwCapabilities();
ASSERT(n3ds_mode.first); ASSERT(n3ds_hw_caps.first);
u32 num_cores = 2; u32 num_cores = 2;
if (Settings::values.is_new_3ds) { if (Settings::values.is_new_3ds) {
num_cores = 4; num_cores = 4;
} }
ResultStatus init_result{ ResultStatus init_result{
Init(emu_window, secondary_window, *system_mode.first, *n3ds_mode.first, num_cores)}; Init(emu_window, secondary_window, *memory_mode.first, *n3ds_hw_caps.first, num_cores)};
if (init_result != ResultStatus::Success) { if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<u32>(init_result)); static_cast<u32>(init_result));
@ -321,12 +323,12 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
cheat_engine = std::make_unique<Cheats::CheatEngine>(title_id, *this); cheat_engine = std::make_unique<Cheats::CheatEngine>(title_id, *this);
perf_stats = std::make_unique<PerfStats>(title_id); perf_stats = std::make_unique<PerfStats>(title_id);
if (Settings::values.dump_textures) {
custom_tex_manager->PrepareDumping(title_id);
}
if (Settings::values.custom_textures) { if (Settings::values.custom_textures) {
custom_tex_manager->FindCustomTextures(); custom_tex_manager->FindCustomTextures();
} }
if (Settings::values.dump_textures) {
custom_tex_manager->WriteConfig();
}
status = ResultStatus::Success; status = ResultStatus::Success;
m_emu_window = &emu_window; m_emu_window = &emu_window;
@ -363,16 +365,18 @@ void System::Reschedule() {
} }
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, System::ResultStatus System::Init(Frontend::EmuWindow& emu_window,
Frontend::EmuWindow* secondary_window, u32 system_mode, Frontend::EmuWindow* secondary_window,
u8 n3ds_mode, u32 num_cores) { Kernel::MemoryMode memory_mode,
const Kernel::New3dsHwCapabilities& n3ds_hw_caps, u32 num_cores) {
LOG_DEBUG(HW_Memory, "initialized OK"); LOG_DEBUG(HW_Memory, "initialized OK");
memory = std::make_unique<Memory::MemorySystem>(); memory = std::make_unique<Memory::MemorySystem>(*this);
timing = std::make_unique<Timing>(num_cores, Settings::values.cpu_clock_percentage.GetValue()); timing = std::make_unique<Timing>(num_cores, Settings::values.cpu_clock_percentage.GetValue());
kernel = std::make_unique<Kernel::KernelSystem>( kernel = std::make_unique<Kernel::KernelSystem>(
*memory, *timing, [this] { PrepareReschedule(); }, system_mode, num_cores, n3ds_mode); *memory, *timing, [this] { PrepareReschedule(); }, memory_mode, num_cores, n3ds_hw_caps,
movie.GetOverrideInitTime());
exclusive_monitor = MakeExclusiveMonitor(*memory, num_cores); exclusive_monitor = MakeExclusiveMonitor(*memory, num_cores);
cpu_cores.reserve(num_cores); cpu_cores.reserve(num_cores);
@ -416,7 +420,9 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window,
telemetry_session = std::make_unique<Core::TelemetrySession>(); telemetry_session = std::make_unique<Core::TelemetrySession>();
rpc_server = std::make_unique<RPC::RPCServer>(); #ifdef ENABLE_SCRIPTING
rpc_server = std::make_unique<RPC::Server>(*this);
#endif
service_manager = std::make_unique<Service::SM::ServiceManager>(*this); service_manager = std::make_unique<Service::SM::ServiceManager>(*this);
archive_manager = std::make_unique<Service::FS::ArchiveManager>(*this); archive_manager = std::make_unique<Service::FS::ArchiveManager>(*this);
@ -508,6 +514,14 @@ const VideoCore::CustomTexManager& System::CustomTexManager() const {
return *custom_tex_manager; return *custom_tex_manager;
} }
Core::Movie& System::Movie() {
return movie;
}
const Core::Movie& System::Movie() const {
return movie;
}
void System::RegisterMiiSelector(std::shared_ptr<Frontend::MiiSelector> mii_selector) { void System::RegisterMiiSelector(std::shared_ptr<Frontend::MiiSelector> mii_selector) {
registered_mii_selector = std::move(mii_selector); registered_mii_selector = std::move(mii_selector);
} }
@ -545,7 +559,9 @@ void System::Shutdown(bool is_deserializing) {
} }
custom_tex_manager.reset(); custom_tex_manager.reset();
telemetry_session.reset(); telemetry_session.reset();
#ifdef ENABLE_SCRIPTING
rpc_server.reset(); rpc_server.reset();
#endif
archive_manager.reset(); archive_manager.reset();
service_manager.reset(); service_manager.reset();
dsp_core.reset(); dsp_core.reset();
@ -617,16 +633,14 @@ void System::ApplySettings() {
if (VideoCore::g_renderer) { if (VideoCore::g_renderer) {
auto& settings = VideoCore::g_renderer->Settings(); auto& settings = VideoCore::g_renderer->Settings();
settings.bg_color_update_requested = true; settings.bg_color_update_requested = true;
settings.sampler_update_requested = true;
settings.shader_update_requested = true; settings.shader_update_requested = true;
settings.texture_filter_update_requested = true;
} }
if (IsPoweredOn()) { if (IsPoweredOn()) {
CoreTiming().UpdateClockSpeed(Settings::values.cpu_clock_percentage.GetValue()); CoreTiming().UpdateClockSpeed(Settings::values.cpu_clock_percentage.GetValue());
Core::DSP().SetSink(Settings::values.output_type.GetValue(), dsp_core->SetSink(Settings::values.output_type.GetValue(),
Settings::values.output_device.GetValue()); Settings::values.output_device.GetValue());
Core::DSP().EnableStretching(Settings::values.enable_audio_stretching.GetValue()); dsp_core->EnableStretching(Settings::values.enable_audio_stretching.GetValue());
auto hid = Service::HID::GetModule(*this); auto hid = Service::HID::GetModule(*this);
if (hid) { if (hid) {
@ -673,10 +687,10 @@ void System::serialize(Archive& ar, const unsigned int file_version) {
Shutdown(true); Shutdown(true);
// Re-initialize everything like it was before // Re-initialize everything like it was before
auto system_mode = this->app_loader->LoadKernelSystemMode(); auto memory_mode = this->app_loader->LoadKernelMemoryMode();
auto n3ds_mode = this->app_loader->LoadKernelN3dsMode(); auto n3ds_hw_caps = this->app_loader->LoadNew3dsHwCapabilities();
[[maybe_unused]] const System::ResultStatus result = Init( [[maybe_unused]] const System::ResultStatus result = Init(
*m_emu_window, m_secondary_window, *system_mode.first, *n3ds_mode.first, num_cores); *m_emu_window, m_secondary_window, *memory_mode.first, *n3ds_hw_caps.first, num_cores);
} }
// flush on save, don't flush on load // flush on save, don't flush on load
@ -704,7 +718,7 @@ void System::serialize(Archive& ar, const unsigned int file_version) {
ar&* kernel.get(); ar&* kernel.get();
VideoCore::serialize(ar, file_version); VideoCore::serialize(ar, file_version);
if (file_version >= 1) { if (file_version >= 1) {
ar& Movie::GetInstance(); ar& movie;
} }
// This needs to be set from somewhere - might as well be here! // This needs to be set from somewhere - might as well be here!

View File

@ -10,18 +10,17 @@
#include <string> #include <string>
#include <boost/serialization/version.hpp> #include <boost/serialization/version.hpp>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/frontend/applets/mii_selector.h" #include "core/arm/arm_interface.h"
#include "core/frontend/applets/swkbd.h" #include "core/movie.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/perf_stats.h" #include "core/perf_stats.h"
#include "core/telemetry_session.h"
class ARM_Interface; class ARM_Interface;
namespace Frontend { namespace Frontend {
class EmuWindow; class EmuWindow;
class ImageInterface; class ImageInterface;
class MiiSelector;
class SoftwareKeyboard;
} // namespace Frontend } // namespace Frontend
namespace Memory { namespace Memory {
@ -32,8 +31,8 @@ namespace AudioCore {
class DspInterface; class DspInterface;
} }
namespace RPC { namespace Core::RPC {
class RPCServer; class Server;
} }
namespace Service { namespace Service {
@ -47,7 +46,9 @@ class ArchiveManager;
namespace Kernel { namespace Kernel {
class KernelSystem; class KernelSystem;
} struct New3dsHwCapabilities;
enum class MemoryMode : u8;
} // namespace Kernel
namespace Cheats { namespace Cheats {
class CheatEngine; class CheatEngine;
@ -62,8 +63,13 @@ class CustomTexManager;
class RendererBase; class RendererBase;
} // namespace VideoCore } // namespace VideoCore
namespace Loader {
class AppLoader;
}
namespace Core { namespace Core {
class TelemetrySession;
class ExclusiveMonitor; class ExclusiveMonitor;
class Timing; class Timing;
@ -95,6 +101,7 @@ public:
ErrorUnknown ///< Any other error ErrorUnknown ///< Any other error
}; };
explicit System();
~System(); ~System();
/** /**
@ -187,6 +194,10 @@ public:
return *cpu_cores[core_id]; return *cpu_cores[core_id];
}; };
[[nodiscard]] const ARM_Interface& GetCore(u32 core_id) const {
return *cpu_cores[core_id];
};
[[nodiscard]] u32 GetNumCores() const { [[nodiscard]] u32 GetNumCores() const {
return static_cast<u32>(cpu_cores.size()); return static_cast<u32>(cpu_cores.size());
} }
@ -258,6 +269,12 @@ public:
/// Gets a const reference to the custom texture cache system /// Gets a const reference to the custom texture cache system
[[nodiscard]] const VideoCore::CustomTexManager& CustomTexManager() const; [[nodiscard]] const VideoCore::CustomTexManager& CustomTexManager() const;
/// Gets a reference to the movie recorder
[[nodiscard]] Core::Movie& Movie();
/// Gets a const reference to the movie recorder
[[nodiscard]] const Core::Movie& Movie() const;
/// Video Dumper interface /// Video Dumper interface
void RegisterVideoDumper(std::shared_ptr<VideoDumper::Backend> video_dumper); void RegisterVideoDumper(std::shared_ptr<VideoDumper::Backend> video_dumper);
@ -342,8 +359,10 @@ private:
* @return ResultStatus code, indicating if the operation succeeded. * @return ResultStatus code, indicating if the operation succeeded.
*/ */
[[nodiscard]] ResultStatus Init(Frontend::EmuWindow& emu_window, [[nodiscard]] ResultStatus Init(Frontend::EmuWindow& emu_window,
Frontend::EmuWindow* secondary_window, u32 system_mode, Frontend::EmuWindow* secondary_window,
u8 n3ds_mode, u32 num_cores); Kernel::MemoryMode memory_mode,
const Kernel::New3dsHwCapabilities& n3ds_hw_caps,
u32 num_cores);
/// Reschedule the core emulation /// Reschedule the core emulation
void Reschedule(); void Reschedule();
@ -371,6 +390,9 @@ private:
std::shared_ptr<Frontend::MiiSelector> registered_mii_selector; std::shared_ptr<Frontend::MiiSelector> registered_mii_selector;
std::shared_ptr<Frontend::SoftwareKeyboard> registered_swkbd; std::shared_ptr<Frontend::SoftwareKeyboard> registered_swkbd;
/// Movie recorder
Core::Movie movie;
/// Cheats manager /// Cheats manager
std::unique_ptr<Cheats::CheatEngine> cheat_engine; std::unique_ptr<Cheats::CheatEngine> cheat_engine;
@ -383,8 +405,10 @@ private:
/// Image interface /// Image interface
std::shared_ptr<Frontend::ImageInterface> registered_image_interface; std::shared_ptr<Frontend::ImageInterface> registered_image_interface;
#ifdef ENABLE_SCRIPTING
/// RPC Server for scripting support /// RPC Server for scripting support
std::unique_ptr<RPC::RPCServer> rpc_server; std::unique_ptr<RPC::Server> rpc_server;
#endif
std::unique_ptr<Service::FS::ArchiveManager> archive_manager; std::unique_ptr<Service::FS::ArchiveManager> archive_manager;
@ -433,10 +457,6 @@ private:
return System::GetInstance().GetNumCores(); return System::GetInstance().GetNumCores();
} }
[[nodiscard]] inline AudioCore::DspInterface& DSP() {
return System::GetInstance().DSP();
}
} // namespace Core } // namespace Core
BOOST_CLASS_VERSION(Core::System, 1) BOOST_CLASS_VERSION(Core::System, 1)

View File

@ -12,8 +12,8 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/file_util.h" #include "common/file_util.h"
#include "common/swap.h" #include "common/swap.h"
#include "core/core.h"
#include "core/file_sys/romfs_reader.h" #include "core/file_sys/romfs_reader.h"
#include "core/loader/loader.h"
enum NCSDContentIndex { Main = 0, Manual = 1, DLP = 2, New3DSUpdate = 6, Update = 7 }; enum NCSDContentIndex { Main = 0, Manual = 1, DLP = 2, New3DSUpdate = 6, Update = 7 };
@ -165,7 +165,11 @@ struct ExHeader_StorageInfo {
struct ExHeader_ARM11_SystemLocalCaps { struct ExHeader_ARM11_SystemLocalCaps {
u64_le program_id; u64_le program_id;
u32_le core_version; u32_le core_version;
u8 reserved_flag; union {
u8 n3ds_cpu_flags;
BitField<0, 1, u8> enable_l2_cache;
BitField<1, 1, u8> enable_804MHz_cpu;
};
u8 n3ds_mode; u8 n3ds_mode;
union { union {
u8 flags0; u8 flags0;

View File

@ -181,6 +181,12 @@ std::array<u8, 16> TitleMetadata::GetContentCTRByIndex(std::size_t index) const
return ctr; return ctr;
} }
bool TitleMetadata::HasEncryptedContent() const {
return std::any_of(tmd_chunks.begin(), tmd_chunks.end(), [](auto& chunk) {
return (static_cast<u16>(chunk.type) & FileSys::TMDContentTypeFlag::Encrypted) != 0;
});
}
void TitleMetadata::SetTitleID(u64 title_id) { void TitleMetadata::SetTitleID(u64 title_id) {
tmd_body.title_id = title_id; tmd_body.title_id = title_id;
} }

View File

@ -98,6 +98,7 @@ public:
u16 GetContentTypeByIndex(std::size_t index) const; u16 GetContentTypeByIndex(std::size_t index) const;
u64 GetContentSizeByIndex(std::size_t index) const; u64 GetContentSizeByIndex(std::size_t index) const;
std::array<u8, 16> GetContentCTRByIndex(std::size_t index) const; std::array<u8, 16> GetContentCTRByIndex(std::size_t index) const;
bool HasEncryptedContent() const;
void SetTitleID(u64 title_id); void SetTitleID(u64 title_id);
void SetTitleType(u32 type); void SetTitleType(u32 type);

View File

@ -11,12 +11,12 @@
namespace Frontend { namespace Frontend {
void MiiSelector::Finalize(u32 return_code, HLE::Applets::MiiData mii) { void MiiSelector::Finalize(u32 return_code, Mii::MiiData mii) {
data = {return_code, mii}; data = {return_code, mii};
} }
std::vector<HLE::Applets::MiiData> LoadMiis() { std::vector<Mii::MiiData> LoadMiis() {
std::vector<HLE::Applets::MiiData> miis; std::vector<Mii::MiiData> miis;
std::string nand_directory{FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)}; std::string nand_directory{FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)};
FileSys::ArchiveFactory_ExtSaveData extdata_archive_factory(nand_directory, true); FileSys::ArchiveFactory_ExtSaveData extdata_archive_factory(nand_directory, true);
@ -36,7 +36,7 @@ std::vector<HLE::Applets::MiiData> LoadMiis() {
u32 saved_miis_offset = 0x8; u32 saved_miis_offset = 0x8;
// The Mii Maker has a 100 Mii limit on the 3ds // The Mii Maker has a 100 Mii limit on the 3ds
for (int i = 0; i < 100; ++i) { for (int i = 0; i < 100; ++i) {
HLE::Applets::MiiData mii; Mii::MiiData mii;
std::array<u8, sizeof(mii)> mii_raw; std::array<u8, sizeof(mii)> mii_raw;
file->Read(saved_miis_offset, sizeof(mii), mii_raw.data()); file->Read(saved_miis_offset, sizeof(mii), mii_raw.data());
std::memcpy(&mii, mii_raw.data(), sizeof(mii)); std::memcpy(&mii, mii_raw.data(), sizeof(mii));

View File

@ -25,7 +25,7 @@ struct MiiSelectorConfig {
struct MiiSelectorData { struct MiiSelectorData {
u32 return_code; u32 return_code;
HLE::Applets::MiiData mii; Mii::MiiData mii;
}; };
class MiiSelector { class MiiSelector {
@ -43,14 +43,14 @@ public:
* Stores the data so that the HLE applet in core can * Stores the data so that the HLE applet in core can
* send this to the calling application * send this to the calling application
*/ */
void Finalize(u32 return_code, HLE::Applets::MiiData mii); void Finalize(u32 return_code, Mii::MiiData mii);
protected: protected:
MiiSelectorConfig config; MiiSelectorConfig config;
MiiSelectorData data; MiiSelectorData data;
}; };
std::vector<HLE::Applets::MiiData> LoadMiis(); std::vector<Mii::MiiData> LoadMiis();
class DefaultMiiSelector final : public MiiSelector { class DefaultMiiSelector final : public MiiSelector {
public: public:

View File

@ -55,15 +55,17 @@ EmuWindow::EmuWindow(bool is_secondary_) : is_secondary{is_secondary_} {
} }
EmuWindow::~EmuWindow() = default; EmuWindow::~EmuWindow() = default;
/**
* Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout bool EmuWindow::IsWithinTouchscreen(const Layout::FramebufferLayout& layout, unsigned framebuffer_x,
* @param layout FramebufferLayout object describing the framebuffer size and screen positions unsigned framebuffer_y) {
* @param framebuffer_x Framebuffer x-coordinate to check #ifndef ANDROID
* @param framebuffer_y Framebuffer y-coordinate to check // If separate windows and the touch is in the primary (top) screen, ignore it.
* @return True if the coordinates are within the touchpad, otherwise false if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows &&
*/ !is_secondary && !Settings::values.swap_screen.GetValue()) {
static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, unsigned framebuffer_x, return false;
unsigned framebuffer_y) { }
#endif
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) { if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) {
return (framebuffer_y >= layout.bottom_screen.top && return (framebuffer_y >= layout.bottom_screen.top &&
framebuffer_y < layout.bottom_screen.bottom && framebuffer_y < layout.bottom_screen.bottom &&
@ -71,6 +73,13 @@ static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, unsigne
framebuffer_x < layout.bottom_screen.right / 2) || framebuffer_x < layout.bottom_screen.right / 2) ||
(framebuffer_x >= (layout.bottom_screen.left / 2) + (layout.width / 2) && (framebuffer_x >= (layout.bottom_screen.left / 2) + (layout.width / 2) &&
framebuffer_x < (layout.bottom_screen.right / 2) + (layout.width / 2)))); framebuffer_x < (layout.bottom_screen.right / 2) + (layout.width / 2))));
} else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::TopBottom) {
return framebuffer_x >= layout.bottom_screen.left &&
framebuffer_x < layout.bottom_screen.right &&
((framebuffer_y >= layout.bottom_screen.top / 2 &&
framebuffer_y < layout.bottom_screen.bottom / 2) ||
(framebuffer_y >= (layout.bottom_screen.top / 2) + (layout.height / 2) &&
framebuffer_y < (layout.bottom_screen.bottom / 2) + (layout.height / 2)));
} else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR) { } else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR) {
return (framebuffer_y >= layout.bottom_screen.top && return (framebuffer_y >= layout.bottom_screen.top &&
framebuffer_y < layout.bottom_screen.bottom && framebuffer_y < layout.bottom_screen.bottom &&
@ -89,22 +98,35 @@ static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, unsigne
std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) const { std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) const {
if (new_x >= framebuffer_layout.width / 2) { if (new_x >= framebuffer_layout.width / 2) {
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) {
new_x -= framebuffer_layout.width / 2; new_x -= framebuffer_layout.width / 2;
else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR) } else if (Settings::values.render_3d.GetValue() ==
Settings::StereoRenderOption::CardboardVR) {
new_x -= new_x -=
(framebuffer_layout.width / 2) - (framebuffer_layout.cardboard.user_x_shift * 2); (framebuffer_layout.width / 2) - (framebuffer_layout.cardboard.user_x_shift * 2);
}
} }
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) { if (new_y >= framebuffer_layout.height / 2) {
new_x = std::max(new_x, framebuffer_layout.bottom_screen.left / 2); if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::TopBottom) {
new_x = std::min(new_x, framebuffer_layout.bottom_screen.right / 2 - 1); new_y -= framebuffer_layout.height / 2;
} else { }
new_x = std::max(new_x, framebuffer_layout.bottom_screen.left);
new_x = std::min(new_x, framebuffer_layout.bottom_screen.right - 1);
} }
new_y = std::max(new_y, framebuffer_layout.bottom_screen.top); if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) {
new_y = std::min(new_y, framebuffer_layout.bottom_screen.bottom - 1); new_x = std::clamp(new_x, framebuffer_layout.bottom_screen.left / 2,
framebuffer_layout.bottom_screen.right / 2 - 1);
} else {
new_x = std::clamp(new_x, framebuffer_layout.bottom_screen.left,
framebuffer_layout.bottom_screen.right - 1);
}
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::TopBottom) {
new_y = std::clamp(new_y, framebuffer_layout.bottom_screen.top / 2,
framebuffer_layout.bottom_screen.bottom / 2 - 1);
} else {
new_y = std::clamp(new_y, framebuffer_layout.bottom_screen.top,
framebuffer_layout.bottom_screen.bottom - 1);
}
return std::make_tuple(new_x, new_y); return std::make_tuple(new_x, new_y);
} }
@ -120,17 +142,25 @@ void EmuWindow::CreateTouchState() {
} }
bool EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) { bool EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
return false; return false;
}
if (framebuffer_x >= framebuffer_layout.width / 2) { if (framebuffer_x >= framebuffer_layout.width / 2) {
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) {
framebuffer_x -= framebuffer_layout.width / 2; framebuffer_x -= framebuffer_layout.width / 2;
else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR) } else if (Settings::values.render_3d.GetValue() ==
Settings::StereoRenderOption::CardboardVR) {
framebuffer_x -= framebuffer_x -=
(framebuffer_layout.width / 2) - (framebuffer_layout.cardboard.user_x_shift * 2); (framebuffer_layout.width / 2) - (framebuffer_layout.cardboard.user_x_shift * 2);
}
} }
std::lock_guard guard(touch_state->mutex); if (framebuffer_y >= framebuffer_layout.height / 2) {
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::TopBottom) {
framebuffer_y -= framebuffer_layout.height / 2;
}
}
std::scoped_lock lock{touch_state->mutex};
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) { if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) {
touch_state->touch_x = touch_state->touch_x =
static_cast<float>(framebuffer_x - framebuffer_layout.bottom_screen.left / 2) / static_cast<float>(framebuffer_x - framebuffer_layout.bottom_screen.left / 2) /
@ -141,9 +171,17 @@ bool EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
static_cast<float>(framebuffer_x - framebuffer_layout.bottom_screen.left) / static_cast<float>(framebuffer_x - framebuffer_layout.bottom_screen.left) /
(framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left); (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
} }
touch_state->touch_y =
static_cast<float>(framebuffer_y - framebuffer_layout.bottom_screen.top) / if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::TopBottom) {
(framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top); touch_state->touch_y =
static_cast<float>(framebuffer_y - framebuffer_layout.bottom_screen.top / 2) /
(framebuffer_layout.bottom_screen.bottom / 2 -
framebuffer_layout.bottom_screen.top / 2);
} else {
touch_state->touch_y =
static_cast<float>(framebuffer_y - framebuffer_layout.bottom_screen.top) /
(framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
}
if (!framebuffer_layout.is_rotated) { if (!framebuffer_layout.is_rotated) {
std::swap(touch_state->touch_x, touch_state->touch_y); std::swap(touch_state->touch_x, touch_state->touch_y);
@ -155,18 +193,20 @@ bool EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
} }
void EmuWindow::TouchReleased() { void EmuWindow::TouchReleased() {
std::lock_guard guard{touch_state->mutex}; std::scoped_lock lock{touch_state->mutex};
touch_state->touch_pressed = false; touch_state->touch_pressed = false;
touch_state->touch_x = 0; touch_state->touch_x = 0;
touch_state->touch_y = 0; touch_state->touch_y = 0;
} }
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!touch_state->touch_pressed) if (!touch_state->touch_pressed) {
return; return;
}
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y); std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
}
TouchPressed(framebuffer_x, framebuffer_y); TouchPressed(framebuffer_x, framebuffer_y);
} }

View File

@ -9,6 +9,7 @@
#include <utility> #include <utility>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/vector_math.h"
#include "core/3ds.h" #include "core/3ds.h"
#include "core/frontend/framebuffer_layout.h" #include "core/frontend/framebuffer_layout.h"
@ -217,6 +218,17 @@ public:
config = val; config = val;
} }
/**
* Updates the position of the client window area relative to the origin of the host display.
*/
void SetScreenPos(const Common::Vec2i pos) {
screen_pos = pos;
}
Common::Vec2i GetScreenPos() const {
return screen_pos;
}
/** /**
* Returns system information about the drawing area. * Returns system information about the drawing area.
*/ */
@ -274,6 +286,7 @@ protected:
bool is_secondary{}; bool is_secondary{};
bool strict_context_required{}; bool strict_context_required{};
WindowSystemInfo window_info; WindowSystemInfo window_info;
Common::Vec2i screen_pos{};
private: private:
/** /**
@ -288,6 +301,17 @@ private:
void CreateTouchState(); void CreateTouchState();
/**
* Check if the given x/y coordinates are within the touchpad specified by the framebuffer
* layout
* @param layout FramebufferLayout object describing the framebuffer size and screen positions
* @param framebuffer_x Framebuffer x-coordinate to check
* @param framebuffer_y Framebuffer y-coordinate to check
* @return True if the coordinates are within the touchpad, otherwise false
*/
bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, unsigned framebuffer_x,
unsigned framebuffer_y);
Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout
WindowConfig config{}; ///< Internal configuration (changes pending for being applied in WindowConfig config{}; ///< Internal configuration (changes pending for being applied in

Some files were not shown because too many files have changed in this diff Show More