Compare commits
54 Commits
shoo-soc
...
custom-tex
Author | SHA1 | Date | |
---|---|---|---|
87a86c90cd | |||
6d0cd5b00e | |||
5b52849f90 | |||
20f4677f80 | |||
0048e61fc7 | |||
aaeba6759e | |||
ebac2e4978 | |||
5b9f4d4129 | |||
b1b6f08926 | |||
7e6a761f07 | |||
6f7612f73d | |||
88ea66053e | |||
970f2284d8 | |||
baf3ea4beb | |||
35e208b447 | |||
f8b8b6e53c | |||
a955f02771 | |||
3fedc68230 | |||
335fb78c5c | |||
22c4eb86d7 | |||
964f9ee3cf | |||
bb364d9bc0 | |||
51996c54f0 | |||
539a1a0b6e | |||
8b21b902f2 | |||
19107cec4b | |||
a537f56766 | |||
b5e1a27a7e | |||
a9e390b1b1 | |||
71582a72a4 | |||
e783b0d4a9 | |||
700c00f021 | |||
9cb14044ec | |||
8b6b58a364 | |||
e043caac27 | |||
0bedb28bdc | |||
c10ffda91f | |||
5e8ae4fa8a | |||
7a7f485640 | |||
bbf833bceb | |||
8eebb83c2c | |||
d702915624 | |||
943d5eeddf | |||
f3ac6f054f | |||
a94af8ea62 | |||
6da4853360 | |||
b738584832 | |||
1cb34ea0d3 | |||
662bb9ba77 | |||
26e3f96983 | |||
cd3244f139 | |||
e5310b25d4 | |||
80033b84cb | |||
cf9bb90ae3 |
8
.github/workflows/ci-merge.js
vendored
8
.github/workflows/ci-merge.js
vendored
@ -11,7 +11,7 @@ async function checkBaseChanges(github, context) {
|
||||
repository(name:$name, owner:$owner) {
|
||||
ref(qualifiedName:$ref) {
|
||||
target {
|
||||
... on Commit { id pushedDate oid }
|
||||
... on Commit { id committedDate oid }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -22,9 +22,9 @@ async function checkBaseChanges(github, context) {
|
||||
ref: 'refs/heads/master',
|
||||
};
|
||||
const result = await github.graphql(query, variables);
|
||||
const pushedAt = result.repository.ref.target.pushedDate;
|
||||
console.log(`Last commit pushed at ${pushedAt}.`);
|
||||
const delta = new Date() - new Date(pushedAt);
|
||||
const committedAt = result.repository.ref.target.committedDate;
|
||||
console.log(`Last commit committed at ${committedAt}.`);
|
||||
const delta = new Date() - new Date(committedAt);
|
||||
if (delta <= DETECTION_TIME_FRAME) {
|
||||
console.info('New changes detected, triggering a new build.');
|
||||
return true;
|
||||
|
@ -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)
|
||||
|
||||
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)
|
||||
option(ENABLE_OPENAL "Enables the OpenAL audio backend" ON)
|
||||
@ -341,13 +342,6 @@ function(get_timestamp _var)
|
||||
set(${_var} "${timestamp}" PARENT_SCOPE)
|
||||
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
|
||||
include(GetGitRevisionDescription)
|
||||
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)
|
||||
get_timestamp(BUILD_DATE)
|
||||
|
||||
if (NOT USE_SYSTEM_BOOST)
|
||||
add_definitions( -DBOOST_ALL_NO_LIB )
|
||||
# Boost
|
||||
# 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()
|
||||
|
||||
enable_testing()
|
||||
add_subdirectory(externals)
|
||||
|
||||
# Boost
|
||||
if (USE_SYSTEM_BOOST)
|
||||
find_package(Boost 1.70.0 COMPONENTS serialization iostreams REQUIRED)
|
||||
else()
|
||||
# Boost (bundled)
|
||||
if (NOT USE_SYSTEM_BOOST)
|
||||
add_definitions( -DBOOST_ALL_NO_LIB )
|
||||
add_library(Boost::boost ALIAS boost)
|
||||
add_library(Boost::serialization ALIAS boost_serialization)
|
||||
add_library(Boost::iostreams ALIAS boost_iostreams)
|
||||
|
@ -57,9 +57,7 @@ if (DEFINED ENV{CI})
|
||||
set(BUILD_VERSION ${CMAKE_MATCH_1})
|
||||
endif()
|
||||
if (BUILD_VERSION)
|
||||
# This leaves a trailing space on the last word, but we actually want that
|
||||
# because of how it's styled in the title bar.
|
||||
set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ")
|
||||
set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION}")
|
||||
else()
|
||||
set(BUILD_FULLNAME "")
|
||||
endif()
|
||||
|
5
dist/qt_themes/qdarkstyle/style.qss
vendored
5
dist/qt_themes/qdarkstyle/style.qss
vendored
@ -298,6 +298,11 @@ QAbstractItemView:read-only {
|
||||
alternate-background-color: #232629;
|
||||
}
|
||||
|
||||
/* Workaround for https://bugreports.qt.io/browse/QTBUG-115529 */
|
||||
QAbstractItemView:item {
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
QWidget:focus {
|
||||
border: 1px solid #3daee9;
|
||||
}
|
||||
|
@ -481,6 +481,11 @@ QAbstractItemView QLineEdit {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
/* Workaround for https://bugreports.qt.io/browse/QTBUG-115529 */
|
||||
QAbstractItemView:item {
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
/* QAbstractScrollArea ----------------------------------------------------
|
||||
|
||||
https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qabstractscrollarea
|
||||
|
100
externals/CMakeLists.txt
vendored
100
externals/CMakeLists.txt
vendored
@ -12,27 +12,29 @@ include(DownloadExternals)
|
||||
include(ExternalProject)
|
||||
|
||||
# Boost
|
||||
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
|
||||
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
|
||||
set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "")
|
||||
add_library(boost INTERFACE)
|
||||
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR})
|
||||
if (NOT USE_SYSTEM_BOOST)
|
||||
message(STATUS "Including vendored Boost library")
|
||||
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
|
||||
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
|
||||
set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "")
|
||||
add_library(boost INTERFACE)
|
||||
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR})
|
||||
|
||||
# Boost::serialization
|
||||
file(GLOB boost_serialization_SRC "${CMAKE_SOURCE_DIR}/externals/boost/libs/serialization/src/*.cpp")
|
||||
add_library(boost_serialization STATIC ${boost_serialization_SRC})
|
||||
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::serialization
|
||||
file(GLOB boost_serialization_SRC "${CMAKE_SOURCE_DIR}/externals/boost/libs/serialization/src/*.cpp")
|
||||
add_library(boost_serialization STATIC ${boost_serialization_SRC})
|
||||
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)
|
||||
# Add additional boost libs here; remember to ALIAS them in the root CMakeLists!
|
||||
endif()
|
||||
|
||||
# Catch2
|
||||
set(CATCH_INSTALL_DOCS OFF CACHE BOOL "")
|
||||
@ -171,37 +173,39 @@ endif()
|
||||
add_library(json-headers INTERFACE)
|
||||
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)
|
||||
target_link_libraries(httplib INTERFACE ifaddrs)
|
||||
endif()
|
||||
|
||||
# cpp-jwt
|
||||
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)
|
||||
target_include_directories(cpp-jwt INTERFACE ./cpp-jwt/include)
|
||||
target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON)
|
||||
|
@ -29,6 +29,7 @@
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<application
|
||||
android:name="org.citra.citra_emu.CitraApplication"
|
||||
|
@ -1,7 +1,10 @@
|
||||
package org.citra.citra_emu.ui.main;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
@ -13,6 +16,7 @@ import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.splashscreen.SplashScreen;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
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
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
SplashScreen splashScreen = SplashScreen.installSplashScreen(this);
|
||||
@ -165,6 +172,12 @@ public final class MainActivity extends AppCompatActivity implements MainView {
|
||||
EmulationActivity.tryDismissRunningNotification(this);
|
||||
|
||||
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
|
||||
|
@ -13,8 +13,8 @@ public class EmulationMenuSettings {
|
||||
public static final int LayoutOption_SingleScreen = 1;
|
||||
public static final int LayoutOption_LargeScreen = 2;
|
||||
public static final int LayoutOption_SideScreen = 3;
|
||||
public static final int LayoutOption_MobilePortrait = 4;
|
||||
public static final int LayoutOption_MobileLandscape = 5;
|
||||
public static final int LayoutOption_MobilePortrait = 5;
|
||||
public static final int LayoutOption_MobileLandscape = 6;
|
||||
|
||||
public static boolean getJoystickRelCenter() {
|
||||
return mPreferences.getBoolean("EmulationMenuSettings_JoystickRelCenter", true);
|
||||
|
@ -53,7 +53,7 @@ void AndroidMiiSelector::Setup(const Frontend::MiiSelectorConfig& config) {
|
||||
const u32 return_code = static_cast<u32>(
|
||||
env->GetLongField(data, env->GetFieldID(s_mii_selector_data_class, "return_code", "J")));
|
||||
if (return_code == 1) {
|
||||
Finalize(return_code, HLE::Applets::MiiData{});
|
||||
Finalize(return_code, Mii::MiiData{});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ JNIEXPORT jlong JNICALL Java_org_citra_citra_1emu_model_GameInfo_initialize(JNIE
|
||||
Loader::SMDH* smdh = nullptr;
|
||||
if (Loader::IsValidSMDH(smdh_data)) {
|
||||
smdh = new Loader::SMDH;
|
||||
memcpy(smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
||||
std::memcpy(smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
||||
}
|
||||
return reinterpret_cast<jlong>(smdh);
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "core/hle/service/nfc/nfc.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/savestate.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "jni/android_common/android_common.h"
|
||||
#include "jni/applets/mii_selector.h"
|
||||
#include "jni/applets/swkbd.h"
|
||||
@ -151,12 +152,12 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
||||
Camera::RegisterFactory("ndk", std::move(ndk_factory));
|
||||
|
||||
// Register frontend applets
|
||||
Frontend::RegisterDefaultApplets();
|
||||
Frontend::RegisterDefaultApplets(system);
|
||||
system.RegisterMiiSelector(std::make_shared<MiiSelector::AndroidMiiSelector>());
|
||||
system.RegisterSoftwareKeyboard(std::make_shared<SoftwareKeyboard::AndroidKeyboard>());
|
||||
|
||||
// Register microphone permission check
|
||||
Core::System::GetInstance().RegisterMicPermissionCheck(&CheckMicPermission);
|
||||
system.RegisterMicPermissionCheck(&CheckMicPermission);
|
||||
|
||||
InputManager::Init();
|
||||
|
||||
@ -166,7 +167,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
||||
return load_result;
|
||||
}
|
||||
|
||||
auto& telemetry_session = Core::System::GetInstance().TelemetrySession();
|
||||
auto& telemetry_session = system.TelemetrySession();
|
||||
telemetry_session.AddField(Common::Telemetry::FieldType::App, "Frontend", "Android");
|
||||
|
||||
stop_run = false;
|
||||
@ -187,8 +188,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
||||
audio_stretching_event =
|
||||
system.CoreTiming().RegisterEvent("AudioStretchingEvent", [&](u64, s64 cycles_late) {
|
||||
if (Settings::values.enable_audio_stretching) {
|
||||
Core::DSP().EnableStretching(
|
||||
Core::System::GetInstance().GetAndResetPerfStats().emulation_speed < 0.95);
|
||||
system.DSP().EnableStretching(system.GetAndResetPerfStats().emulation_speed < 0.95);
|
||||
}
|
||||
|
||||
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; });
|
||||
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; });
|
||||
window->PollEvents();
|
||||
}
|
||||
@ -621,7 +621,7 @@ jobjectArray Java_org_citra_citra_1emu_NativeLibrary_GetSavestateInfo(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto savestates = Core::ListSaveStates(title_id);
|
||||
const auto savestates = Core::ListSaveStates(title_id, system.Movie().GetCurrentMovieID());
|
||||
const jobjectArray array =
|
||||
env->NewObjectArray(static_cast<jsize>(savestates.size()), savestate_info_class, nullptr);
|
||||
for (std::size_t i = 0; i < savestates.size(); ++i) {
|
||||
|
@ -37,39 +37,33 @@ CubebInput::CubebInput(std::string device_id)
|
||||
}
|
||||
|
||||
CubebInput::~CubebInput() {
|
||||
if (!impl->ctx)
|
||||
return;
|
||||
|
||||
if (impl->stream) {
|
||||
if (cubeb_stream_stop(impl->stream) != CUBEB_OK) {
|
||||
LOG_ERROR(Audio, "Error stopping cubeb input stream.");
|
||||
}
|
||||
|
||||
cubeb_stream_destroy(impl->stream);
|
||||
}
|
||||
|
||||
cubeb_destroy(impl->ctx);
|
||||
if (impl->ctx) {
|
||||
cubeb_destroy(impl->ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void CubebInput::StartSampling(const InputParameters& params) {
|
||||
// Cubeb apparently only supports signed 16 bit PCM (and float32 which the 3ds doesn't support)
|
||||
// TODO resample the input stream
|
||||
// TODO: Resample the input stream.
|
||||
if (params.sign == Signedness::Unsigned) {
|
||||
LOG_ERROR(Audio,
|
||||
"Application requested unsupported unsigned pcm format. Falling back to signed");
|
||||
"Application requested unsupported unsigned pcm format. Falling back to signed.");
|
||||
}
|
||||
|
||||
impl->sample_size_in_bytes = params.sample_size / 8;
|
||||
|
||||
parameters = params;
|
||||
is_sampling = true;
|
||||
impl->sample_size_in_bytes = params.sample_size / 8;
|
||||
|
||||
cubeb_devid input_device = nullptr;
|
||||
if (device_id != auto_device_name && !device_id.empty()) {
|
||||
cubeb_device_collection collection;
|
||||
if (cubeb_enumerate_devices(impl->ctx, CUBEB_DEVICE_TYPE_INPUT, &collection) != CUBEB_OK) {
|
||||
LOG_WARNING(Audio, "Audio input device enumeration not supported");
|
||||
} else {
|
||||
if (cubeb_enumerate_devices(impl->ctx, CUBEB_DEVICE_TYPE_INPUT, &collection) == CUBEB_OK) {
|
||||
const auto collection_end = collection.device + collection.count;
|
||||
const auto device = std::find_if(
|
||||
collection.device, collection_end, [this](const cubeb_device_info& info) {
|
||||
@ -79,39 +73,42 @@ void CubebInput::StartSampling(const InputParameters& params) {
|
||||
input_device = device->devid;
|
||||
}
|
||||
cubeb_device_collection_destroy(impl->ctx, &collection);
|
||||
} else {
|
||||
LOG_WARNING(Audio_Sink,
|
||||
"Audio input device enumeration not supported, using default device.");
|
||||
}
|
||||
}
|
||||
|
||||
cubeb_stream_params input_params;
|
||||
input_params.channels = 1;
|
||||
input_params.layout = CUBEB_LAYOUT_UNDEFINED;
|
||||
input_params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
input_params.format = CUBEB_SAMPLE_S16LE;
|
||||
input_params.rate = params.sample_rate;
|
||||
cubeb_stream_params input_params = {
|
||||
.format = CUBEB_SAMPLE_S16LE,
|
||||
.rate = params.sample_rate,
|
||||
.channels = 1,
|
||||
.layout = CUBEB_LAYOUT_UNDEFINED,
|
||||
};
|
||||
|
||||
u32 latency_frames = 512; // Firefox default
|
||||
if (cubeb_get_min_latency(impl->ctx, &input_params, &latency_frames) != CUBEB_OK) {
|
||||
LOG_ERROR(Audio, "Could not get minimum latency");
|
||||
LOG_WARNING(Audio, "Error getting minimum input latency, falling back to default latency.");
|
||||
}
|
||||
|
||||
if (cubeb_stream_init(impl->ctx, &impl->stream, "Citra Microphone", input_device, &input_params,
|
||||
nullptr, nullptr, latency_frames, Impl::DataCallback, Impl::StateCallback,
|
||||
impl.get()) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio, "Error creating cubeb input stream");
|
||||
is_sampling = false;
|
||||
LOG_CRITICAL(Audio, "Error creating cubeb input stream.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cubeb_stream_start(impl->stream) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio, "Error starting cubeb input stream");
|
||||
is_sampling = false;
|
||||
LOG_CRITICAL(Audio, "Error starting cubeb input stream.");
|
||||
cubeb_stream_destroy(impl->stream);
|
||||
impl->stream = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
is_sampling = true;
|
||||
}
|
||||
|
||||
void CubebInput::StopSampling() {
|
||||
// TODO(xperia64): Destroy the stream for now to avoid a leak because StartSampling
|
||||
// reinitializes the stream every time
|
||||
if (impl->stream) {
|
||||
cubeb_stream_stop(impl->stream);
|
||||
cubeb_stream_destroy(impl->stream);
|
||||
@ -121,8 +118,14 @@ void CubebInput::StopSampling() {
|
||||
}
|
||||
|
||||
void CubebInput::AdjustSampleRate(u32 sample_rate) {
|
||||
// TODO This should restart the stream with the new sample rate
|
||||
LOG_ERROR(Audio, "AdjustSampleRate unimplemented!");
|
||||
if (!is_sampling) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto new_parameters = parameters;
|
||||
new_parameters.sample_rate = sample_rate;
|
||||
StopSampling();
|
||||
StartSampling(new_parameters);
|
||||
}
|
||||
|
||||
Samples CubebInput::Read() {
|
||||
@ -136,7 +139,7 @@ Samples CubebInput::Read() {
|
||||
|
||||
long CubebInput::Impl::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
|
||||
void* output_buffer, long num_frames) {
|
||||
Impl* impl = static_cast<Impl*>(user_data);
|
||||
auto impl = static_cast<Impl*>(user_data);
|
||||
if (!impl) {
|
||||
return 0;
|
||||
}
|
||||
@ -177,9 +180,7 @@ std::vector<std::string> ListCubebInputDevices() {
|
||||
}
|
||||
|
||||
cubeb_device_collection collection;
|
||||
if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &collection) != CUBEB_OK) {
|
||||
LOG_WARNING(Audio_Sink, "Audio input device enumeration not supported");
|
||||
} else {
|
||||
if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &collection) == CUBEB_OK) {
|
||||
for (std::size_t i = 0; i < collection.count; i++) {
|
||||
const cubeb_device_info& device = collection.device[i];
|
||||
if (device.state == CUBEB_DEVICE_STATE_ENABLED && device.friendly_name) {
|
||||
@ -187,6 +188,8 @@ std::vector<std::string> ListCubebInputDevices() {
|
||||
}
|
||||
}
|
||||
cubeb_device_collection_destroy(ctx, &collection);
|
||||
} else {
|
||||
LOG_WARNING(Audio_Sink, "Audio input device enumeration not supported.");
|
||||
}
|
||||
|
||||
cubeb_destroy(ctx);
|
||||
|
@ -13,8 +13,6 @@
|
||||
namespace AudioCore {
|
||||
|
||||
struct CubebSink::Impl {
|
||||
unsigned int sample_rate = 0;
|
||||
|
||||
cubeb* ctx = nullptr;
|
||||
cubeb_stream* stream = nullptr;
|
||||
|
||||
@ -31,28 +29,29 @@ CubebSink::CubebSink(std::string_view target_device_name) : impl(std::make_uniqu
|
||||
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||
return;
|
||||
}
|
||||
cubeb_set_log_callback(CUBEB_LOG_NORMAL, &Impl::LogCallback);
|
||||
|
||||
impl->sample_rate = native_sample_rate;
|
||||
if (cubeb_set_log_callback(CUBEB_LOG_NORMAL, &Impl::LogCallback) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "cubeb_set_log_callback failed");
|
||||
return;
|
||||
}
|
||||
|
||||
cubeb_stream_params params;
|
||||
params.rate = impl->sample_rate;
|
||||
params.channels = 2;
|
||||
params.layout = CUBEB_LAYOUT_STEREO;
|
||||
params.format = CUBEB_SAMPLE_S16NE;
|
||||
params.prefs = CUBEB_STREAM_PREF_PERSIST;
|
||||
cubeb_stream_params params = {
|
||||
.format = CUBEB_SAMPLE_S16LE,
|
||||
.rate = native_sample_rate,
|
||||
.channels = 2,
|
||||
.layout = CUBEB_LAYOUT_STEREO,
|
||||
};
|
||||
|
||||
u32 minimum_latency = 100 * impl->sample_rate / 1000; // Firefox default
|
||||
u32 minimum_latency = 100 * native_sample_rate / 1000; // Firefox default
|
||||
if (cubeb_get_min_latency(impl->ctx, ¶ms, &minimum_latency) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
|
||||
LOG_WARNING(Audio_Sink,
|
||||
"Error getting minimum output latency, falling back to default latency.");
|
||||
}
|
||||
|
||||
cubeb_devid output_device = nullptr;
|
||||
if (target_device_name != auto_device_name && !target_device_name.empty()) {
|
||||
cubeb_device_collection collection;
|
||||
if (cubeb_enumerate_devices(impl->ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) {
|
||||
LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
|
||||
} else {
|
||||
if (cubeb_enumerate_devices(impl->ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) == CUBEB_OK) {
|
||||
const auto collection_end{collection.device + collection.count};
|
||||
const auto device{
|
||||
std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) {
|
||||
@ -63,12 +62,15 @@ CubebSink::CubebSink(std::string_view target_device_name) : impl(std::make_uniqu
|
||||
output_device = device->devid;
|
||||
}
|
||||
cubeb_device_collection_destroy(impl->ctx, &collection);
|
||||
} else {
|
||||
LOG_WARNING(Audio_Sink,
|
||||
"Audio output device enumeration not supported, using default device.");
|
||||
}
|
||||
}
|
||||
|
||||
int stream_err = cubeb_stream_init(impl->ctx, &impl->stream, "CitraAudio", nullptr, nullptr,
|
||||
output_device, ¶ms, std::max(512u, minimum_latency),
|
||||
&Impl::DataCallback, &Impl::StateCallback, impl.get());
|
||||
auto stream_err = cubeb_stream_init(impl->ctx, &impl->stream, "CitraAudio", nullptr, nullptr,
|
||||
output_device, ¶ms, std::max(512u, minimum_latency),
|
||||
&Impl::DataCallback, &Impl::StateCallback, impl.get());
|
||||
if (stream_err != CUBEB_OK) {
|
||||
switch (stream_err) {
|
||||
case CUBEB_ERROR:
|
||||
@ -92,23 +94,20 @@ CubebSink::CubebSink(std::string_view target_device_name) : impl(std::make_uniqu
|
||||
}
|
||||
|
||||
CubebSink::~CubebSink() {
|
||||
if (!impl->ctx) {
|
||||
return;
|
||||
if (impl->stream) {
|
||||
if (cubeb_stream_stop(impl->stream) != CUBEB_OK) {
|
||||
LOG_ERROR(Audio_Sink, "Error stopping cubeb stream.");
|
||||
}
|
||||
cubeb_stream_destroy(impl->stream);
|
||||
}
|
||||
|
||||
if (cubeb_stream_stop(impl->stream) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream");
|
||||
if (impl->ctx) {
|
||||
cubeb_destroy(impl->ctx);
|
||||
}
|
||||
|
||||
cubeb_stream_destroy(impl->stream);
|
||||
cubeb_destroy(impl->ctx);
|
||||
}
|
||||
|
||||
unsigned int CubebSink::GetNativeSampleRate() const {
|
||||
if (!impl->ctx)
|
||||
return native_sample_rate;
|
||||
|
||||
return impl->sample_rate;
|
||||
return native_sample_rate;
|
||||
}
|
||||
|
||||
void CubebSink::SetCallback(std::function<void(s16*, std::size_t)> cb) {
|
||||
@ -121,13 +120,12 @@ long CubebSink::Impl::DataCallback(cubeb_stream* stream, void* user_data, const
|
||||
auto* buffer = static_cast<s16*>(output_buffer);
|
||||
|
||||
if (!impl || !impl->cb) {
|
||||
LOG_DEBUG(Audio_Sink, "Emitting zeros");
|
||||
LOG_DEBUG(Audio_Sink, "Missing internal data and/or audio callback, emitting zeroes.");
|
||||
std::memset(output_buffer, 0, num_frames * 2 * sizeof(s16));
|
||||
return num_frames;
|
||||
} else {
|
||||
impl->cb(buffer, num_frames);
|
||||
}
|
||||
|
||||
impl->cb(buffer, num_frames);
|
||||
|
||||
return num_frames;
|
||||
}
|
||||
|
||||
@ -149,7 +147,7 @@ void CubebSink::Impl::StateCallback(cubeb_stream* stream, void* user_data, cubeb
|
||||
}
|
||||
|
||||
void CubebSink::Impl::LogCallback(char const* format, ...) {
|
||||
std::array<char, 512> buffer;
|
||||
std::array<char, 512> buffer{};
|
||||
std::va_list args;
|
||||
va_start(args, format);
|
||||
#ifdef _MSC_VER
|
||||
@ -166,15 +164,13 @@ std::vector<std::string> ListCubebSinkDevices() {
|
||||
std::vector<std::string> device_list;
|
||||
cubeb* ctx;
|
||||
|
||||
if (cubeb_init(&ctx, "CitraEnumerator", nullptr) != CUBEB_OK) {
|
||||
if (cubeb_init(&ctx, "Citra Output Device Enumerator", nullptr) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||
return {};
|
||||
}
|
||||
|
||||
cubeb_device_collection collection;
|
||||
if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) {
|
||||
LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
|
||||
} else {
|
||||
if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) == CUBEB_OK) {
|
||||
for (std::size_t i = 0; i < collection.count; i++) {
|
||||
const cubeb_device_info& device = collection.device[i];
|
||||
if (device.state == CUBEB_DEVICE_STATE_ENABLED && device.friendly_name) {
|
||||
@ -182,6 +178,8 @@ std::vector<std::string> ListCubebSinkDevices() {
|
||||
}
|
||||
}
|
||||
cubeb_device_collection_destroy(ctx, &collection);
|
||||
} else {
|
||||
LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported.");
|
||||
}
|
||||
|
||||
cubeb_destroy(ctx);
|
||||
|
@ -5,7 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <span>
|
||||
#include <boost/serialization/access.hpp>
|
||||
#include "audio_core/audio_types.h"
|
||||
#include "audio_core/time_stretch.h"
|
||||
@ -80,7 +80,7 @@ public:
|
||||
* @param pipe_number The Pipe ID
|
||||
* @param buffer The data to write to the pipe.
|
||||
*/
|
||||
virtual void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) = 0;
|
||||
virtual void PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) = 0;
|
||||
|
||||
/// Returns a reference to the array backing DSP memory
|
||||
virtual std::array<u8, Memory::DSP_RAM_SIZE>& GetDspMemory() = 0;
|
||||
@ -89,7 +89,7 @@ public:
|
||||
virtual void SetServiceToInterrupt(std::weak_ptr<Service::DSP::DSP_DSP> dsp) = 0;
|
||||
|
||||
/// Loads the DSP program
|
||||
virtual void LoadComponent(const std::vector<u8>& buffer) = 0;
|
||||
virtual void LoadComponent(std::span<const u8> buffer) = 0;
|
||||
|
||||
/// Unloads the DSP program
|
||||
virtual void UnloadComponent() = 0;
|
||||
|
@ -5,20 +5,24 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
struct ADTSData {
|
||||
u8 header_length;
|
||||
bool MPEG2;
|
||||
u8 profile;
|
||||
u8 channels;
|
||||
u8 channel_idx;
|
||||
u8 framecount;
|
||||
u8 samplerate_idx;
|
||||
u32 length;
|
||||
u32 samplerate;
|
||||
u8 header_length = 0;
|
||||
bool mpeg2 = false;
|
||||
u8 profile = 0;
|
||||
u8 channels = 0;
|
||||
u8 channel_idx = 0;
|
||||
u8 framecount = 0;
|
||||
u8 samplerate_idx = 0;
|
||||
u32 length = 0;
|
||||
u32 samplerate = 0;
|
||||
};
|
||||
|
||||
ADTSData ParseADTS(const char* buffer);
|
||||
ADTSData ParseADTS(const u8* buffer);
|
||||
|
||||
// last two bytes of MF AAC decoder user data
|
||||
// see https://docs.microsoft.com/en-us/windows/desktop/medfound/aac-decoder#example-media-types
|
||||
u16 MFGetAACTag(const ADTSData& input);
|
||||
|
||||
} // namespace AudioCore
|
||||
|
@ -3,44 +3,59 @@
|
||||
// Refer to the license.txt file included.
|
||||
#include <array>
|
||||
#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,
|
||||
16000, 12000, 11025, 8000, 7350, 0, 0, 0};
|
||||
constexpr std::array<u8, 8> channel_table = {0, 1, 2, 3, 4, 5, 6, 8};
|
||||
|
||||
ADTSData ParseADTS(const char* buffer) {
|
||||
u32 tmp = 0;
|
||||
ADTSData out;
|
||||
struct ADTSHeader {
|
||||
union {
|
||||
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
|
||||
tmp = (buffer[0] << 8) | (buffer[1] & 0xf0);
|
||||
if ((tmp & 0xffff) != 0xfff0) {
|
||||
out.length = 0;
|
||||
return out;
|
||||
if (header.sync_word != 0xfff) {
|
||||
return {};
|
||||
}
|
||||
|
||||
ADTSData out{};
|
||||
// bit 16 = no CRC
|
||||
out.header_length = (buffer[1] & 0x1) ? 7 : 9;
|
||||
out.MPEG2 = (buffer[1] >> 3) & 0x1;
|
||||
out.header_length = header.protection_absent ? 7 : 9;
|
||||
out.mpeg2 = static_cast<bool>(header.mpeg2);
|
||||
// bit 17 to 18
|
||||
out.profile = (buffer[2] >> 6) + 1;
|
||||
out.profile = static_cast<u8>(header.profile) + 1;
|
||||
// bit 19 to 22
|
||||
tmp = (buffer[2] >> 2) & 0xf;
|
||||
out.samplerate_idx = tmp;
|
||||
out.samplerate = (tmp > 15) ? 0 : freq_table[tmp];
|
||||
out.samplerate_idx = static_cast<u8>(header.samplerate_idx);
|
||||
out.samplerate = header.samplerate_idx > 15 ? 0 : freq_table[header.samplerate_idx];
|
||||
// bit 24 to 26
|
||||
tmp = ((buffer[2] & 0x1) << 2) | ((buffer[3] >> 6) & 0x3);
|
||||
out.channel_idx = tmp;
|
||||
out.channels = (tmp > 7) ? 0 : channel_table[tmp];
|
||||
|
||||
out.channel_idx = static_cast<u8>(header.channel_idx);
|
||||
out.channels = (header.channel_idx > 7) ? 0 : channel_table[header.channel_idx];
|
||||
// bit 55 to 56
|
||||
out.framecount = (buffer[6] & 0x3) + 1;
|
||||
|
||||
out.framecount = static_cast<u8>(header.frame_count + 1);
|
||||
// bit 31 to 43
|
||||
tmp = (buffer[3] & 0x3) << 11;
|
||||
tmp |= (buffer[4] << 3) & 0x7f8;
|
||||
tmp |= (buffer[5] >> 5) & 0x7;
|
||||
|
||||
out.length = tmp;
|
||||
out.length = static_cast<u32>(header.frame_length);
|
||||
|
||||
return out;
|
||||
}
|
||||
@ -61,3 +76,4 @@ u16 MFGetAACTag(const ADTSData& input) {
|
||||
|
||||
return tag;
|
||||
}
|
||||
} // namespace AudioCore
|
||||
|
@ -24,7 +24,7 @@ private:
|
||||
std::optional<BinaryMessage> Decode(const BinaryMessage& request);
|
||||
|
||||
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,
|
||||
AudioBufferList* io_data,
|
||||
@ -33,7 +33,7 @@ private:
|
||||
|
||||
Memory::MemorySystem& memory;
|
||||
|
||||
ADTSData adts_config;
|
||||
AudioCore::ADTSData adts_config;
|
||||
AudioStreamBasicDescription output_format = {};
|
||||
AudioConverterRef converter = nullptr;
|
||||
|
||||
@ -85,7 +85,11 @@ std::optional<BinaryMessage> AudioToolboxDecoder::Impl::ProcessRequest(
|
||||
case DecoderCommand::EncodeDecode: {
|
||||
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;
|
||||
response.header.result = ResultStatus::Success;
|
||||
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 (adts_config.channels == adts_header.channels &&
|
||||
adts_config.samplerate == adts_header.samplerate) {
|
||||
@ -179,8 +183,9 @@ std::optional<BinaryMessage> AudioToolboxDecoder::Impl::Decode(const BinaryMessa
|
||||
return {};
|
||||
}
|
||||
|
||||
auto data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR);
|
||||
auto adts_header = ParseADTS(reinterpret_cast<const char*>(data));
|
||||
const auto 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_len = request.decode_aac_request.size - adts_header.header_length;
|
||||
|
||||
|
@ -42,7 +42,9 @@ std::optional<BinaryMessage> NullDecoder::ProcessRequest(const BinaryMessage& re
|
||||
BinaryMessage response{};
|
||||
switch (request.header.cmd) {
|
||||
case DecoderCommand::Init:
|
||||
case DecoderCommand::Unknown:
|
||||
case DecoderCommand::Shutdown:
|
||||
case DecoderCommand::SaveState:
|
||||
case DecoderCommand::LoadState:
|
||||
response = request;
|
||||
response.header.result = ResultStatus::Success;
|
||||
return response;
|
||||
|
@ -14,9 +14,16 @@
|
||||
namespace AudioCore::HLE {
|
||||
|
||||
enum class DecoderCommand : u16 {
|
||||
/// Initializes the decoder.
|
||||
Init = 0,
|
||||
/// Decodes/encodes a data frame.
|
||||
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 {
|
||||
|
@ -111,7 +111,11 @@ std::optional<BinaryMessage> FDKDecoder::Impl::ProcessRequest(const BinaryMessag
|
||||
case DecoderCommand::EncodeDecode: {
|
||||
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;
|
||||
response.header.result = ResultStatus::Success;
|
||||
return response;
|
||||
|
@ -80,7 +80,11 @@ std::optional<BinaryMessage> FFMPEGDecoder::Impl::ProcessRequest(const BinaryMes
|
||||
case DecoderCommand::EncodeDecode: {
|
||||
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;
|
||||
response.header.result = ResultStatus::Success;
|
||||
return response;
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include <boost/serialization/weak_ptr.hpp>
|
||||
#include "audio_core/audio_types.h"
|
||||
#include "common/archives.h"
|
||||
#ifdef HAVE_MF
|
||||
#include "audio_core/hle/wmf_decoder.h"
|
||||
#elif HAVE_AUDIOTOOLBOX
|
||||
@ -66,7 +67,7 @@ public:
|
||||
bool RecvDataIsReady(u32 register_number) const;
|
||||
std::vector<u8> PipeRead(DspPipe pipe_number, std::size_t length);
|
||||
std::size_t GetPipeReadableSize(DspPipe pipe_number) const;
|
||||
void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer);
|
||||
void PipeWrite(DspPipe pipe_number, std::span<const u8> buffer);
|
||||
|
||||
std::array<u8, Memory::DSP_RAM_SIZE>& GetDspMemory();
|
||||
|
||||
@ -244,7 +245,7 @@ size_t DspHle::Impl::GetPipeReadableSize(DspPipe pipe_number) const {
|
||||
return pipe_data[pipe_index].size();
|
||||
}
|
||||
|
||||
void DspHle::Impl::PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
|
||||
void DspHle::Impl::PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) {
|
||||
switch (pipe_number) {
|
||||
case DspPipe::Audio: {
|
||||
if (buffer.size() != 4) {
|
||||
@ -319,6 +320,10 @@ void DspHle::Impl::PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer)
|
||||
pipe_data[static_cast<u32>(pipe_number)].resize(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;
|
||||
}
|
||||
default:
|
||||
@ -461,8 +466,6 @@ void DspHle::Impl::AudioTickCallback(s64 cycles_late) {
|
||||
// TODO(merry): Signal all the other interrupts as appropriate.
|
||||
if (auto service = dsp_dsp.lock()) {
|
||||
service->SignalInterrupt(InterruptType::Pipe, DspPipe::Audio);
|
||||
// HACK(merry): Added to prevent regressions. Will remove soon.
|
||||
service->SignalInterrupt(InterruptType::Pipe, DspPipe::Binary);
|
||||
}
|
||||
}
|
||||
|
||||
@ -494,7 +497,7 @@ size_t DspHle::GetPipeReadableSize(DspPipe pipe_number) const {
|
||||
return impl->GetPipeReadableSize(pipe_number);
|
||||
}
|
||||
|
||||
void DspHle::PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
|
||||
void DspHle::PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) {
|
||||
impl->PipeWrite(pipe_number, buffer);
|
||||
}
|
||||
|
||||
@ -506,7 +509,7 @@ void DspHle::SetServiceToInterrupt(std::weak_ptr<DSP_DSP> dsp) {
|
||||
impl->SetServiceToInterrupt(std::move(dsp));
|
||||
}
|
||||
|
||||
void DspHle::LoadComponent(const std::vector<u8>& component_data) {
|
||||
void DspHle::LoadComponent(std::span<const u8> component_data) {
|
||||
// HLE doesn't need DSP program. Only log some info here
|
||||
LOG_INFO(Service_DSP, "Firmware hash: {:#018x}",
|
||||
Common::ComputeHash64(component_data.data(), component_data.size()));
|
||||
|
@ -30,13 +30,13 @@ public:
|
||||
void SetSemaphore(u16 semaphore_value) override;
|
||||
std::vector<u8> PipeRead(DspPipe pipe_number, std::size_t length) override;
|
||||
std::size_t GetPipeReadableSize(DspPipe pipe_number) const override;
|
||||
void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) override;
|
||||
void PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) override;
|
||||
|
||||
std::array<u8, Memory::DSP_RAM_SIZE>& GetDspMemory() override;
|
||||
|
||||
void SetServiceToInterrupt(std::weak_ptr<Service::DSP::DSP_DSP> dsp) override;
|
||||
|
||||
void LoadComponent(const std::vector<u8>& buffer) override;
|
||||
void LoadComponent(std::span<const u8> buffer) override;
|
||||
void UnloadComponent() override;
|
||||
|
||||
private:
|
||||
|
@ -27,7 +27,7 @@ public:
|
||||
~Impl();
|
||||
std::optional<BinaryMessage> ProcessRequest(const BinaryMessage& request);
|
||||
|
||||
bool SetMediaType(const ADTSData& adts_data);
|
||||
bool SetMediaType(const AudioCore::ADTSData& adts_data);
|
||||
|
||||
private:
|
||||
std::optional<BinaryMessage> Initalize(const BinaryMessage& request);
|
||||
@ -36,8 +36,8 @@ private:
|
||||
Memory::MemorySystem& memory;
|
||||
std::unique_ptr<AMediaCodec, AMediaCodecRelease> decoder;
|
||||
// default: 2 channles, 48000 samplerate
|
||||
ADTSData mADTSData{
|
||||
/*header_length*/ 7, /*MPEG2*/ false, /*profile*/ 2,
|
||||
AudioCore::ADTSData mADTSData{
|
||||
/*header_length*/ 7, /*mpeg2*/ false, /*profile*/ 2,
|
||||
/*channels*/ 2, /*channel_idx*/ 2, /*framecount*/ 0,
|
||||
/*samplerate_idx*/ 3, /*length*/ 0, /*samplerate*/ 48000};
|
||||
};
|
||||
@ -54,7 +54,7 @@ std::optional<BinaryMessage> MediaNDKDecoder::Impl::Initalize(const BinaryMessag
|
||||
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";
|
||||
if (decoder && mADTSData.profile == adts_data.profile &&
|
||||
mADTSData.channel_idx == adts_data.channel_idx &&
|
||||
@ -110,7 +110,11 @@ std::optional<BinaryMessage> MediaNDKDecoder::Impl::ProcessRequest(const BinaryM
|
||||
case DecoderCommand::EncodeDecode: {
|
||||
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;
|
||||
response.header.result = ResultStatus::Success;
|
||||
return response;
|
||||
@ -137,8 +141,9 @@ std::optional<BinaryMessage> MediaNDKDecoder::Impl::Decode(const BinaryMessage&
|
||||
return response;
|
||||
}
|
||||
|
||||
u8* data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR);
|
||||
ADTSData adts_data = ParseADTS(reinterpret_cast<const char*>(data));
|
||||
const u8* data =
|
||||
memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR);
|
||||
ADTSData adts_data = AudioCore::ParseADTS(data);
|
||||
SetMediaType(adts_data);
|
||||
response.decode_aac_response.sample_rate = GetSampleRateEnum(adts_data.samplerate);
|
||||
response.decode_aac_response.num_channels = adts_data.channels;
|
||||
|
@ -66,7 +66,7 @@ public:
|
||||
|
||||
private:
|
||||
const std::size_t source_id;
|
||||
Memory::MemorySystem* memory_system;
|
||||
const Memory::MemorySystem* memory_system{};
|
||||
StereoFrame16 current_frame;
|
||||
|
||||
using Format = SourceConfiguration::Configuration::Format;
|
||||
|
@ -23,7 +23,8 @@ private:
|
||||
|
||||
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 format_selected = false;
|
||||
@ -115,7 +116,11 @@ std::optional<BinaryMessage> WMFDecoder::Impl::ProcessRequest(const BinaryMessag
|
||||
case DecoderCommand::EncodeDecode: {
|
||||
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;
|
||||
response.header.result = ResultStatus::Success;
|
||||
return response;
|
||||
@ -135,7 +140,7 @@ std::optional<BinaryMessage> WMFDecoder::Impl::Initalize(const BinaryMessage& re
|
||||
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::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);
|
||||
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;
|
||||
unique_mfptr<IMFSample> sample;
|
||||
MFInputState input_status = MFInputState::OK;
|
||||
MFOutputState output_status = MFOutputState::OK;
|
||||
std::optional<ADTSMeta> adts_meta =
|
||||
DetectMediaType((char*)data, request.decode_aac_request.size);
|
||||
std::optional<ADTSMeta> adts_meta = DetectMediaType(data, request.decode_aac_request.size);
|
||||
|
||||
if (!adts_meta) {
|
||||
LOG_ERROR(Audio_DSP, "Unable to deduce decoding parameters from ADTS stream");
|
||||
|
@ -110,8 +110,9 @@ unique_mfptr<IMFSample> CreateSample(const void* data, DWORD len, DWORD alignmen
|
||||
return sample;
|
||||
}
|
||||
|
||||
bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts,
|
||||
const UINT8* user_data, UINT32 user_data_len, GUID audio_format) {
|
||||
bool SelectInputMediaType(IMFTransform* transform, int in_stream_id,
|
||||
const AudioCore::ADTSData& adts, const UINT8* user_data,
|
||||
UINT32 user_data_len, GUID audio_format) {
|
||||
HRESULT hr = S_OK;
|
||||
unique_mfptr<IMFMediaType> t;
|
||||
|
||||
@ -190,12 +191,12 @@ bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audi
|
||||
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) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ADTSData tmp;
|
||||
AudioCore::ADTSData tmp;
|
||||
ADTSMeta result;
|
||||
// see https://docs.microsoft.com/en-us/windows/desktop/api/mmreg/ns-mmreg-heaacwaveinfo_tag
|
||||
// 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};
|
||||
uint16_t tag = 0;
|
||||
|
||||
tmp = ParseADTS(buffer);
|
||||
tmp = AudioCore::ParseADTS(buffer);
|
||||
if (tmp.length == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@ -215,7 +216,7 @@ std::optional<ADTSMeta> DetectMediaType(char* buffer, std::size_t len) {
|
||||
tag = MFGetAACTag(tmp);
|
||||
aac_tmp[12] |= (tag & 0xff00) >> 8;
|
||||
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);
|
||||
return result;
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ void ReportError(std::string msg, HRESULT hr);
|
||||
|
||||
// data type for transferring ADTS metadata between functions
|
||||
struct ADTSMeta {
|
||||
ADTSData ADTSHeader;
|
||||
AudioCore::ADTSData ADTSHeader;
|
||||
u8 AACTag[14];
|
||||
};
|
||||
|
||||
@ -110,10 +110,10 @@ bool InitMFDLL();
|
||||
unique_mfptr<IMFTransform> MFDecoderInit(GUID audio_format = MFAudioFormat_AAC);
|
||||
unique_mfptr<IMFSample> CreateSample(const void* data, DWORD len, DWORD alignment = 1,
|
||||
LONGLONG duration = 0);
|
||||
bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts,
|
||||
const UINT8* user_data, UINT32 user_data_len,
|
||||
GUID audio_format = MFAudioFormat_AAC);
|
||||
std::optional<ADTSMeta> DetectMediaType(char* buffer, std::size_t len);
|
||||
bool SelectInputMediaType(IMFTransform* transform, int in_stream_id,
|
||||
const AudioCore::ADTSData& adts, const UINT8* user_data,
|
||||
UINT32 user_data_len, GUID audio_format = MFAudioFormat_AAC);
|
||||
std::optional<ADTSMeta> DetectMediaType(const u8* buffer, std::size_t len);
|
||||
bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id,
|
||||
GUID audio_format = MFAudioFormat_PCM);
|
||||
void MFFlush(IMFTransform* transform);
|
||||
|
@ -26,7 +26,7 @@ enum class SegmentType : u8 {
|
||||
|
||||
class Dsp1 {
|
||||
public:
|
||||
explicit Dsp1(const std::vector<u8>& raw);
|
||||
explicit Dsp1(std::span<const u8> raw);
|
||||
|
||||
struct Header {
|
||||
std::array<u8, 0x100> signature;
|
||||
@ -65,7 +65,7 @@ public:
|
||||
bool recv_data_on_start;
|
||||
};
|
||||
|
||||
Dsp1::Dsp1(const std::vector<u8>& raw) {
|
||||
Dsp1::Dsp1(std::span<const u8> raw) {
|
||||
Header header;
|
||||
std::memcpy(&header, raw.data(), sizeof(header));
|
||||
recv_data_on_start = header.recv_data_on_start != 0;
|
||||
@ -220,7 +220,7 @@ struct DspLle::Impl final {
|
||||
}
|
||||
}
|
||||
|
||||
void WritePipe(u8 pipe_index, const std::vector<u8>& data) {
|
||||
void WritePipe(u8 pipe_index, std::span<const u8> data) {
|
||||
PipeStatus pipe_status = GetPipeStatus(pipe_index, PipeDirection::CPUtoDSP);
|
||||
bool need_update = false;
|
||||
const u8* buffer_ptr = data.data();
|
||||
@ -304,7 +304,7 @@ struct DspLle::Impl final {
|
||||
return size & PipeStatus::PtrMask;
|
||||
}
|
||||
|
||||
void LoadComponent(const std::vector<u8>& buffer) {
|
||||
void LoadComponent(std::span<const u8> buffer) {
|
||||
if (loaded) {
|
||||
LOG_ERROR(Audio_DSP, "Component already loaded!");
|
||||
return;
|
||||
@ -400,7 +400,7 @@ std::size_t DspLle::GetPipeReadableSize(DspPipe pipe_number) const {
|
||||
return impl->GetPipeReadableSize(static_cast<u8>(pipe_number));
|
||||
}
|
||||
|
||||
void DspLle::PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
|
||||
void DspLle::PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) {
|
||||
impl->WritePipe(static_cast<u8>(pipe_number), buffer);
|
||||
}
|
||||
|
||||
@ -476,7 +476,7 @@ void DspLle::SetRecvDataHandler(u8 index, std::function<void()> handler) {
|
||||
impl->teakra.SetRecvDataHandler(index, handler);
|
||||
}
|
||||
|
||||
void DspLle::LoadComponent(const std::vector<u8>& buffer) {
|
||||
void DspLle::LoadComponent(std::span<const u8> buffer) {
|
||||
impl->LoadComponent(buffer);
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include "audio_core/dsp_interface.h"
|
||||
|
||||
namespace Core {
|
||||
@ -22,7 +23,7 @@ public:
|
||||
void SetSemaphore(u16 semaphore_value) override;
|
||||
std::vector<u8> PipeRead(DspPipe pipe_number, std::size_t length) override;
|
||||
std::size_t GetPipeReadableSize(DspPipe pipe_number) const override;
|
||||
void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) override;
|
||||
void PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) override;
|
||||
|
||||
std::array<u8, Memory::DSP_RAM_SIZE>& GetDspMemory() override;
|
||||
|
||||
@ -31,7 +32,7 @@ public:
|
||||
void SetSemaphoreHandler(std::function<void()> handler);
|
||||
void SetRecvDataHandler(u8 index, std::function<void()> handler);
|
||||
|
||||
void LoadComponent(const std::vector<u8>& buffer) override;
|
||||
void LoadComponent(const std::span<const u8> buffer) override;
|
||||
void UnloadComponent() override;
|
||||
|
||||
private:
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include "common/detached_tasks.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/scope_exit.h"
|
||||
@ -28,18 +27,15 @@
|
||||
#include "core/core.h"
|
||||
#include "core/dumping/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/framebuffer_layout.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/cfg/cfg.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/movie.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "input_common/main.h"
|
||||
#include "network/network.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
#undef _UNICODE
|
||||
#include <getopt.h>
|
||||
@ -331,7 +327,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
auto& movie = Core::Movie::GetInstance();
|
||||
auto& movie = system.Movie();
|
||||
|
||||
if (!movie_record.empty()) {
|
||||
movie.PrepareForRecording();
|
||||
@ -346,7 +342,7 @@ int main(int argc, char** argv) {
|
||||
system.ApplySettings();
|
||||
|
||||
// Register frontend applets
|
||||
Frontend::RegisterDefaultApplets();
|
||||
Frontend::RegisterDefaultApplets(system);
|
||||
|
||||
EmuWindow_SDL2::InitializeSDL2();
|
||||
|
||||
@ -354,12 +350,12 @@ int main(int argc, char** argv) {
|
||||
bool is_secondary) -> std::unique_ptr<EmuWindow_SDL2> {
|
||||
switch (Settings::values.graphics_api.GetValue()) {
|
||||
case Settings::GraphicsAPI::OpenGL:
|
||||
return std::make_unique<EmuWindow_SDL2_GL>(fullscreen, is_secondary);
|
||||
return std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen, is_secondary);
|
||||
case Settings::GraphicsAPI::Software:
|
||||
return std::make_unique<EmuWindow_SDL2_SW>(system, fullscreen, is_secondary);
|
||||
}
|
||||
LOG_ERROR(Frontend, "Invalid Graphics API, using OpenGL");
|
||||
return std::make_unique<EmuWindow_SDL2_GL>(fullscreen, is_secondary);
|
||||
return std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen, is_secondary);
|
||||
};
|
||||
|
||||
const auto emu_window{create_emu_window(fullscreen, false)};
|
||||
|
@ -109,7 +109,8 @@ void EmuWindow_SDL2::Fullscreen() {
|
||||
SDL_MaximizeWindow(render_window);
|
||||
}
|
||||
|
||||
EmuWindow_SDL2::EmuWindow_SDL2(bool is_secondary) : EmuWindow(is_secondary) {}
|
||||
EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system_, bool is_secondary)
|
||||
: EmuWindow(is_secondary), system(system_) {}
|
||||
|
||||
EmuWindow_SDL2::~EmuWindow_SDL2() {
|
||||
SDL_Quit();
|
||||
@ -127,16 +128,54 @@ void EmuWindow_SDL2::InitializeSDL2() {
|
||||
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() {
|
||||
SDL_Event event;
|
||||
std::vector<SDL_Event> other_window_events;
|
||||
|
||||
// SDL_PollEvent returns 0 when there are no more events in the event queue
|
||||
while (SDL_PollEvent(&event)) {
|
||||
if (event.window.windowID != render_window_id) {
|
||||
if (GetEventWindowId(event) != render_window_id) {
|
||||
other_window_events.push_back(event);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (event.window.event) {
|
||||
@ -202,7 +241,7 @@ void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minima
|
||||
void EmuWindow_SDL2::UpdateFramerateCounter() {
|
||||
const u32 current_time = SDL_GetTicks();
|
||||
if (current_time > last_time + 2000) {
|
||||
const auto results = Core::System::GetInstance().GetAndResetPerfStats();
|
||||
const auto results = system.GetAndResetPerfStats();
|
||||
const auto title =
|
||||
fmt::format("Citra {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname,
|
||||
Common::g_scm_branch, Common::g_scm_desc, results.game_fps,
|
||||
|
@ -8,11 +8,16 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
|
||||
union SDL_Event;
|
||||
struct SDL_Window;
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
class EmuWindow_SDL2 : public Frontend::EmuWindow {
|
||||
public:
|
||||
explicit EmuWindow_SDL2(bool is_secondary);
|
||||
explicit EmuWindow_SDL2(Core::System& system_, bool is_secondary);
|
||||
~EmuWindow_SDL2();
|
||||
|
||||
/// Initializes SDL2
|
||||
@ -31,6 +36,9 @@ public:
|
||||
void RequestClose();
|
||||
|
||||
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.
|
||||
void OnKeyEvent(int key, u8 state);
|
||||
|
||||
@ -78,4 +86,6 @@ protected:
|
||||
|
||||
/// Keeps track of how often to update the title bar during gameplay
|
||||
u32 last_time = 0;
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
@ -42,8 +42,8 @@ private:
|
||||
SDL_GLContext context;
|
||||
};
|
||||
|
||||
EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen, bool is_secondary)
|
||||
: EmuWindow_SDL2{is_secondary} {
|
||||
EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system_, bool fullscreen, bool is_secondary)
|
||||
: EmuWindow_SDL2{system_, is_secondary} {
|
||||
// Initialize the window
|
||||
if (Settings::values.use_gles) {
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
|
@ -9,9 +9,13 @@
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
class EmuWindow_SDL2_GL : public EmuWindow_SDL2 {
|
||||
public:
|
||||
explicit EmuWindow_SDL2_GL(bool fullscreen, bool is_secondary);
|
||||
explicit EmuWindow_SDL2_GL(Core::System& system_, bool fullscreen, bool is_secondary);
|
||||
~EmuWindow_SDL2_GL();
|
||||
|
||||
void Present() override;
|
||||
|
@ -18,7 +18,7 @@
|
||||
class DummyContext : public Frontend::GraphicsContext {};
|
||||
|
||||
EmuWindow_SDL2_SW::EmuWindow_SDL2_SW(Core::System& system_, bool fullscreen, bool is_secondary)
|
||||
: EmuWindow_SDL2{is_secondary}, system{system_} {
|
||||
: EmuWindow_SDL2{system_, is_secondary}, system{system_} {
|
||||
std::string window_title = fmt::format("Citra {} | {}-{}", Common::g_build_fullname,
|
||||
Common::g_scm_branch, Common::g_scm_desc);
|
||||
render_window =
|
||||
|
@ -341,10 +341,6 @@ if (USE_DISCORD_PRESENCE)
|
||||
target_compile_definitions(citra-qt PRIVATE -DUSE_DISCORD_PRESENCE)
|
||||
endif()
|
||||
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
target_compile_definitions(citra-qt PRIVATE -DENABLE_WEB_SERVICE)
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
|
||||
endif()
|
||||
|
@ -67,6 +67,5 @@ void QtMiiSelector::OpenDialog() {
|
||||
dialog.return_code, index);
|
||||
|
||||
const auto mii_data = dialog.miis.at(index);
|
||||
Finalize(dialog.return_code,
|
||||
dialog.return_code == 0 ? std::move(mii_data) : HLE::Applets::MiiData{});
|
||||
Finalize(dialog.return_code, dialog.return_code == 0 ? std::move(mii_data) : Mii::MiiData{});
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ private:
|
||||
QVBoxLayout* layout;
|
||||
QtMiiSelector* mii_selector;
|
||||
u32 return_code = 0;
|
||||
std::vector<HLE::Applets::MiiData> miis;
|
||||
std::vector<Mii::MiiData> miis;
|
||||
|
||||
friend class QtMiiSelector;
|
||||
};
|
||||
|
@ -44,7 +44,8 @@
|
||||
|
||||
static Frontend::WindowSystemType GetWindowSystemType();
|
||||
|
||||
EmuThread::EmuThread(Frontend::GraphicsContext& core_context) : core_context(core_context) {}
|
||||
EmuThread::EmuThread(Core::System& system_, Frontend::GraphicsContext& core_context)
|
||||
: system{system_}, core_context(core_context) {}
|
||||
|
||||
EmuThread::~EmuThread() = default;
|
||||
|
||||
@ -62,7 +63,6 @@ static GMainWindow* GetMainWindow() {
|
||||
void EmuThread::run() {
|
||||
MicroProfileOnThreadCreate("EmuThread");
|
||||
const auto scope = core_context.Acquire();
|
||||
Core::System& system = Core::System::GetInstance();
|
||||
|
||||
if (Settings::values.preload_textures) {
|
||||
emit LoadProgress(VideoCore::LoadCallbackStage::Preload, 0, 0);
|
||||
@ -107,7 +107,7 @@ void EmuThread::run() {
|
||||
}
|
||||
if (result != Core::System::ResultStatus::Success) {
|
||||
this->SetRunning(false);
|
||||
emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails());
|
||||
emit ErrorThrown(result, system.GetStatusDetails());
|
||||
}
|
||||
|
||||
was_active = running || exec_step;
|
||||
@ -248,8 +248,8 @@ public:
|
||||
#ifdef HAS_OPENGL
|
||||
class OpenGLRenderWidget : public RenderWidget {
|
||||
public:
|
||||
explicit OpenGLRenderWidget(GRenderWindow* parent, bool is_secondary)
|
||||
: RenderWidget(parent), is_secondary(is_secondary) {
|
||||
explicit OpenGLRenderWidget(GRenderWindow* parent, Core::System& system_, bool is_secondary)
|
||||
: RenderWidget(parent), system(system_), is_secondary(is_secondary) {
|
||||
setAttribute(Qt::WA_NativeWindow);
|
||||
setAttribute(Qt::WA_PaintOnScreen);
|
||||
if (GetWindowSystemType() == Frontend::WindowSystemType::Wayland) {
|
||||
@ -266,7 +266,7 @@ public:
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
if (!Core::System::GetInstance().IsPoweredOn()) {
|
||||
if (!system.IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
context->MakeCurrent();
|
||||
@ -284,6 +284,7 @@ public:
|
||||
|
||||
private:
|
||||
std::unique_ptr<Frontend::GraphicsContext> context{};
|
||||
Core::System& system;
|
||||
bool is_secondary;
|
||||
};
|
||||
#endif
|
||||
@ -296,7 +297,7 @@ struct SoftwareRenderWidget : public RenderWidget {
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
if (!Core::System::GetInstance().IsPoweredOn()) {
|
||||
if (!system.IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -386,10 +387,6 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread_, Core::Sys
|
||||
bool is_secondary_)
|
||||
: 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);
|
||||
auto layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
@ -666,7 +663,7 @@ bool GRenderWindow::InitializeOpenGL() {
|
||||
|
||||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
||||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
||||
auto child = new OpenGLRenderWidget(this, is_secondary);
|
||||
auto child = new OpenGLRenderWidget(this, system, is_secondary);
|
||||
child_widget = child;
|
||||
child_widget->windowHandle()->create();
|
||||
|
||||
|
@ -18,6 +18,10 @@ class QTouchEvent;
|
||||
|
||||
class GRenderWindow;
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace VideoCore {
|
||||
enum class LoadCallbackStage;
|
||||
}
|
||||
@ -26,7 +30,7 @@ class EmuThread final : public QThread {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EmuThread(Frontend::GraphicsContext& context);
|
||||
explicit EmuThread(Core::System& system_, Frontend::GraphicsContext& context);
|
||||
~EmuThread() override;
|
||||
|
||||
/**
|
||||
@ -80,6 +84,7 @@ private:
|
||||
std::mutex running_mutex;
|
||||
std::condition_variable running_cv;
|
||||
|
||||
Core::System& system;
|
||||
Frontend::GraphicsContext& core_context;
|
||||
|
||||
signals:
|
||||
|
@ -9,11 +9,12 @@
|
||||
#include "citra_qt/compatdb.h"
|
||||
#include "common/telemetry.h"
|
||||
#include "core/core.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "ui_compatdb.h"
|
||||
|
||||
CompatDB::CompatDB(QWidget* parent)
|
||||
CompatDB::CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent)
|
||||
: QWizard(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
|
||||
ui{std::make_unique<Ui::CompatDB>()} {
|
||||
ui{std::make_unique<Ui::CompatDB>()}, telemetry_session{telemetry_session_} {
|
||||
ui->setupUi(this);
|
||||
connect(ui->radioButton_Perfect, &QRadioButton::clicked, this, &CompatDB::EnableNext);
|
||||
connect(ui->radioButton_Great, &QRadioButton::clicked, this, &CompatDB::EnableNext);
|
||||
@ -51,16 +52,15 @@ void CompatDB::Submit() {
|
||||
case CompatDBPage::Final:
|
||||
back();
|
||||
LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId());
|
||||
Core::System::GetInstance().TelemetrySession().AddField(
|
||||
Common::Telemetry::FieldType::UserFeedback, "Compatibility",
|
||||
compatibility->checkedId());
|
||||
telemetry_session.AddField(Common::Telemetry::FieldType::UserFeedback, "Compatibility",
|
||||
compatibility->checkedId());
|
||||
|
||||
button(NextButton)->setEnabled(false);
|
||||
button(NextButton)->setText(tr("Submitting"));
|
||||
button(CancelButton)->setVisible(false);
|
||||
|
||||
testcase_watcher.setFuture(QtConcurrent::run(
|
||||
[] { return Core::System::GetInstance().TelemetrySession().SubmitTestcase(); }));
|
||||
testcase_watcher.setFuture(
|
||||
QtConcurrent::run([this] { return telemetry_session.SubmitTestcase(); }));
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Frontend, "Unexpected page: {}", currentId());
|
||||
|
@ -8,6 +8,10 @@
|
||||
#include <QFutureWatcher>
|
||||
#include <QWizard>
|
||||
|
||||
namespace Core {
|
||||
class TelemetrySession;
|
||||
}
|
||||
|
||||
namespace Ui {
|
||||
class CompatDB;
|
||||
}
|
||||
@ -16,7 +20,7 @@ class CompatDB : public QWizard {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CompatDB(QWidget* parent = nullptr);
|
||||
explicit CompatDB(Core::TelemetrySession& telemetry_session_, QWidget* parent = nullptr);
|
||||
~CompatDB();
|
||||
|
||||
private:
|
||||
@ -27,4 +31,6 @@ private:
|
||||
void Submit();
|
||||
void OnTestcaseSubmitted();
|
||||
void EnableNext();
|
||||
|
||||
Core::TelemetrySession& telemetry_session;
|
||||
};
|
||||
|
@ -29,7 +29,7 @@ Config::~Config() {
|
||||
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_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{{
|
||||
@ -772,6 +772,11 @@ void Config::ReadUIGameListValues() {
|
||||
ReadBasicSetting(UISettings::values.game_list_hide_no_icon);
|
||||
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();
|
||||
}
|
||||
|
||||
@ -1230,6 +1235,11 @@ void Config::SaveUIGameListValues() {
|
||||
WriteBasicSetting(UISettings::values.game_list_hide_no_icon);
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -21,68 +21,8 @@
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>-1</number>
|
||||
</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>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -10,14 +10,13 @@
|
||||
#include "citra_qt/configuration/configuration_shared.h"
|
||||
#include "citra_qt/configuration/configure_audio.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "ui_configure_audio.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include "common/apple_authorization.h"
|
||||
#endif
|
||||
|
||||
ConfigureAudio::ConfigureAudio(QWidget* parent)
|
||||
ConfigureAudio::ConfigureAudio(bool is_powered_on, QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureAudio>()) {
|
||||
ui->setupUi(this);
|
||||
|
||||
@ -27,8 +26,7 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
|
||||
AudioCore::GetSinkName(static_cast<AudioCore::SinkType>(type)).data()));
|
||||
}
|
||||
|
||||
const bool is_running = Core::System::GetInstance().IsPoweredOn();
|
||||
ui->emulation_combo_box->setEnabled(!is_running);
|
||||
ui->emulation_combo_box->setEnabled(!is_powered_on);
|
||||
|
||||
connect(ui->volume_slider, &QSlider::valueChanged, this,
|
||||
&ConfigureAudio::SetVolumeIndicatorText);
|
||||
|
@ -19,7 +19,7 @@ class ConfigureAudio : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureAudio(QWidget* parent = nullptr);
|
||||
explicit ConfigureAudio(bool is_powered_on, QWidget* parent = nullptr);
|
||||
~ConfigureAudio() override;
|
||||
|
||||
void ApplyConfiguration();
|
||||
|
@ -47,8 +47,7 @@ ConfigureCamera::~ConfigureCamera() {
|
||||
}
|
||||
|
||||
void ConfigureCamera::ConnectEvents() {
|
||||
connect(ui->image_source,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
connect(ui->image_source, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this](int index) {
|
||||
StopPreviewing();
|
||||
UpdateImageSourceUI();
|
||||
@ -58,36 +57,33 @@ void ConfigureCamera::ConnectEvents() {
|
||||
}
|
||||
#endif
|
||||
});
|
||||
connect(ui->camera_selection,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this] {
|
||||
StopPreviewing();
|
||||
if (GetCameraSelection() != current_selected) {
|
||||
RecordConfig();
|
||||
}
|
||||
if (ui->camera_selection->currentIndex() == 1) {
|
||||
ui->camera_mode->setCurrentIndex(1); // Double
|
||||
if (camera_name[0] == camera_name[2] && camera_config[0] == camera_config[2]) {
|
||||
ui->camera_mode->setCurrentIndex(0); // Single
|
||||
}
|
||||
}
|
||||
UpdateCameraMode();
|
||||
SetConfiguration();
|
||||
});
|
||||
connect(ui->camera_mode, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, [this] {
|
||||
StopPreviewing();
|
||||
ui->camera_position_label->setVisible(ui->camera_mode->currentIndex() == 1);
|
||||
ui->camera_position->setVisible(ui->camera_mode->currentIndex() == 1);
|
||||
current_selected = GetCameraSelection();
|
||||
});
|
||||
connect(ui->camera_position,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this] {
|
||||
StopPreviewing();
|
||||
if (GetCameraSelection() != current_selected) {
|
||||
RecordConfig();
|
||||
}
|
||||
SetConfiguration();
|
||||
});
|
||||
connect(ui->camera_selection, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
|
||||
StopPreviewing();
|
||||
if (GetCameraSelection() != current_selected) {
|
||||
RecordConfig();
|
||||
}
|
||||
if (ui->camera_selection->currentIndex() == 1) {
|
||||
ui->camera_mode->setCurrentIndex(1); // Double
|
||||
if (camera_name[0] == camera_name[2] && camera_config[0] == camera_config[2]) {
|
||||
ui->camera_mode->setCurrentIndex(0); // Single
|
||||
}
|
||||
}
|
||||
UpdateCameraMode();
|
||||
SetConfiguration();
|
||||
});
|
||||
connect(ui->camera_mode, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
|
||||
StopPreviewing();
|
||||
ui->camera_position_label->setVisible(ui->camera_mode->currentIndex() == 1);
|
||||
ui->camera_position->setVisible(ui->camera_mode->currentIndex() == 1);
|
||||
current_selected = GetCameraSelection();
|
||||
});
|
||||
connect(ui->camera_position, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
|
||||
StopPreviewing();
|
||||
if (GetCameraSelection() != current_selected) {
|
||||
RecordConfig();
|
||||
}
|
||||
SetConfiguration();
|
||||
});
|
||||
connect(ui->toolButton, &QToolButton::clicked, this, &ConfigureCamera::OnToolButtonClicked);
|
||||
connect(ui->preview_button, &QPushButton::clicked, this, [this] { StartPreviewing(); });
|
||||
connect(ui->prompt_before_load, &QCheckBox::stateChanged, this, [this](int state) {
|
||||
|
@ -9,11 +9,9 @@
|
||||
#include "core/cheats/cheat_base.h"
|
||||
#include "core/cheats/cheats.h"
|
||||
#include "core/cheats/gateway_cheat.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/process.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_} {
|
||||
// Setup gui control settings
|
||||
ui->setupUi(this);
|
||||
@ -36,7 +34,7 @@ ConfigureCheats::ConfigureCheats(u64 title_id_, QWidget* parent)
|
||||
[this] { SaveCheat(ui->tableCheats->currentRow()); });
|
||||
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();
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QWidget>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Cheats {
|
||||
@ -12,6 +13,10 @@ class CheatBase;
|
||||
class CheatEngine;
|
||||
} // namespace Cheats
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Ui {
|
||||
class ConfigureCheats;
|
||||
} // namespace Ui
|
||||
@ -20,7 +25,7 @@ class ConfigureCheats : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureCheats(u64 title_id_, QWidget* parent = nullptr);
|
||||
explicit ConfigureCheats(Core::System& system, u64 title_id, QWidget* parent = nullptr);
|
||||
~ConfigureCheats();
|
||||
bool ApplyConfiguration();
|
||||
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "ui_configure_debug.h"
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
ConfigureDebug::ConfigureDebug(QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureDebug>()) {
|
||||
ConfigureDebug::ConfigureDebug(bool is_powered_on_, QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureDebug>()), is_powered_on{is_powered_on_} {
|
||||
ui->setupUi(this);
|
||||
SetConfiguration();
|
||||
|
||||
@ -35,7 +34,6 @@ ConfigureDebug::ConfigureDebug(QWidget* parent)
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
});
|
||||
|
||||
const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
|
||||
ui->toggle_cpu_jit->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->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub.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->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue()));
|
||||
ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue());
|
||||
|
@ -15,7 +15,7 @@ class ConfigureDebug : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureDebug(QWidget* parent = nullptr);
|
||||
explicit ConfigureDebug(bool is_powered_on, QWidget* parent = nullptr);
|
||||
~ConfigureDebug() override;
|
||||
|
||||
void ApplyConfiguration();
|
||||
@ -25,4 +25,5 @@ public:
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureDebug> ui;
|
||||
bool is_powered_on;
|
||||
};
|
||||
|
@ -4,8 +4,19 @@
|
||||
|
||||
#include <map>
|
||||
#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_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 "common/settings.h"
|
||||
#include "core/core.h"
|
||||
@ -14,16 +25,41 @@
|
||||
ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Core::System& system_,
|
||||
bool enable_web_config)
|
||||
: 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);
|
||||
|
||||
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();
|
||||
|
||||
connect(ui->uiTab, &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged);
|
||||
connect(ui_tab.get(), &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged);
|
||||
connect(ui->selectorList, &QListWidget::itemSelectionChanged, this,
|
||||
&ConfigureDialog::UpdateVisibleTabs);
|
||||
|
||||
@ -31,46 +67,46 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Cor
|
||||
ui->selectorList->setCurrentRow(0);
|
||||
|
||||
// Set up used key list synchronisation
|
||||
connect(ui->inputTab, &ConfigureInput::InputKeysChanged, ui->hotkeysTab,
|
||||
connect(input_tab.get(), &ConfigureInput::InputKeysChanged, hotkeys_tab.get(),
|
||||
&ConfigureHotkeys::OnInputKeysChanged);
|
||||
connect(ui->hotkeysTab, &ConfigureHotkeys::HotkeysChanged, ui->inputTab,
|
||||
connect(hotkeys_tab.get(), &ConfigureHotkeys::HotkeysChanged, input_tab.get(),
|
||||
&ConfigureInput::OnHotkeysChanged);
|
||||
|
||||
// Synchronise lists upon initialisation
|
||||
ui->inputTab->EmitInputKeysChanged();
|
||||
ui->hotkeysTab->EmitHotkeysChanged();
|
||||
input_tab->EmitInputKeysChanged();
|
||||
hotkeys_tab->EmitHotkeysChanged();
|
||||
}
|
||||
|
||||
ConfigureDialog::~ConfigureDialog() = default;
|
||||
|
||||
void ConfigureDialog::SetConfiguration() {
|
||||
ui->generalTab->SetConfiguration();
|
||||
ui->systemTab->SetConfiguration();
|
||||
ui->inputTab->LoadConfiguration();
|
||||
ui->graphicsTab->SetConfiguration();
|
||||
ui->enhancementsTab->SetConfiguration();
|
||||
ui->audioTab->SetConfiguration();
|
||||
ui->cameraTab->SetConfiguration();
|
||||
ui->debugTab->SetConfiguration();
|
||||
ui->webTab->SetConfiguration();
|
||||
ui->uiTab->SetConfiguration();
|
||||
ui->storageTab->SetConfiguration();
|
||||
general_tab->SetConfiguration();
|
||||
system_tab->SetConfiguration();
|
||||
input_tab->LoadConfiguration();
|
||||
graphics_tab->SetConfiguration();
|
||||
enhancements_tab->SetConfiguration();
|
||||
audio_tab->SetConfiguration();
|
||||
camera_tab->SetConfiguration();
|
||||
debug_tab->SetConfiguration();
|
||||
web_tab->SetConfiguration();
|
||||
ui_tab->SetConfiguration();
|
||||
storage_tab->SetConfiguration();
|
||||
}
|
||||
|
||||
void ConfigureDialog::ApplyConfiguration() {
|
||||
ui->generalTab->ApplyConfiguration();
|
||||
ui->systemTab->ApplyConfiguration();
|
||||
ui->inputTab->ApplyConfiguration();
|
||||
ui->inputTab->ApplyProfile();
|
||||
ui->hotkeysTab->ApplyConfiguration(registry);
|
||||
ui->graphicsTab->ApplyConfiguration();
|
||||
ui->enhancementsTab->ApplyConfiguration();
|
||||
ui->audioTab->ApplyConfiguration();
|
||||
ui->cameraTab->ApplyConfiguration();
|
||||
ui->debugTab->ApplyConfiguration();
|
||||
ui->webTab->ApplyConfiguration();
|
||||
ui->uiTab->ApplyConfiguration();
|
||||
ui->storageTab->ApplyConfiguration();
|
||||
general_tab->ApplyConfiguration();
|
||||
system_tab->ApplyConfiguration();
|
||||
input_tab->ApplyConfiguration();
|
||||
input_tab->ApplyProfile();
|
||||
hotkeys_tab->ApplyConfiguration(registry);
|
||||
graphics_tab->ApplyConfiguration();
|
||||
enhancements_tab->ApplyConfiguration();
|
||||
audio_tab->ApplyConfiguration();
|
||||
camera_tab->ApplyConfiguration();
|
||||
debug_tab->ApplyConfiguration();
|
||||
web_tab->ApplyConfiguration();
|
||||
ui_tab->ApplyConfiguration();
|
||||
storage_tab->ApplyConfiguration();
|
||||
system.ApplySettings();
|
||||
Settings::LogSettings();
|
||||
}
|
||||
@ -81,11 +117,11 @@ void ConfigureDialog::PopulateSelectionList() {
|
||||
ui->selectorList->clear();
|
||||
|
||||
const std::array<std::pair<QString, QList<QWidget*>>, 5> items{
|
||||
{{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}},
|
||||
{tr("System"), {ui->systemTab, ui->cameraTab, ui->storageTab}},
|
||||
{tr("Graphics"), {ui->enhancementsTab, ui->graphicsTab}},
|
||||
{tr("Audio"), {ui->audioTab}},
|
||||
{tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}};
|
||||
{{tr("General"), {general_tab.get(), web_tab.get(), debug_tab.get(), ui_tab.get()}},
|
||||
{tr("System"), {system_tab.get(), camera_tab.get(), storage_tab.get()}},
|
||||
{tr("Graphics"), {enhancements_tab.get(), graphics_tab.get()}},
|
||||
{tr("Audio"), {audio_tab.get()}},
|
||||
{tr("Controls"), {input_tab.get(), hotkeys_tab.get()}}}};
|
||||
|
||||
for (const auto& entry : items) {
|
||||
auto* const item = new QListWidgetItem(entry.first);
|
||||
@ -112,18 +148,18 @@ void ConfigureDialog::RetranslateUI() {
|
||||
ui->selectorList->setCurrentRow(old_row);
|
||||
ui->tabWidget->setCurrentIndex(old_index);
|
||||
|
||||
ui->generalTab->RetranslateUI();
|
||||
ui->systemTab->RetranslateUI();
|
||||
ui->inputTab->RetranslateUI();
|
||||
ui->hotkeysTab->RetranslateUI();
|
||||
ui->graphicsTab->RetranslateUI();
|
||||
ui->enhancementsTab->RetranslateUI();
|
||||
ui->audioTab->RetranslateUI();
|
||||
ui->cameraTab->RetranslateUI();
|
||||
ui->debugTab->RetranslateUI();
|
||||
ui->webTab->RetranslateUI();
|
||||
ui->uiTab->RetranslateUI();
|
||||
ui->storageTab->RetranslateUI();
|
||||
general_tab->RetranslateUI();
|
||||
system_tab->RetranslateUI();
|
||||
input_tab->RetranslateUI();
|
||||
hotkeys_tab->RetranslateUI();
|
||||
graphics_tab->RetranslateUI();
|
||||
enhancements_tab->RetranslateUI();
|
||||
audio_tab->RetranslateUI();
|
||||
camera_tab->RetranslateUI();
|
||||
debug_tab->RetranslateUI();
|
||||
web_tab->RetranslateUI();
|
||||
ui_tab->RetranslateUI();
|
||||
storage_tab->RetranslateUI();
|
||||
}
|
||||
|
||||
void ConfigureDialog::UpdateVisibleTabs() {
|
||||
@ -131,18 +167,18 @@ void ConfigureDialog::UpdateVisibleTabs() {
|
||||
if (items.isEmpty())
|
||||
return;
|
||||
|
||||
const std::map<QWidget*, QString> widgets = {{ui->generalTab, tr("General")},
|
||||
{ui->systemTab, tr("System")},
|
||||
{ui->inputTab, tr("Input")},
|
||||
{ui->hotkeysTab, tr("Hotkeys")},
|
||||
{ui->enhancementsTab, tr("Enhancements")},
|
||||
{ui->graphicsTab, tr("Advanced")},
|
||||
{ui->audioTab, tr("Audio")},
|
||||
{ui->cameraTab, tr("Camera")},
|
||||
{ui->debugTab, tr("Debug")},
|
||||
{ui->storageTab, tr("Storage")},
|
||||
{ui->webTab, tr("Web")},
|
||||
{ui->uiTab, tr("UI")}};
|
||||
const std::map<QWidget*, QString> widgets = {{general_tab.get(), tr("General")},
|
||||
{system_tab.get(), tr("System")},
|
||||
{input_tab.get(), tr("Input")},
|
||||
{hotkeys_tab.get(), tr("Hotkeys")},
|
||||
{enhancements_tab.get(), tr("Enhancements")},
|
||||
{graphics_tab.get(), tr("Advanced")},
|
||||
{audio_tab.get(), tr("Audio")},
|
||||
{camera_tab.get(), tr("Camera")},
|
||||
{debug_tab.get(), tr("Debug")},
|
||||
{storage_tab.get(), tr("Storage")},
|
||||
{web_tab.get(), tr("Web")},
|
||||
{ui_tab.get(), tr("UI")}};
|
||||
|
||||
ui->tabWidget->clear();
|
||||
|
||||
|
@ -17,6 +17,19 @@ namespace Core {
|
||||
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 {
|
||||
Q_OBJECT
|
||||
|
||||
@ -42,4 +55,18 @@ private:
|
||||
std::unique_ptr<Ui::ConfigureDialog> ui;
|
||||
HotkeyRegistry& registry;
|
||||
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;
|
||||
};
|
||||
|
@ -22,8 +22,7 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent)
|
||||
const bool res_scale_enabled = graphics_api != Settings::GraphicsAPI::Software;
|
||||
ui->resolution_factor_combobox->setEnabled(res_scale_enabled);
|
||||
|
||||
connect(ui->render_3d_combobox,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
connect(ui->render_3d_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this](int currentIndex) {
|
||||
updateShaders(static_cast<Settings::StereoRenderOption>(currentIndex));
|
||||
});
|
||||
|
@ -6,14 +6,14 @@
|
||||
#include "citra_qt/configuration/configuration_shared.h"
|
||||
#include "citra_qt/configuration/configure_graphics.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.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>()) {
|
||||
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
|
||||
ui->graphics_api_combo->setCurrentIndex(-1);
|
||||
|
||||
@ -29,9 +29,10 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
});
|
||||
|
||||
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();
|
||||
ui->hw_shader_group->setEnabled(checked);
|
||||
ui->toggle_disk_shader_cache->setEnabled(checked);
|
||||
ui->hw_shader_group->setEnabled(checked && enabled);
|
||||
ui->toggle_disk_shader_cache->setEnabled(checked && enabled);
|
||||
});
|
||||
|
||||
SetupPerGameUI();
|
||||
|
@ -19,7 +19,7 @@ class ConfigureGraphics : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureGraphics(QWidget* parent = nullptr);
|
||||
explicit ConfigureGraphics(bool is_powered_on, QWidget* parent = nullptr);
|
||||
~ConfigureGraphics() override;
|
||||
|
||||
void ApplyConfiguration();
|
||||
|
@ -19,6 +19,9 @@
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Graphics</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="apiBox">
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "citra_qt/configuration/configure_hotkeys.h"
|
||||
#include "citra_qt/hotkeys.h"
|
||||
#include "citra_qt/util/sequence_dialog/sequence_dialog.h"
|
||||
#include "common/settings.h"
|
||||
#include "ui_configure_hotkeys.h"
|
||||
|
||||
constexpr int name_column = 0;
|
||||
@ -189,9 +188,9 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) {
|
||||
QAction* clear = context_menu.addAction(tr("Clear"));
|
||||
|
||||
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); });
|
||||
connect(clear, &QAction::triggered,
|
||||
connect(clear, &QAction::triggered, this,
|
||||
[this, hotkey_index] { model->setData(hotkey_index, QString{}); });
|
||||
|
||||
context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location));
|
||||
|
@ -162,7 +162,7 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
ui->buttonDpadUp, ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight,
|
||||
ui->buttonL, ui->buttonR, ui->buttonStart, ui->buttonSelect,
|
||||
ui->buttonDebug, ui->buttonGpio14, ui->buttonZL, ui->buttonZR,
|
||||
ui->buttonHome,
|
||||
ui->buttonHome, ui->buttonPower,
|
||||
};
|
||||
|
||||
analog_map_buttons = {{
|
||||
|
@ -305,6 +305,24 @@
|
||||
</layout>
|
||||
</item>
|
||||
<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">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_36">
|
||||
@ -340,7 +358,7 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="3" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_32">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_40">
|
||||
|
@ -27,16 +27,18 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString
|
||||
Core::System& system_)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()),
|
||||
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);
|
||||
|
||||
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);
|
||||
enhancements_tab = std::make_unique<ConfigureEnhancements>(this);
|
||||
graphics_tab = std::make_unique<ConfigureGraphics>(this);
|
||||
system_tab = std::make_unique<ConfigureSystem>(this);
|
||||
debug_tab = std::make_unique<ConfigureDebug>(this);
|
||||
cheat_tab = std::make_unique<ConfigureCheats>(title_id, this);
|
||||
graphics_tab = std::make_unique<ConfigureGraphics>(is_powered_on, this);
|
||||
system_tab = std::make_unique<ConfigureSystem>(system, this);
|
||||
debug_tab = std::make_unique<ConfigureDebug>(is_powered_on, this);
|
||||
cheat_tab = std::make_unique<ConfigureCheats>(system, title_id, this);
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
@ -129,7 +131,7 @@ void ConfigurePerGame::HandleApplyButtonClicked() {
|
||||
|
||||
static QPixmap GetQPixmapFromSMDH(std::vector<u8>& smdh_data) {
|
||||
Loader::SMDH smdh;
|
||||
memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
||||
std::memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
||||
|
||||
bool large = true;
|
||||
std::vector<u16> icon_data = smdh.GetIcon(large);
|
||||
|
@ -6,12 +6,12 @@
|
||||
#include <QFileDialog>
|
||||
#include <QUrl>
|
||||
#include "citra_qt/configuration/configure_storage.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "ui_configure_storage.h"
|
||||
|
||||
ConfigureStorage::ConfigureStorage(QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureStorage>()) {
|
||||
ConfigureStorage::ConfigureStorage(bool is_powered_on_, QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureStorage>()), is_powered_on{is_powered_on_} {
|
||||
ui->setupUi(this);
|
||||
SetConfiguration();
|
||||
|
||||
@ -74,7 +74,7 @@ void ConfigureStorage::SetConfiguration() {
|
||||
ui->toggle_virtual_sd->setChecked(Settings::values.use_virtual_sd.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() {
|
||||
|
@ -15,7 +15,7 @@ class ConfigureStorage : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureStorage(QWidget* parent = nullptr);
|
||||
explicit ConfigureStorage(bool is_powered_on, QWidget* parent = nullptr);
|
||||
~ConfigureStorage() override;
|
||||
|
||||
void ApplyConfiguration();
|
||||
@ -23,4 +23,5 @@ public:
|
||||
void SetConfiguration();
|
||||
|
||||
std::unique_ptr<Ui::ConfigureStorage> ui;
|
||||
bool is_powered_on;
|
||||
};
|
||||
|
@ -223,14 +223,12 @@ static const std::array<const char*, 187> country_names = {
|
||||
QT_TRANSLATE_NOOP("ConfigureSystem", "Bermuda"), // 180-186
|
||||
};
|
||||
|
||||
ConfigureSystem::ConfigureSystem(QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureSystem>()) {
|
||||
ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureSystem>()), system{system_} {
|
||||
ui->setupUi(this);
|
||||
connect(ui->combo_birthmonth,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
connect(ui->combo_birthmonth, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureSystem::UpdateBirthdayComboBox);
|
||||
connect(ui->combo_init_clock,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
connect(ui->combo_init_clock, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureSystem::UpdateInitTime);
|
||||
connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
|
||||
&ConfigureSystem::RefreshConsoleID);
|
||||
@ -280,7 +278,7 @@ ConfigureSystem::ConfigureSystem(QWidget* parent)
|
||||
ConfigureSystem::~ConfigureSystem() = default;
|
||||
|
||||
void ConfigureSystem::SetConfiguration() {
|
||||
enabled = !Core::System::GetInstance().IsPoweredOn();
|
||||
enabled = !system.IsPoweredOn();
|
||||
|
||||
ui->combo_init_clock->setCurrentIndex(static_cast<u8>(Settings::values.init_clock.GetValue()));
|
||||
QDateTime date_time;
|
||||
@ -296,7 +294,7 @@ void ConfigureSystem::SetConfiguration() {
|
||||
ui->edit_init_time_offset_time->setTime(time);
|
||||
|
||||
if (!enabled) {
|
||||
cfg = Service::CFG::GetModule(Core::System::GetInstance());
|
||||
cfg = Service::CFG::GetModule(system);
|
||||
ASSERT_MSG(cfg, "CFG Module missing!");
|
||||
ReadSystemSettings();
|
||||
ui->group_system_settings->setEnabled(false);
|
||||
|
@ -16,6 +16,10 @@ namespace ConfigurationShared {
|
||||
enum class CheckState;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service {
|
||||
namespace CFG {
|
||||
class Module;
|
||||
@ -26,7 +30,7 @@ class ConfigureSystem : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureSystem(QWidget* parent = nullptr);
|
||||
explicit ConfigureSystem(Core::System& system, QWidget* parent = nullptr);
|
||||
~ConfigureSystem() override;
|
||||
|
||||
void ApplyConfiguration();
|
||||
@ -45,8 +49,10 @@ private:
|
||||
|
||||
void DownloadFromNUS();
|
||||
|
||||
ConfigurationShared::CheckState is_new_3ds;
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureSystem> ui;
|
||||
Core::System& system;
|
||||
ConfigurationShared::CheckState is_new_3ds;
|
||||
bool enabled = false;
|
||||
|
||||
std::shared_ptr<Service::CFG::Module> cfg;
|
||||
|
@ -167,8 +167,7 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
|
||||
const auto format = texture.format;
|
||||
|
||||
const auto info = Pica::Texture::TextureInfo::FromPicaRegister(config, format);
|
||||
const u8* src =
|
||||
Core::System::GetInstance().Memory().GetPhysicalPointer(config.GetPhysicalAddress());
|
||||
const u8* src = system.Memory().GetPhysicalPointer(config.GetPhysicalAddress());
|
||||
new_info_widget = new TextureInfoWidget(src, info);
|
||||
}
|
||||
if (command_info_widget) {
|
||||
@ -182,8 +181,8 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
|
||||
}
|
||||
#undef COMMAND_IN_RANGE
|
||||
|
||||
GPUCommandListWidget::GPUCommandListWidget(QWidget* parent)
|
||||
: QDockWidget(tr("Pica Command List"), parent) {
|
||||
GPUCommandListWidget::GPUCommandListWidget(Core::System& system_, QWidget* parent)
|
||||
: QDockWidget(tr("Pica Command List"), parent), system{system_} {
|
||||
setObjectName(QStringLiteral("Pica Command List"));
|
||||
GPUCommandListModel* model = new GPUCommandListModel(this);
|
||||
|
||||
|
@ -7,11 +7,14 @@
|
||||
#include <QAbstractListModel>
|
||||
#include <QDockWidget>
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/gpu_debugger.h"
|
||||
|
||||
class QPushButton;
|
||||
class QTreeView;
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
class GPUCommandListModel : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
|
||||
@ -39,7 +42,7 @@ class GPUCommandListWidget : public QDockWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GPUCommandListWidget(QWidget* parent = nullptr);
|
||||
explicit GPUCommandListWidget(Core::System& system, QWidget* parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void OnToggleTracing();
|
||||
@ -54,7 +57,7 @@ signals:
|
||||
|
||||
private:
|
||||
std::unique_ptr<Pica::DebugUtils::PicaTrace> pica_trace;
|
||||
|
||||
Core::System& system;
|
||||
QTreeView* list_widget;
|
||||
QWidget* command_info_widget;
|
||||
QPushButton* toggle_tracing;
|
||||
|
@ -15,8 +15,6 @@
|
||||
#include "citra_qt/debugger/graphics/graphics_surface.h"
|
||||
#include "citra_qt/util/spinbox.h"
|
||||
#include "common/color.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hw/gpu.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/pica_state.h"
|
||||
#include "video_core/regs_framebuffer.h"
|
||||
@ -51,9 +49,10 @@ void SurfacePicture::mouseMoveEvent(QMouseEvent* 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)
|
||||
: BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent),
|
||||
: BreakPointObserverDock(debug_context, tr("Pica Surface Viewer"), parent), memory{memory_},
|
||||
surface_source(Source::ColorBuffer) {
|
||||
setObjectName(QStringLiteral("PicaSurface"));
|
||||
|
||||
@ -290,57 +289,57 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
|
||||
return;
|
||||
}
|
||||
|
||||
u8* buffer = Core::System::GetInstance().Memory().GetPhysicalPointer(surface_address);
|
||||
if (buffer == nullptr) {
|
||||
const u8* buffer = memory.GetPhysicalPointer(surface_address);
|
||||
if (!buffer) {
|
||||
surface_info_label->setText(tr("(unable to access pixel data)"));
|
||||
surface_info_label->setAlignment(Qt::AlignCenter);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format);
|
||||
unsigned stride = nibbles_per_pixel * surface_width / 2;
|
||||
|
||||
unsigned bytes_per_pixel;
|
||||
bool nibble_mode = (nibbles_per_pixel == 1);
|
||||
if (nibble_mode) {
|
||||
// As nibbles are contained in a byte we still need to access one byte per nibble
|
||||
bytes_per_pixel = 1;
|
||||
} else {
|
||||
bytes_per_pixel = nibbles_per_pixel / 2;
|
||||
}
|
||||
const u32 nibbles_per_pixel = GraphicsSurfaceWidget::NibblesPerPixel(surface_format);
|
||||
const u32 stride = nibbles_per_pixel * surface_width / 2;
|
||||
const bool nibble_mode = (nibbles_per_pixel == 1);
|
||||
const u32 bytes_per_pixel = [&] {
|
||||
if (nibble_mode) {
|
||||
// As nibbles are contained in a byte we still need to access one byte per nibble
|
||||
return 1u;
|
||||
} else {
|
||||
return nibbles_per_pixel / 2;
|
||||
}
|
||||
}();
|
||||
|
||||
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);
|
||||
|
||||
auto GetText = [offset](Format format, const u8* pixel) {
|
||||
const auto get_text = [offset](Format format, const u8* pixel) {
|
||||
switch (format) {
|
||||
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")
|
||||
.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));
|
||||
}
|
||||
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")
|
||||
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
|
||||
QString::number(value.b(), 'f', 2));
|
||||
}
|
||||
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")
|
||||
.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));
|
||||
}
|
||||
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")
|
||||
.arg(QString::number(value.r(), 'f', 2), QString::number(value.g(), 'f', 2),
|
||||
QString::number(value.b(), 'f', 2));
|
||||
}
|
||||
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")
|
||||
.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));
|
||||
@ -348,7 +347,7 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
|
||||
case Format::IA8:
|
||||
return QStringLiteral("Index: %1, Alpha: %2").arg(pixel[0], pixel[1]);
|
||||
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")
|
||||
.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:
|
||||
return QStringLiteral("Index: %1, Alpha: %2").arg(*pixel & 0xF, (*pixel & 0xF0) >> 4);
|
||||
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);
|
||||
}
|
||||
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));
|
||||
}
|
||||
case Format::ETC1:
|
||||
@ -371,17 +370,17 @@ void GraphicsSurfaceWidget::Pick(int x, int y) {
|
||||
// TODO: Display block information or channel values?
|
||||
return QStringLiteral("Compressed data");
|
||||
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));
|
||||
}
|
||||
case Format::D24: {
|
||||
auto value = Common::Color::DecodeD24(pixel);
|
||||
const auto value = Common::Color::DecodeD24(pixel);
|
||||
return QStringLiteral("Depth: %1")
|
||||
.arg(QString::number(value / (float)0xFFFFFF, 'f', 4));
|
||||
}
|
||||
case Format::D24X8:
|
||||
case Format::X24S8: {
|
||||
auto values = Common::Color::DecodeD24S8(pixel);
|
||||
const auto values = Common::Color::DecodeD24S8(pixel);
|
||||
return QStringLiteral("Depth: %1, Stencil: %2")
|
||||
.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) {
|
||||
nibble_index += (offset % 2) ? 0 : 1;
|
||||
}
|
||||
u8 byte = pixel[nibble_index / 2];
|
||||
u8 nibble = (byte >> ((nibble_index % 2) ? 0 : 4)) & 0xF;
|
||||
const u8 byte = pixel[nibble_index / 2];
|
||||
const u8 nibble = (byte >> ((nibble_index % 2) ? 0 : 4)) & 0xF;
|
||||
nibbles.append(QString::number(nibble, 16).toUpper());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -546,9 +545,9 @@ void GraphicsSurfaceWidget::OnUpdate() {
|
||||
// TODO: Implement a good way to visualize alpha components!
|
||||
|
||||
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_info_label->setText(tr("(invalid surface address)"));
|
||||
surface_info_label->setAlignment(Qt::AlignCenter);
|
||||
@ -682,9 +681,8 @@ void GraphicsSurfaceWidget::SaveSurface() {
|
||||
tr("Failed to save surface data to file '%1'").arg(filename));
|
||||
}
|
||||
} else if (selected_filter == bin_filter) {
|
||||
const u8* const buffer =
|
||||
Core::System::GetInstance().Memory().GetPhysicalPointer(surface_address);
|
||||
ASSERT_MSG(buffer != nullptr, "Memory not accessible");
|
||||
const u8* const buffer = memory.GetPhysicalPointer(surface_address);
|
||||
ASSERT_MSG(buffer, "Memory not accessible");
|
||||
|
||||
QFile file{filename};
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
|
@ -14,6 +14,10 @@ class CSpinBox;
|
||||
|
||||
class GraphicsSurfaceWidget;
|
||||
|
||||
namespace Memory {
|
||||
class MemorySystem;
|
||||
}
|
||||
|
||||
class SurfacePicture : public QLabel {
|
||||
Q_OBJECT
|
||||
|
||||
@ -72,7 +76,8 @@ class GraphicsSurfaceWidget : public BreakPointObserverDock {
|
||||
static unsigned int NibblesPerPixel(Format format);
|
||||
|
||||
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);
|
||||
void Pick(int x, int y);
|
||||
|
||||
@ -95,6 +100,7 @@ private:
|
||||
|
||||
void SaveSurface();
|
||||
|
||||
Memory::MemorySystem& memory;
|
||||
QComboBox* surface_source_list;
|
||||
CSpinBox* surface_address_control;
|
||||
QSpinBox* surface_width_control;
|
||||
|
@ -487,7 +487,7 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
|
||||
|
||||
if (replace_vertex_data) {
|
||||
if (vertex_data) {
|
||||
memcpy(&input_vertex, vertex_data, sizeof(input_vertex));
|
||||
std::memcpy(&input_vertex, vertex_data, sizeof(input_vertex));
|
||||
for (unsigned attr = 0; attr < 16; ++attr) {
|
||||
for (unsigned comp = 0; comp < 4; ++comp) {
|
||||
input_data[4 * attr + comp]->setText(
|
||||
|
@ -18,7 +18,7 @@ QString RecordDialog::FormatObject(const IPCDebugger::ObjectInfo& object) const
|
||||
.arg(object.id, 8, 16, QLatin1Char('0'));
|
||||
}
|
||||
|
||||
QString RecordDialog::FormatCmdbuf(const std::vector<u32>& cmdbuf) const {
|
||||
QString RecordDialog::FormatCmdbuf(std::span<const u32> cmdbuf) const {
|
||||
QString result;
|
||||
for (std::size_t i = 0; i < cmdbuf.size(); ++i) {
|
||||
result.append(
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <QDialog>
|
||||
@ -28,7 +29,7 @@ public:
|
||||
|
||||
private:
|
||||
QString FormatObject(const IPCDebugger::ObjectInfo& object) const;
|
||||
QString FormatCmdbuf(const std::vector<u32>& cmdbuf) const;
|
||||
QString FormatCmdbuf(std::span<const u32> cmdbuf) const;
|
||||
void UpdateCmdbufDisplay();
|
||||
|
||||
std::unique_ptr<Ui::RecordDialog> ui;
|
||||
|
@ -16,13 +16,13 @@
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "ui_recorder.h"
|
||||
|
||||
IPCRecorderWidget::IPCRecorderWidget(QWidget* parent)
|
||||
: QDockWidget(parent), ui(std::make_unique<Ui::IPCRecorder>()) {
|
||||
IPCRecorderWidget::IPCRecorderWidget(Core::System& system_, QWidget* parent)
|
||||
: QDockWidget(parent), ui(std::make_unique<Ui::IPCRecorder>()), system{system_} {
|
||||
|
||||
ui->setupUi(this);
|
||||
qRegisterMetaType<IPCDebugger::RequestRecord>();
|
||||
|
||||
connect(ui->enabled, &QCheckBox::stateChanged,
|
||||
connect(ui->enabled, &QCheckBox::stateChanged, this,
|
||||
[this](int new_state) { SetEnabled(new_state == Qt::Checked); });
|
||||
connect(ui->clearButton, &QPushButton::clicked, this, &IPCRecorderWidget::Clear);
|
||||
connect(ui->filter, &QLineEdit::textChanged, this, &IPCRecorderWidget::ApplyFilterToAll);
|
||||
@ -90,7 +90,7 @@ void IPCRecorderWidget::OnEntryUpdated(IPCDebugger::RequestRecord record) {
|
||||
(record.status == IPCDebugger::RequestStatus::Handled &&
|
||||
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) {
|
||||
item->setBackground(column, QBrush(QColor::fromRgb(255, 0, 0)));
|
||||
}
|
||||
@ -100,11 +100,11 @@ void IPCRecorderWidget::OnEntryUpdated(IPCDebugger::RequestRecord record) {
|
||||
}
|
||||
|
||||
void IPCRecorderWidget::SetEnabled(bool enabled) {
|
||||
if (!Core::System::GetInstance().IsPoweredOn()) {
|
||||
if (!system.IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& ipc_recorder = Core::System::GetInstance().Kernel().GetIPCRecorder();
|
||||
auto& ipc_recorder = system.Kernel().GetIPCRecorder();
|
||||
ipc_recorder.SetEnabled(enabled);
|
||||
|
||||
if (enabled) {
|
||||
@ -123,10 +123,10 @@ void IPCRecorderWidget::Clear() {
|
||||
}
|
||||
|
||||
QString IPCRecorderWidget::GetServiceName(const IPCDebugger::RequestRecord& record) const {
|
||||
if (Core::System::GetInstance().IsPoweredOn() && record.client_port.id != -1) {
|
||||
const auto service_name =
|
||||
Core::System::GetInstance().ServiceManager().GetServiceNameByPortId(
|
||||
static_cast<u32>(record.client_port.id));
|
||||
if (system.IsPoweredOn() && record.client_port.id != -1) {
|
||||
const Service::SM::ServiceManager& sm = system.ServiceManager();
|
||||
const u32 port_id = static_cast<u32>(record.client_port.id);
|
||||
const auto service_name = sm.GetServiceNameByPortId(port_id);
|
||||
|
||||
if (!service_name.empty()) {
|
||||
return QString::fromStdString(service_name);
|
||||
|
@ -14,11 +14,15 @@ namespace Ui {
|
||||
class IPCRecorder;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
class IPCRecorderWidget : public QDockWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit IPCRecorderWidget(QWidget* parent = nullptr);
|
||||
explicit IPCRecorderWidget(Core::System& system, QWidget* parent = nullptr);
|
||||
~IPCRecorderWidget();
|
||||
|
||||
void OnEmulationStarting();
|
||||
@ -37,9 +41,10 @@ private:
|
||||
QString GetFunctionName(const IPCDebugger::RequestRecord& record) const;
|
||||
void OpenRecordDialog(QTreeWidgetItem* item, int column);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::IPCRecorder> ui;
|
||||
IPCDebugger::CallbackHandle handle;
|
||||
|
||||
Core::System& system;
|
||||
// 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.
|
||||
// The initial value is 1, which means record 1 = row 0.
|
||||
|
@ -9,8 +9,8 @@
|
||||
#include "core/core.h"
|
||||
#include "ui_registers.h"
|
||||
|
||||
RegistersWidget::RegistersWidget(QWidget* parent)
|
||||
: QDockWidget(parent), cpu_regs_ui(std::make_unique<Ui::ARMRegisters>()) {
|
||||
RegistersWidget::RegistersWidget(const Core::System& system_, QWidget* parent)
|
||||
: QDockWidget(parent), cpu_regs_ui(std::make_unique<Ui::ARMRegisters>()), system{system_} {
|
||||
cpu_regs_ui->setupUi(this);
|
||||
|
||||
tree = cpu_regs_ui->treeWidget;
|
||||
@ -62,17 +62,21 @@ RegistersWidget::RegistersWidget(QWidget* parent)
|
||||
RegistersWidget::~RegistersWidget() = default;
|
||||
|
||||
void RegistersWidget::OnDebugModeEntered() {
|
||||
if (!Core::System::GetInstance().IsPoweredOn())
|
||||
if (!system.IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Todo: Handle all cores
|
||||
for (int i = 0; i < core_registers->childCount(); ++i)
|
||||
// TODO: Handle all cores
|
||||
const ARM_Interface& core = system.GetCore(0);
|
||||
for (int i = 0; i < core_registers->childCount(); ++i) {
|
||||
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(
|
||||
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();
|
||||
UpdateVFPSystemRegisterValues();
|
||||
@ -86,24 +90,29 @@ void RegistersWidget::OnEmulationStarting(EmuThread* emu_thread) {
|
||||
|
||||
void RegistersWidget::OnEmulationStopping() {
|
||||
// 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{});
|
||||
}
|
||||
|
||||
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{});
|
||||
}
|
||||
|
||||
for (int i = 0; i < cpsr->childCount(); ++i)
|
||||
for (int i = 0; i < cpsr->childCount(); ++i) {
|
||||
cpsr->child(i)->setText(1, QString{});
|
||||
}
|
||||
|
||||
cpsr->setText(1, QString{});
|
||||
|
||||
// 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{});
|
||||
}
|
||||
|
||||
// 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(0)->setText(1, QString{});
|
||||
vfp_system_registers->child(1)->setText(1, QString{});
|
||||
@ -130,8 +139,8 @@ void RegistersWidget::CreateCPSRChildren() {
|
||||
}
|
||||
|
||||
void RegistersWidget::UpdateCPSRValues() {
|
||||
// Todo: Handle all cores
|
||||
const u32 cpsr_val = Core::GetCore(0).GetCPSR();
|
||||
// TODO: Handle all cores
|
||||
const u32 cpsr_val = system.GetCore(0).GetCPSR();
|
||||
|
||||
cpsr->setText(1, QStringLiteral("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0')));
|
||||
cpsr->child(0)->setText(
|
||||
@ -193,9 +202,10 @@ void RegistersWidget::CreateVFPSystemRegisterChildren() {
|
||||
}
|
||||
|
||||
void RegistersWidget::UpdateVFPSystemRegisterValues() {
|
||||
// Todo: handle all cores
|
||||
const u32 fpscr_val = Core::GetCore(0).GetVFPSystemReg(VFP_FPSCR);
|
||||
const u32 fpexc_val = Core::GetCore(0).GetVFPSystemReg(VFP_FPEXC);
|
||||
// TODO: handle all cores
|
||||
const ARM_Interface& core = system.GetCore(0);
|
||||
const u32 fpscr_val = core.GetVFPSystemReg(VFP_FPSCR);
|
||||
const u32 fpexc_val = core.GetVFPSystemReg(VFP_FPEXC);
|
||||
|
||||
QTreeWidgetItem* const fpscr = vfp_system_registers->child(0);
|
||||
fpscr->setText(1, QStringLiteral("0x%1").arg(fpscr_val, 8, 16, QLatin1Char('0')));
|
||||
|
@ -15,11 +15,15 @@ namespace Ui {
|
||||
class ARMRegisters;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
class RegistersWidget : public QDockWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RegistersWidget(QWidget* parent = nullptr);
|
||||
explicit RegistersWidget(const Core::System& system, QWidget* parent = nullptr);
|
||||
~RegistersWidget();
|
||||
|
||||
public slots:
|
||||
@ -37,7 +41,7 @@ private:
|
||||
void UpdateVFPSystemRegisterValues();
|
||||
|
||||
std::unique_ptr<Ui::ARMRegisters> cpu_regs_ui;
|
||||
|
||||
const Core::System& system;
|
||||
QTreeWidget* tree;
|
||||
|
||||
QTreeWidgetItem* core_registers;
|
||||
|
@ -5,11 +5,10 @@
|
||||
#include <array>
|
||||
#include "citra_qt/debugger/wait_tree.h"
|
||||
#include "citra_qt/uisettings.h"
|
||||
#include "citra_qt/util/util.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/mutex.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/semaphore.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/timer.h"
|
||||
@ -64,7 +63,7 @@ WaitTreeItem* WaitTreeItem::Parent() const {
|
||||
return parent;
|
||||
}
|
||||
|
||||
const std::vector<std::unique_ptr<WaitTreeItem>>& WaitTreeItem::Children() const {
|
||||
std::span<const std::unique_ptr<WaitTreeItem>> WaitTreeItem::Children() const {
|
||||
return children;
|
||||
}
|
||||
|
||||
@ -76,13 +75,13 @@ std::size_t WaitTreeItem::Row() const {
|
||||
return row;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() {
|
||||
const u32 num_cores = Core::GetNumCores();
|
||||
std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList(
|
||||
Core::System& system) {
|
||||
const u32 num_cores = system.GetNumCores();
|
||||
std::vector<std::unique_ptr<WaitTreeThread>> item_list;
|
||||
item_list.reserve(num_cores);
|
||||
for (u32 i = 0; i < num_cores; ++i) {
|
||||
const auto& threads =
|
||||
Core::System::GetInstance().Kernel().GetThreadManager(i).GetThreadList();
|
||||
const auto threads = system.Kernel().GetThreadManager(i).GetThreadList();
|
||||
item_list.reserve(item_list.size() + threads.size());
|
||||
for (std::size_t j = 0; j < threads.size(); ++j) {
|
||||
item_list.push_back(std::make_unique<WaitTreeThread>(*threads[j]));
|
||||
@ -436,11 +435,12 @@ void WaitTreeModel::ClearItems() {
|
||||
thread_items.clear();
|
||||
}
|
||||
|
||||
void WaitTreeModel::InitItems() {
|
||||
thread_items = WaitTreeItem::MakeThreadItemList();
|
||||
void WaitTreeModel::InitItems(Core::System& system) {
|
||||
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"));
|
||||
view = new QTreeView(this);
|
||||
view->setHeaderHidden(true);
|
||||
@ -449,9 +449,10 @@ WaitTreeWidget::WaitTreeWidget(QWidget* parent) : QDockWidget(tr("Wait Tree"), p
|
||||
}
|
||||
|
||||
void WaitTreeWidget::OnDebugModeEntered() {
|
||||
if (!Core::System::GetInstance().IsPoweredOn())
|
||||
if (!system.IsPoweredOn()) {
|
||||
return;
|
||||
model->InitItems();
|
||||
}
|
||||
model->InitItems(system);
|
||||
view->setModel(model);
|
||||
setEnabled(true);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QDockWidget>
|
||||
#include <QTreeView>
|
||||
@ -22,6 +23,10 @@ class Thread;
|
||||
class Timer;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
class WaitTreeThread;
|
||||
|
||||
class WaitTreeItem : public QObject {
|
||||
@ -36,9 +41,9 @@ public:
|
||||
|
||||
void Expand();
|
||||
WaitTreeItem* Parent() const;
|
||||
const std::vector<std::unique_ptr<WaitTreeItem>>& Children() const;
|
||||
std::span<const std::unique_ptr<WaitTreeItem>> Children() 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:
|
||||
std::size_t row;
|
||||
@ -165,7 +170,7 @@ public:
|
||||
int columnCount(const QModelIndex& parent) const override;
|
||||
|
||||
void ClearItems();
|
||||
void InitItems();
|
||||
void InitItems(Core::System& system);
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<WaitTreeThread>> thread_items;
|
||||
@ -175,7 +180,7 @@ class WaitTreeWidget : public QDockWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit WaitTreeWidget(QWidget* parent = nullptr);
|
||||
explicit WaitTreeWidget(Core::System& system, QWidget* parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void OnDebugModeEntered();
|
||||
@ -187,4 +192,5 @@ public slots:
|
||||
private:
|
||||
QTreeView* view;
|
||||
WaitTreeModel* model;
|
||||
Core::System& system;
|
||||
};
|
||||
|
@ -9,10 +9,11 @@
|
||||
#include "citra_qt/uisettings.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace DiscordRPC {
|
||||
|
||||
DiscordImpl::DiscordImpl() {
|
||||
DiscordImpl::DiscordImpl(const Core::System& system_) : system{system_} {
|
||||
DiscordEventHandlers handlers{};
|
||||
|
||||
// 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())
|
||||
.count();
|
||||
std::string title;
|
||||
if (Core::System::GetInstance().IsPoweredOn())
|
||||
Core::System::GetInstance().GetAppLoader().ReadTitle(title);
|
||||
const bool is_powered_on = system.IsPoweredOn();
|
||||
if (is_powered_on) {
|
||||
system.GetAppLoader().ReadTitle(title);
|
||||
}
|
||||
|
||||
DiscordRichPresence presence{};
|
||||
presence.largeImageKey = "citra";
|
||||
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.details = "Currently in game";
|
||||
} else {
|
||||
|
@ -6,15 +6,22 @@
|
||||
|
||||
#include "citra_qt/discord.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace DiscordRPC {
|
||||
|
||||
class DiscordImpl : public DiscordInterface {
|
||||
public:
|
||||
DiscordImpl();
|
||||
DiscordImpl(const Core::System& system);
|
||||
~DiscordImpl() override;
|
||||
|
||||
void Pause() override;
|
||||
void Update() override;
|
||||
|
||||
private:
|
||||
const Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace DiscordRPC
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <QDialog>
|
||||
#include "core/dumping/ffmpeg_backend.h"
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QActionGroup>
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
@ -307,6 +308,9 @@ GameList::GameList(GMainWindow* parent) : QWidget{parent} {
|
||||
tree_view->setEditTriggers(QHeaderView::NoEditTriggers);
|
||||
tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
tree_view->setStyleSheet(QStringLiteral("QTreeView{ border: none; }"));
|
||||
tree_view->header()->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
UpdateColumnVisibility();
|
||||
|
||||
item_model->insertColumns(0, COLUMN_COUNT);
|
||||
RetranslateUI();
|
||||
@ -317,6 +321,8 @@ GameList::GameList(GMainWindow* parent) : QWidget{parent} {
|
||||
connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
|
||||
connect(tree_view, &QTreeView::expanded, 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
|
||||
// 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));
|
||||
}
|
||||
|
||||
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) {
|
||||
for (const std::string_view cache_type : {"separable", "conventional"}) {
|
||||
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) {
|
||||
tree_view->setEnabled(false);
|
||||
|
||||
// Update the columns in case UISettings has changed
|
||||
UpdateColumnVisibility();
|
||||
|
||||
// Delete any rows that might already exist if we're repopulating
|
||||
item_model->removeRows(0, item_model->rowCount());
|
||||
search_field->clear();
|
||||
|
@ -104,9 +104,11 @@ private:
|
||||
void DonePopulating(const QStringList& watch_list);
|
||||
|
||||
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 AddCustomDirPopup(QMenu& context_menu, QModelIndex selected);
|
||||
void AddPermDirPopup(QMenu& context_menu, QModelIndex selected);
|
||||
void UpdateColumnVisibility();
|
||||
|
||||
QString FindGameByProgramID(QStandardItem* current_item, u64 program_id, int role);
|
||||
|
||||
|
@ -6,8 +6,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <QCoreApplication>
|
||||
#include <QFileInfo>
|
||||
#include <QImage>
|
||||
@ -153,7 +155,7 @@ public:
|
||||
static constexpr int LongTitleRole = SortRole + 5;
|
||||
|
||||
GameListItemPath() = default;
|
||||
GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id,
|
||||
GameListItemPath(const QString& game_path, std::span<const u8> smdh_data, u64 program_id,
|
||||
u64 extdata_id) {
|
||||
setData(type(), TypeRole);
|
||||
setData(game_path, FullPathRole);
|
||||
@ -178,7 +180,7 @@ public:
|
||||
}
|
||||
|
||||
Loader::SMDH smdh;
|
||||
memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
||||
std::memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
||||
|
||||
// Get icon from SMDH
|
||||
if (UISettings::values.game_list_icon_size.GetValue() !=
|
||||
@ -286,7 +288,7 @@ public:
|
||||
class GameListItemRegion : public GameListItem {
|
||||
public:
|
||||
GameListItemRegion() = default;
|
||||
explicit GameListItemRegion(const std::vector<u8>& smdh_data) {
|
||||
explicit GameListItemRegion(std::span<const u8> smdh_data) {
|
||||
setData(type(), TypeRole);
|
||||
|
||||
if (!Loader::IsValidSMDH(smdh_data)) {
|
||||
@ -295,7 +297,7 @@ public:
|
||||
}
|
||||
|
||||
Loader::SMDH smdh;
|
||||
memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
||||
std::memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
||||
|
||||
setText(GetRegionFromSMDH(smdh));
|
||||
setData(GetRegionFromSMDH(smdh), SortRole);
|
||||
|
@ -80,7 +80,7 @@ const static std::unordered_map<VideoCore::LoadCallbackStage, const char*> progr
|
||||
|
||||
static QPixmap GetQPixmapFromSMDH(std::vector<u8>& smdh_data) {
|
||||
Loader::SMDH smdh;
|
||||
memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
||||
std::memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH));
|
||||
|
||||
bool large = true;
|
||||
std::vector<u16> icon_data = smdh.GetIcon(large);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <QtGui>
|
||||
#include <QtWidgets>
|
||||
#include <fmt/format.h>
|
||||
#include "core/telemetry_session.h"
|
||||
#ifdef __APPLE__
|
||||
#include <unistd.h> // for chdir
|
||||
#endif
|
||||
@ -73,7 +74,6 @@
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/string_util.h"
|
||||
#if CITRA_ARCH(x86_64)
|
||||
#include "common/x64/cpu_detect.h"
|
||||
#endif
|
||||
@ -173,7 +173,7 @@ static QString PrettyProductName() {
|
||||
}
|
||||
|
||||
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} {
|
||||
Common::Log::Initialize();
|
||||
Common::Log::Start();
|
||||
@ -353,8 +353,8 @@ void GMainWindow::InitializeWidgets() {
|
||||
});
|
||||
|
||||
InputCommon::Init();
|
||||
multiplayer_state = new MultiplayerState(this, game_list->GetModel(), ui->action_Leave_Room,
|
||||
ui->action_Show_Room);
|
||||
multiplayer_state = new MultiplayerState(system, this, game_list->GetModel(),
|
||||
ui->action_Leave_Room, ui->action_Show_Room);
|
||||
multiplayer_state->setVisible(false);
|
||||
|
||||
#if ENABLE_QT_UPDATER
|
||||
@ -434,7 +434,7 @@ void GMainWindow::InitializeDebugWidgets() {
|
||||
debug_menu->addAction(microProfileDialog->toggleViewAction());
|
||||
#endif
|
||||
|
||||
registersWidget = new RegistersWidget(this);
|
||||
registersWidget = new RegistersWidget(system, this);
|
||||
addDockWidget(Qt::RightDockWidgetArea, registersWidget);
|
||||
registersWidget->hide();
|
||||
debug_menu->addAction(registersWidget->toggleViewAction());
|
||||
@ -448,7 +448,7 @@ void GMainWindow::InitializeDebugWidgets() {
|
||||
graphicsWidget->hide();
|
||||
debug_menu->addAction(graphicsWidget->toggleViewAction());
|
||||
|
||||
graphicsCommandsWidget = new GPUCommandListWidget(this);
|
||||
graphicsCommandsWidget = new GPUCommandListWidget(system, this);
|
||||
addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
|
||||
graphicsCommandsWidget->hide();
|
||||
debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
|
||||
@ -472,7 +472,7 @@ void GMainWindow::InitializeDebugWidgets() {
|
||||
connect(this, &GMainWindow::EmulationStopping, graphicsTracingWidget,
|
||||
&GraphicsTracingWidget::OnEmulationStopping);
|
||||
|
||||
waitTreeWidget = new WaitTreeWidget(this);
|
||||
waitTreeWidget = new WaitTreeWidget(system, this);
|
||||
addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
|
||||
waitTreeWidget->hide();
|
||||
debug_menu->addAction(waitTreeWidget->toggleViewAction());
|
||||
@ -490,7 +490,7 @@ void GMainWindow::InitializeDebugWidgets() {
|
||||
connect(this, &GMainWindow::EmulationStopping, waitTreeWidget,
|
||||
[this] { lleServiceModulesWidget->setDisabled(false); });
|
||||
|
||||
ipcRecorderWidget = new IPCRecorderWidget(this);
|
||||
ipcRecorderWidget = new IPCRecorderWidget(system, this);
|
||||
addDockWidget(Qt::RightDockWidgetArea, ipcRecorderWidget);
|
||||
ipcRecorderWidget->hide();
|
||||
debug_menu->addAction(ipcRecorderWidget->toggleViewAction());
|
||||
@ -1171,20 +1171,18 @@ void GMainWindow::BootGame(const QString& filename) {
|
||||
movie.PrepareForPlayback(movie_playback_path.toStdString());
|
||||
}
|
||||
|
||||
u64 title_id{0};
|
||||
const std::string path = filename.toStdString();
|
||||
const auto loader = Loader::GetLoader(path);
|
||||
|
||||
if (loader && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success) {
|
||||
// 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);
|
||||
Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
|
||||
system.ApplySettings();
|
||||
u64 title_id{0};
|
||||
loader->ReadProgramId(title_id);
|
||||
|
||||
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();
|
||||
|
||||
@ -1223,7 +1221,7 @@ void GMainWindow::BootGame(const QString& filename) {
|
||||
}
|
||||
|
||||
// Create and start the emulation thread
|
||||
emu_thread = std::make_unique<EmuThread>(*render_window);
|
||||
emu_thread = std::make_unique<EmuThread>(system, *render_window);
|
||||
emit EmulationStarting(emu_thread.get());
|
||||
emu_thread->start();
|
||||
|
||||
@ -1415,7 +1413,7 @@ void GMainWindow::UpdateSaveStates() {
|
||||
if (system.GetAppLoader().ReadProgramId(title_id) != Loader::ResultStatus::Success) {
|
||||
return;
|
||||
}
|
||||
auto savestates = Core::ListSaveStates(title_id);
|
||||
auto savestates = Core::ListSaveStates(title_id, movie.GetCurrentMovieID());
|
||||
for (u32 i = 0; i < Core::SaveStateSlotCount; ++i) {
|
||||
actions_load_state[i]->setEnabled(false);
|
||||
actions_load_state[i]->setText(tr("Slot %1").arg(i + 1));
|
||||
@ -1814,7 +1812,7 @@ void GMainWindow::OnLoadComplete() {
|
||||
|
||||
void GMainWindow::OnMenuReportCompatibility() {
|
||||
if (!NetSettings::values.citra_token.empty() && !NetSettings::values.citra_username.empty()) {
|
||||
CompatDB compatdb{this};
|
||||
CompatDB compatdb{system.TelemetrySession(), this};
|
||||
compatdb.exec();
|
||||
} else {
|
||||
QMessageBox::critical(this, tr("Missing Citra Account"),
|
||||
@ -2048,7 +2046,6 @@ void GMainWindow::OnLoadAmiibo() {
|
||||
return;
|
||||
}
|
||||
|
||||
Core::System& system{Core::System::GetInstance()};
|
||||
Service::SM::ServiceManager& sm = system.ServiceManager();
|
||||
auto nfc = sm.GetService<Service::NFC::Module::Interface>("nfc:u");
|
||||
if (nfc == nullptr) {
|
||||
@ -2120,14 +2117,15 @@ void GMainWindow::OnToggleFilterBar() {
|
||||
}
|
||||
|
||||
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);
|
||||
// TODO: Maybe graphicsSurfaceViewerWidget->setFloating(true);
|
||||
graphicsSurfaceViewerWidget->show();
|
||||
}
|
||||
|
||||
void GMainWindow::OnRecordMovie() {
|
||||
MovieRecordDialog dialog(this);
|
||||
MovieRecordDialog dialog(this, system);
|
||||
if (dialog.exec() != QDialog::Accepted) {
|
||||
return;
|
||||
}
|
||||
@ -2144,7 +2142,7 @@ void GMainWindow::OnRecordMovie() {
|
||||
}
|
||||
|
||||
void GMainWindow::OnPlayMovie() {
|
||||
MoviePlayDialog dialog(this, game_list);
|
||||
MoviePlayDialog dialog(this, game_list, system);
|
||||
if (dialog.exec() != QDialog::Accepted) {
|
||||
return;
|
||||
}
|
||||
@ -2785,9 +2783,13 @@ void GMainWindow::UpdateWindowTitle() {
|
||||
const QString full_name = QString::fromUtf8(Common::g_build_fullname);
|
||||
|
||||
if (game_title.isEmpty()) {
|
||||
setWindowTitle(tr("Citra %1").arg(full_name));
|
||||
setWindowTitle(QStringLiteral("Citra %1").arg(full_name));
|
||||
} 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 +2847,7 @@ void GMainWindow::RetranslateStatusBar() {
|
||||
void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
|
||||
#ifdef USE_DISCORD_PRESENCE
|
||||
if (state) {
|
||||
discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>();
|
||||
discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>(system);
|
||||
} else {
|
||||
discord_rpc = std::make_unique<DiscordRPC::NullImpl>();
|
||||
}
|
||||
@ -2931,7 +2933,7 @@ int main(int argc, char* argv[]) {
|
||||
GMainWindow main_window(system);
|
||||
|
||||
// Register frontend applets
|
||||
Frontend::RegisterDefaultApplets();
|
||||
Frontend::RegisterDefaultApplets(system);
|
||||
|
||||
system.RegisterMiiSelector(std::make_shared<QtMiiSelector>(main_window));
|
||||
system.RegisterSoftwareKeyboard(std::make_shared<QtKeyboard>(main_window));
|
||||
|
@ -15,8 +15,9 @@
|
||||
#include "core/movie.h"
|
||||
#include "ui_movie_play_dialog.h"
|
||||
|
||||
MoviePlayDialog::MoviePlayDialog(QWidget* parent, GameList* game_list_)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::MoviePlayDialog>()), game_list(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_}, system{system_} {
|
||||
ui->setupUi(this);
|
||||
|
||||
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::rejected, this, &MoviePlayDialog::reject);
|
||||
|
||||
if (Core::System::GetInstance().IsPoweredOn()) {
|
||||
if (system.IsPoweredOn()) {
|
||||
QString note_text;
|
||||
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."));
|
||||
}
|
||||
ui->note2Label->setText(note_text);
|
||||
@ -43,7 +44,7 @@ QString MoviePlayDialog::GetMoviePath() 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);
|
||||
}
|
||||
|
||||
@ -67,9 +68,10 @@ void MoviePlayDialog::UpdateUIDisplay() {
|
||||
ui->lengthLineEdit->clear();
|
||||
ui->note1Label->setVisible(true);
|
||||
|
||||
const auto& movie = system.Movie();
|
||||
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) {
|
||||
ui->note1Label->setText(tr("Invalid movie file."));
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
@ -94,7 +96,7 @@ void MoviePlayDialog::UpdateUIDisplay() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
const auto metadata = Core::Movie::GetInstance().GetMovieMetadata(path);
|
||||
const auto metadata = movie.GetMovieMetadata(path);
|
||||
|
||||
// Format game title
|
||||
const auto title =
|
||||
|
@ -11,11 +11,15 @@ namespace Ui {
|
||||
class MoviePlayDialog;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
class MoviePlayDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MoviePlayDialog(QWidget* parent, GameList* game_list);
|
||||
explicit MoviePlayDialog(QWidget* parent, GameList* game_list, const Core::System& system);
|
||||
~MoviePlayDialog() override;
|
||||
|
||||
QString GetMoviePath() const;
|
||||
@ -27,4 +31,5 @@ private:
|
||||
|
||||
std::unique_ptr<Ui::MoviePlayDialog> ui;
|
||||
GameList* game_list;
|
||||
const Core::System& system;
|
||||
};
|
||||
|
@ -10,8 +10,8 @@
|
||||
#include "core/movie.h"
|
||||
#include "ui_movie_record_dialog.h"
|
||||
|
||||
MovieRecordDialog::MovieRecordDialog(QWidget* parent)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::MovieRecordDialog>()) {
|
||||
MovieRecordDialog::MovieRecordDialog(QWidget* parent, const Core::System& system_)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::MovieRecordDialog>()), system{system_} {
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
@ -23,9 +23,9 @@ MovieRecordDialog::MovieRecordDialog(QWidget* parent)
|
||||
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &MovieRecordDialog::reject);
|
||||
|
||||
QString note_text;
|
||||
if (Core::System::GetInstance().IsPoweredOn()) {
|
||||
if (system.IsPoweredOn()) {
|
||||
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."));
|
||||
}
|
||||
} else {
|
||||
|
@ -9,11 +9,15 @@ namespace Ui {
|
||||
class MovieRecordDialog;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
class MovieRecordDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MovieRecordDialog(QWidget* parent);
|
||||
explicit MovieRecordDialog(QWidget* parent, const Core::System& system);
|
||||
~MovieRecordDialog() override;
|
||||
|
||||
QString GetPath() const;
|
||||
@ -24,4 +28,5 @@ private:
|
||||
void UpdateUIDisplay();
|
||||
|
||||
std::unique_ptr<Ui::MovieRecordDialog> ui;
|
||||
const Core::System& system;
|
||||
};
|
||||
|
@ -9,10 +9,8 @@
|
||||
#include <QString>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
#include "citra_qt/main.h"
|
||||
#include "citra_qt/multiplayer/client_room.h"
|
||||
#include "citra_qt/multiplayer/direct_connect.h"
|
||||
#include "citra_qt/multiplayer/message.h"
|
||||
#include "citra_qt/multiplayer/state.h"
|
||||
#include "citra_qt/multiplayer/validation.h"
|
||||
#include "citra_qt/uisettings.h"
|
||||
#include "core/hle/service/cfg/cfg.h"
|
||||
@ -22,9 +20,9 @@
|
||||
|
||||
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),
|
||||
ui(std::make_unique<Ui::DirectConnect>()) {
|
||||
ui(std::make_unique<Ui::DirectConnect>()), system{system_} {
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
@ -91,7 +89,7 @@ void DirectConnectWindow::Connect() {
|
||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||
auto port = UISettings::values.port.toUInt();
|
||||
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,
|
||||
Network::NoPreferredMac, ui->password->text().toStdString().c_str());
|
||||
}
|
||||
|
@ -13,11 +13,15 @@ namespace Ui {
|
||||
class DirectConnect;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
class DirectConnectWindow : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DirectConnectWindow(QWidget* parent = nullptr);
|
||||
explicit DirectConnectWindow(Core::System& system, QWidget* parent = nullptr);
|
||||
~DirectConnectWindow();
|
||||
|
||||
void RetranslateUi();
|
||||
@ -39,5 +43,6 @@ private:
|
||||
|
||||
QFutureWatcher<void>* watcher;
|
||||
std::unique_ptr<Ui::DirectConnect> ui;
|
||||
Core::System& system;
|
||||
Validation validation;
|
||||
};
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <QTime>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
#include "citra_qt/game_list_p.h"
|
||||
#include "citra_qt/main.h"
|
||||
#include "citra_qt/multiplayer/host_room.h"
|
||||
#include "citra_qt/multiplayer/message.h"
|
||||
#include "citra_qt/multiplayer/state.h"
|
||||
@ -27,10 +26,10 @@
|
||||
#include "web_service/verify_user_jwt.h"
|
||||
#endif
|
||||
|
||||
HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list,
|
||||
HostRoomWindow::HostRoomWindow(Core::System& system_, QWidget* parent, QStandardItemModel* list,
|
||||
std::shared_ptr<Network::AnnounceMultiplayerSession> session)
|
||||
: 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);
|
||||
|
||||
// set up validation for all of the fields
|
||||
@ -196,9 +195,8 @@ void HostRoomWindow::Host() {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
member->Join(ui->username->text().toStdString(),
|
||||
Service::CFG::GetConsoleIdHash(Core::System::GetInstance()), "127.0.0.1", port,
|
||||
0, Network::NoPreferredMac, password, token);
|
||||
member->Join(ui->username->text().toStdString(), Service::CFG::GetConsoleIdHash(system),
|
||||
"127.0.0.1", port, 0, Network::NoPreferredMac, password, token);
|
||||
|
||||
// Store settings
|
||||
UISettings::values.room_nickname = ui->username->text();
|
||||
|
@ -9,14 +9,16 @@
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QStandardItemModel>
|
||||
#include <QVariant>
|
||||
#include "citra_qt/multiplayer/chat_room.h"
|
||||
#include "citra_qt/multiplayer/validation.h"
|
||||
#include "network/network.h"
|
||||
|
||||
namespace Ui {
|
||||
class HostRoom;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Network {
|
||||
class AnnounceMultiplayerSession;
|
||||
}
|
||||
@ -34,7 +36,7 @@ class HostRoomWindow : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit HostRoomWindow(QWidget* parent, QStandardItemModel* list,
|
||||
explicit HostRoomWindow(Core::System& system, QWidget* parent, QStandardItemModel* list,
|
||||
std::shared_ptr<Network::AnnounceMultiplayerSession> session);
|
||||
~HostRoomWindow();
|
||||
|
||||
@ -50,6 +52,7 @@ private:
|
||||
std::unique_ptr<Network::VerifyUser::Backend> CreateVerifyBackend(bool use_validation) const;
|
||||
|
||||
std::unique_ptr<Ui::HostRoom> ui;
|
||||
Core::System& system;
|
||||
std::weak_ptr<Network::AnnounceMultiplayerSession> announce_multiplayer_session;
|
||||
QStandardItemModel* game_list;
|
||||
ComboBoxProxyModel* proxy;
|
||||
|
@ -7,11 +7,9 @@
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
#include "citra_qt/game_list_p.h"
|
||||
#include "citra_qt/main.h"
|
||||
#include "citra_qt/multiplayer/client_room.h"
|
||||
#include "citra_qt/multiplayer/lobby.h"
|
||||
#include "citra_qt/multiplayer/lobby_p.h"
|
||||
#include "citra_qt/multiplayer/message.h"
|
||||
#include "citra_qt/multiplayer/state.h"
|
||||
#include "citra_qt/multiplayer/validation.h"
|
||||
#include "citra_qt/uisettings.h"
|
||||
#include "common/logging/log.h"
|
||||
@ -23,10 +21,10 @@
|
||||
#include "web_service/web_backend.h"
|
||||
#endif
|
||||
|
||||
Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
|
||||
Lobby::Lobby(Core::System& system_, QWidget* parent, QStandardItemModel* list,
|
||||
std::shared_ptr<Network::AnnounceMultiplayerSession> session)
|
||||
: 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);
|
||||
|
||||
// setup the watcher for background connections
|
||||
@ -152,8 +150,8 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
|
||||
const std::string verify_UID =
|
||||
proxy->data(connection_index, LobbyItemHost::HostVerifyUIDRole).toString().toStdString();
|
||||
|
||||
// attempt to connect in a different thread
|
||||
QFuture<void> f = QtConcurrent::run([nickname, ip, port, password, verify_UID] {
|
||||
// Attempt to connect in a different thread
|
||||
QFuture<void> f = QtConcurrent::run([this, nickname, ip, port, password, verify_UID] {
|
||||
std::string token;
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
if (!NetSettings::values.citra_username.empty() &&
|
||||
@ -170,8 +168,8 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
|
||||
}
|
||||
#endif
|
||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||
room_member->Join(nickname, Service::CFG::GetConsoleIdHash(Core::System::GetInstance()),
|
||||
ip.c_str(), port, 0, Network::NoPreferredMac, password, token);
|
||||
room_member->Join(nickname, Service::CFG::GetConsoleIdHash(system), ip.c_str(), port, 0,
|
||||
Network::NoPreferredMac, password, token);
|
||||
}
|
||||
});
|
||||
watcher->setFuture(f);
|
||||
|
@ -12,12 +12,16 @@
|
||||
#include "citra_qt/multiplayer/validation.h"
|
||||
#include "common/announce_multiplayer_room.h"
|
||||
#include "network/announce_multiplayer_session.h"
|
||||
#include "network/network.h"
|
||||
#include "network/room_member.h"
|
||||
|
||||
namespace Ui {
|
||||
class Lobby;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
class LobbyModel;
|
||||
class LobbyFilterProxyModel;
|
||||
|
||||
@ -29,7 +33,7 @@ class Lobby : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Lobby(QWidget* parent, QStandardItemModel* list,
|
||||
explicit Lobby(Core::System& system, QWidget* parent, QStandardItemModel* list,
|
||||
std::shared_ptr<Network::AnnounceMultiplayerSession> session);
|
||||
~Lobby() override;
|
||||
|
||||
@ -84,7 +88,9 @@ private:
|
||||
*/
|
||||
QString PasswordPrompt();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::Lobby> ui;
|
||||
Core::System& system;
|
||||
|
||||
QStandardItemModel* model{};
|
||||
QStandardItemModel* game_list{};
|
||||
|
@ -17,9 +17,10 @@
|
||||
#include "citra_qt/util/clickable_label.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_list_model,
|
||||
QAction* leave_room, QAction* show_room)
|
||||
: QWidget(parent), game_list_model(game_list_model), leave_room(leave_room),
|
||||
MultiplayerState::MultiplayerState(Core::System& system_, QWidget* parent,
|
||||
QStandardItemModel* game_list_model, QAction* leave_room,
|
||||
QAction* show_room)
|
||||
: QWidget(parent), system{system_}, game_list_model(game_list_model), leave_room(leave_room),
|
||||
show_room(show_room) {
|
||||
if (auto member = Network::GetRoomMember().lock()) {
|
||||
// register the network structs to use in slots and signals
|
||||
@ -203,14 +204,14 @@ static void BringWidgetToFront(QWidget* widget) {
|
||||
|
||||
void MultiplayerState::OnViewLobby() {
|
||||
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);
|
||||
}
|
||||
|
||||
void MultiplayerState::OnCreateRoom() {
|
||||
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);
|
||||
}
|
||||
@ -274,7 +275,7 @@ void MultiplayerState::OnOpenNetworkRoom() {
|
||||
|
||||
void MultiplayerState::OnDirectConnectToRoom() {
|
||||
if (direct_connect == nullptr) {
|
||||
direct_connect = new DirectConnectWindow(this);
|
||||
direct_connect = new DirectConnectWindow(system, this);
|
||||
}
|
||||
BringWidgetToFront(direct_connect);
|
||||
}
|
||||
|
@ -15,12 +15,16 @@ class ClientRoomWindow;
|
||||
class DirectConnectWindow;
|
||||
class ClickableLabel;
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
class MultiplayerState : public QWidget {
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
explicit MultiplayerState(QWidget* parent, QStandardItemModel* game_list, QAction* leave_room,
|
||||
QAction* show_room);
|
||||
explicit MultiplayerState(Core::System& system, QWidget* parent, QStandardItemModel* game_list,
|
||||
QAction* leave_room, QAction* show_room);
|
||||
~MultiplayerState();
|
||||
|
||||
/**
|
||||
@ -71,6 +75,7 @@ signals:
|
||||
void AnnounceFailed(const Common::WebResult&);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
Lobby* lobby = nullptr;
|
||||
HostRoomWindow* host_room = nullptr;
|
||||
ClientRoomWindow* client_room = nullptr;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user