[hle,display,overlay,starter,hid] add overlay functions, starter applet (initially), HID handheld for system applets and fw21 stubs (#3080)
Adds fully functional overlay display. - Enable Overlay Applet via "View" -> "Enable Overlay Display Applet" - Open the overlay by pressing the home button for over 1s - Can adjust volume - Can toggle airplane mode (if on WiFi, maybe if overlay is enabled pretend to be on WiFi?) - Future TODO(?): Adjust Brightness implementation for host system - Inputs are properly registered. e.g. if overlay open, application does not register inputs. You can control volume and airplane mode outside of the emulator window Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3080 Reviewed-by: CamilleLaVey <camillelavey99@gmail.com> Reviewed-by: crueter <crueter@eden-emu.dev> Co-authored-by: Maufeat <sahyno1996@gmail.com> Co-committed-by: Maufeat <sahyno1996@gmail.com>
This commit is contained in:
@@ -72,7 +72,8 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
DEBUG_FLUSH_BY_LINE("flush_line"),
|
||||
USE_LRU_CACHE("use_lru_cache"),
|
||||
|
||||
DONT_SHOW_DRIVER_SHADER_WARNING("dont_show_driver_shader_warning");
|
||||
DONT_SHOW_DRIVER_SHADER_WARNING("dont_show_driver_shader_warning"),
|
||||
ENABLE_OVERLAY("enable_overlay");
|
||||
|
||||
|
||||
// external fun isFrameSkippingEnabled(): Boolean
|
||||
|
||||
@@ -849,7 +849,14 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.airplane_mode_description
|
||||
)
|
||||
)
|
||||
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ENABLE_OVERLAY,
|
||||
titleId = R.string.enable_overlay,
|
||||
descriptionId = R.string.enable_overlay_description
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -491,6 +491,7 @@ class SettingsFragmentPresenter(
|
||||
sl.apply {
|
||||
add(IntSetting.SWKBD_APPLET.key)
|
||||
add(BooleanSetting.AIRPLANE_MODE.key)
|
||||
add(BooleanSetting.ENABLE_OVERLAY.key)
|
||||
}
|
||||
}
|
||||
private fun addInputPlayer(sl: ArrayList<SettingsItem>, playerIndex: Int) {
|
||||
|
||||
@@ -1125,7 +1125,7 @@
|
||||
|
||||
<!-- Applet Modes -->
|
||||
<string name="applets_menu">Applets</string>
|
||||
<string name="applets_menu_description">(WIP) Change applet frontends and settings</string>
|
||||
<string name="applets_menu_description">Change applet frontends and settings</string>
|
||||
|
||||
<string name="applet_hle">Custom Frontend</string>
|
||||
<string name="applet_lle">Real Applet</string>
|
||||
@@ -1135,6 +1135,9 @@
|
||||
<string name="airplane_mode">Airplane Mode</string>
|
||||
<string name="airplane_mode_description">Passes Airplane Mode to the Switch OS</string>
|
||||
|
||||
<string name="enable_overlay">Enable Overlay Applet</string>
|
||||
<string name="enable_overlay_description">Enables Horizon\'s built-in overlay applet. Press and hold the home button for 1 second to show it.</string>
|
||||
|
||||
<!-- Licenses screen strings -->
|
||||
<string name="licenses">Licenses</string>
|
||||
<string name="license_fidelityfx_fsr" translatable="false">FidelityFX-FSR</string>
|
||||
|
||||
@@ -774,6 +774,8 @@ struct Values {
|
||||
|
||||
// Per-game overrides
|
||||
bool use_squashed_iterated_blend;
|
||||
|
||||
Setting<bool> enable_overlay{linkage, true, "enable_overlay", Category::Core};
|
||||
};
|
||||
|
||||
extern Values values;
|
||||
|
||||
@@ -487,6 +487,10 @@ add_library(core STATIC
|
||||
hle/service/am/service/library_applet_self_accessor.h
|
||||
hle/service/am/service/lock_accessor.cpp
|
||||
hle/service/am/service/lock_accessor.h
|
||||
hle/service/am/service/overlay_functions.cpp
|
||||
hle/service/am/service/overlay_functions.h
|
||||
hle/service/am/service/overlay_applet_proxy.cpp
|
||||
hle/service/am/service/overlay_applet_proxy.h
|
||||
hle/service/am/service/process_winding_controller.cpp
|
||||
hle/service/am/service/process_winding_controller.h
|
||||
hle/service/am/service/self_controller.cpp
|
||||
|
||||
@@ -580,6 +580,21 @@ struct System::Impl {
|
||||
gpu_dirty_memory_managers;
|
||||
|
||||
std::deque<std::vector<u8>> user_channel;
|
||||
|
||||
std::mutex general_channel_mutex;
|
||||
std::deque<std::vector<u8>> general_channel;
|
||||
std::unique_ptr<Service::KernelHelpers::ServiceContext> general_channel_context; // lazy
|
||||
std::unique_ptr<Service::Event> general_channel_event; // lazy
|
||||
bool general_channel_initialized{false};
|
||||
|
||||
void EnsureGeneralChannelInitialized(System& system) {
|
||||
if (general_channel_initialized) {
|
||||
return;
|
||||
}
|
||||
general_channel_context = std::make_unique<Service::KernelHelpers::ServiceContext>(system, "GeneralChannel");
|
||||
general_channel_event = std::make_unique<Service::Event>(*general_channel_context);
|
||||
general_channel_initialized = true;
|
||||
}
|
||||
};
|
||||
|
||||
System::System() : impl{std::make_unique<Impl>(*this)} {}
|
||||
@@ -993,6 +1008,39 @@ std::deque<std::vector<u8>>& System::GetUserChannel() {
|
||||
return impl->user_channel;
|
||||
}
|
||||
|
||||
std::deque<std::vector<u8>>& System::GetGeneralChannel() {
|
||||
return impl->general_channel;
|
||||
}
|
||||
|
||||
void System::PushGeneralChannelData(std::vector<u8>&& data) {
|
||||
std::scoped_lock lk{impl->general_channel_mutex};
|
||||
impl->EnsureGeneralChannelInitialized(*this);
|
||||
const bool was_empty = impl->general_channel.empty();
|
||||
impl->general_channel.push_back(std::move(data));
|
||||
if (was_empty) {
|
||||
impl->general_channel_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
bool System::TryPopGeneralChannel(std::vector<u8>& out_data) {
|
||||
std::scoped_lock lk{impl->general_channel_mutex};
|
||||
if (!impl->general_channel_initialized || impl->general_channel.empty()) {
|
||||
return false;
|
||||
}
|
||||
out_data = std::move(impl->general_channel.back());
|
||||
impl->general_channel.pop_back();
|
||||
if (impl->general_channel.empty()) {
|
||||
impl->general_channel_event->Clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Service::Event& System::GetGeneralChannelEvent() {
|
||||
std::scoped_lock lk{impl->general_channel_mutex};
|
||||
impl->EnsureGeneralChannelInitialized(*this);
|
||||
return *impl->general_channel_event;
|
||||
}
|
||||
|
||||
void System::RegisterExitCallback(ExitCallback&& callback) {
|
||||
impl->exit_callback = std::move(callback);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs/vfs_types.h"
|
||||
#include "core/hle/service/os/event.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
@@ -428,6 +430,11 @@ public:
|
||||
*/
|
||||
[[nodiscard]] std::deque<std::vector<u8>>& GetUserChannel();
|
||||
|
||||
[[nodiscard]] std::deque<std::vector<u8>>& GetGeneralChannel();
|
||||
void PushGeneralChannelData(std::vector<u8>&& data);
|
||||
bool TryPopGeneralChannel(std::vector<u8>& out_data);
|
||||
[[nodiscard]] Service::Event& GetGeneralChannelEvent();
|
||||
|
||||
/// Type used for the frontend to designate a callback for System to exit the application.
|
||||
using ExitCallback = std::function<void()>;
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -5,6 +8,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
@@ -95,7 +95,7 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
|
||||
case SaveDataSpaceId::System:
|
||||
return "/system/";
|
||||
case SaveDataSpaceId::User:
|
||||
case SaveDataSpaceId::SdUser:
|
||||
case SaveDataSpaceId::SdUser:
|
||||
return "/user/";
|
||||
case SaveDataSpaceId::Temporary:
|
||||
return "/temp/";
|
||||
|
||||
@@ -215,7 +215,7 @@ public:
|
||||
{220, nullptr, "SynchronizeProfileAsync"},
|
||||
{221, nullptr, "UploadProfileAsync"},
|
||||
{222, nullptr, "SynchronizaProfileAsyncIfSecondsElapsed"},
|
||||
{250, nullptr, "IsLinkedWithNintendoAccount"},
|
||||
{250, &IAdministrator::IsLinkedWithNintendoAccount, "IsLinkedWithNintendoAccount"},
|
||||
{251, nullptr, "CreateProcedureToLinkWithNintendoAccount"},
|
||||
{252, nullptr, "ResumeProcedureToLinkWithNintendoAccount"},
|
||||
{255, nullptr, "CreateProcedureToUpdateLinkageStateOfNintendoAccount"},
|
||||
@@ -236,6 +236,13 @@ public:
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void IsLinkedWithNintendoAccount(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(false);
|
||||
}
|
||||
};
|
||||
|
||||
class IAuthorizationRequest final : public ServiceFramework<IAuthorizationRequest> {
|
||||
@@ -496,6 +503,35 @@ protected:
|
||||
rb.Push(static_cast<u32>(buffer.size()));
|
||||
}
|
||||
|
||||
void LoadIdTokenCache(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
std::vector<u8> token_data(0x100);
|
||||
std::fill(token_data.begin(), token_data.end(), u8(0));
|
||||
|
||||
(void)ctx.WriteBuffer(token_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(static_cast<u32>(token_data.size()));
|
||||
}
|
||||
|
||||
void GetNintendoAccountUserResourceCacheForApplication(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
std::vector<u8> nas_user_base_for_application(0x68);
|
||||
(void)ctx.WriteBuffer(nas_user_base_for_application);
|
||||
|
||||
if (ctx.CanWriteBuffer(1)) {
|
||||
std::vector<u8> unknown_out_buffer(ctx.GetWriteBufferSize(1));
|
||||
(void)ctx.WriteBuffer(unknown_out_buffer, 1);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<u64>(profile_manager.GetLastOpenedUser().Hash());
|
||||
}
|
||||
|
||||
void Store(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto base = rp.PopRaw<ProfileBase>();
|
||||
@@ -707,7 +743,7 @@ private:
|
||||
std::vector<u8> token_data(0x100);
|
||||
std::fill(token_data.begin(), token_data.end(), u8(0));
|
||||
|
||||
ctx.WriteBuffer(token_data, 0);
|
||||
ctx.WriteBuffer(token_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -718,7 +754,7 @@ private:
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
std::vector<u8> nas_user_base_for_application(0x68);
|
||||
ctx.WriteBuffer(nas_user_base_for_application, 0);
|
||||
ctx.WriteBuffer(nas_user_base_for_application);
|
||||
|
||||
if (ctx.CanWriteBuffer(1)) {
|
||||
std::vector<u8> unknown_out_buffer(ctx.GetWriteBufferSize(1));
|
||||
@@ -934,6 +970,7 @@ Result Module::Interface::InitializeApplicationInfoBase() {
|
||||
application_info.application_type = ApplicationType::GameCard;
|
||||
break;
|
||||
case FileSys::StorageId::Host:
|
||||
case FileSys::StorageId::NandSystem:
|
||||
case FileSys::StorageId::NandUser:
|
||||
case FileSys::StorageId::SdCard:
|
||||
case FileSys::StorageId::None: // Yuzu specific, differs from hardware
|
||||
@@ -1054,6 +1091,17 @@ void Module::Interface::GetProfileEditor(HLERequestContext& ctx) {
|
||||
rb.PushIpcInterface<IProfileEditor>(system, user_id, *profile_manager);
|
||||
}
|
||||
|
||||
void Module::Interface::GetBaasAccountAdministrator(HLERequestContext &ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_INFO(Service_ACC, "called, uuid=0x{}", uuid.RawString());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IAdministrator>(system, uuid);
|
||||
}
|
||||
|
||||
void Module::Interface::ListQualifiedUsers(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
void DeleteUser(HLERequestContext& ctx);
|
||||
void SetUserPosition(HLERequestContext& ctx);
|
||||
void GetProfileEditor(HLERequestContext& ctx);
|
||||
void GetBaasAccountAdministrator(HLERequestContext &ctx);
|
||||
void ListQualifiedUsers(HLERequestContext& ctx);
|
||||
void ListOpenContextStoredUsers(HLERequestContext& ctx);
|
||||
void StoreSaveDataThumbnailApplication(HLERequestContext& ctx);
|
||||
|
||||
@@ -55,7 +55,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
|
||||
{211, nullptr, "CreateProcedureToRegisterUserWithNintendoAccount"},
|
||||
{212, nullptr, "ResumeProcedureToRegisterUserWithNintendoAccount"},
|
||||
{230, nullptr, "AuthenticateServiceAsync"},
|
||||
{250, nullptr, "GetBaasAccountAdministrator"},
|
||||
{250, &ACC_SU::GetBaasAccountAdministrator, "GetBaasAccountAdministrator"},
|
||||
{290, nullptr, "ProxyProcedureForGuestLoginWithNintendoAccount"},
|
||||
{291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"},
|
||||
{299, nullptr, "SuspendBackgroundDaemon"},
|
||||
@@ -80,4 +80,4 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
|
||||
|
||||
ACC_SU::~ACC_SU() = default;
|
||||
|
||||
} // namespace Service::Account
|
||||
} // namespace Service::Account
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -15,6 +18,7 @@ class FrontendApplet;
|
||||
enum class AppletType {
|
||||
Application,
|
||||
LibraryApplet,
|
||||
OverlayApplet,
|
||||
SystemApplet,
|
||||
};
|
||||
|
||||
@@ -146,6 +150,7 @@ enum class AppletMessage : u32 {
|
||||
ForceHideApplicationLogo = 57,
|
||||
FloatingApplicationDetected = 60,
|
||||
DetectShortPressingCaptureButton = 90,
|
||||
DetectLongPressingCaptureButton = 91,
|
||||
AlbumScreenShotTaken = 92,
|
||||
AlbumRecordingSaved = 93,
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Service::AM {
|
||||
|
||||
Applet::Applet(Core::System& system, std::unique_ptr<Process> process_, bool is_application)
|
||||
: context(system, "Applet"), lifecycle_manager(system, context, is_application),
|
||||
process(std::move(process_)), hid_registration(system, *process),
|
||||
process(std::move(process_)), hid_registration(system, *process), overlay_event(context),
|
||||
gpu_error_detected_event(context), friend_invitation_storage_channel_event(context),
|
||||
notification_storage_channel_event(context), health_warning_disappeared_system_event(context),
|
||||
unknown_event(context), acquired_sleep_lock_event(context), pop_from_general_channel_event(context),
|
||||
@@ -63,7 +63,15 @@ void Applet::SetInteractibleLocked(bool interactible) {
|
||||
|
||||
is_interactible = interactible;
|
||||
|
||||
hid_registration.EnableAppletToGetInput(interactible && !lifecycle_manager.GetExitRequested());
|
||||
const bool exit_requested = lifecycle_manager.GetExitRequested();
|
||||
const bool input_enabled = interactible && !exit_requested;
|
||||
|
||||
if (applet_id == AppletId::OverlayDisplay || applet_id == AppletId::Application) {
|
||||
LOG_DEBUG(Service_AM, "called, applet={} interactible={} exit_requested={} input_enabled={} overlay_in_foreground={}",
|
||||
static_cast<u32>(applet_id), interactible, exit_requested, input_enabled, overlay_in_foreground);
|
||||
}
|
||||
|
||||
hid_registration.EnableAppletToGetInput(input_enabled);
|
||||
}
|
||||
|
||||
void Applet::OnProcessTerminatedLocked() {
|
||||
|
||||
@@ -122,8 +122,10 @@ struct Applet {
|
||||
bool is_activity_runnable{};
|
||||
bool is_interactible{true};
|
||||
bool window_visible{true};
|
||||
bool overlay_in_foreground{false};
|
||||
|
||||
// Events
|
||||
Event overlay_event;
|
||||
Event gpu_error_detected_event;
|
||||
Event friend_invitation_storage_channel_event;
|
||||
Event notification_storage_channel_event;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -15,6 +18,7 @@
|
||||
#include "core/hle/service/am/service/storage.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "hid_core/hid_types.h"
|
||||
#include "core/hle/service/am/process_creation.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
@@ -262,6 +266,22 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
||||
|
||||
m_cv.wait(lk, [&] { return m_pending_process != nullptr; });
|
||||
|
||||
if (Settings::values.enable_overlay) {
|
||||
if (auto overlay_process = CreateProcess(m_system, static_cast<u64>(AppletProgramId::OverlayDisplay), 0, 0)) {
|
||||
auto overlay_applet = std::make_shared<Applet>(m_system, std::move(overlay_process), false);
|
||||
overlay_applet->program_id = static_cast<u64>(AppletProgramId::OverlayDisplay);
|
||||
overlay_applet->applet_id = AppletId::OverlayDisplay;
|
||||
overlay_applet->type = AppletType::OverlayApplet;
|
||||
overlay_applet->library_applet_mode = LibraryAppletMode::PartialForeground;
|
||||
overlay_applet->window_visible = true;
|
||||
overlay_applet->home_button_short_pressed_blocked = false;
|
||||
overlay_applet->home_button_long_pressed_blocked = false;
|
||||
m_window_system->TrackApplet(overlay_applet, false);
|
||||
overlay_applet->process->Run();
|
||||
LOG_INFO(Service_AM, "called, Overlay applet launched before application (initially hidden, watching home button)");
|
||||
}
|
||||
}
|
||||
|
||||
const auto& params = m_pending_parameters;
|
||||
auto applet = std::make_shared<Applet>(m_system, std::move(m_pending_process),
|
||||
params.applet_id == AppletId::Application);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -46,6 +49,7 @@ public:
|
||||
|
||||
public:
|
||||
void SetWindowSystem(WindowSystem* window_system);
|
||||
[[nodiscard]] WindowSystem* GetWindowSystem() const { return m_window_system; }
|
||||
|
||||
private:
|
||||
Core::System& m_system;
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
#include "core/hle/service/am/button_poller.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "hid_core/frontend/emulated_controller.h"
|
||||
@@ -19,9 +23,9 @@ ButtonPressDuration ClassifyPressDuration(std::chrono::steady_clock::time_point
|
||||
|
||||
// TODO: determine actual thresholds
|
||||
// TODO: these are likely different for each button
|
||||
if (dur < 500ms) {
|
||||
if (dur < 400ms) {
|
||||
return ButtonPressDuration::ShortPressing;
|
||||
} else if (dur < 1000ms) {
|
||||
} else if (dur < 800ms) {
|
||||
return ButtonPressDuration::MiddlePressing;
|
||||
} else {
|
||||
return ButtonPressDuration::LongPressing;
|
||||
@@ -47,14 +51,22 @@ ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system)
|
||||
m_handheld_key = m_handheld->SetCallback(engine_callback);
|
||||
m_player1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||
m_player1_key = m_player1->SetCallback(engine_callback);
|
||||
|
||||
m_thread = std::thread([this] { this->ThreadLoop(); });
|
||||
}
|
||||
|
||||
ButtonPoller::~ButtonPoller() {
|
||||
m_handheld->DeleteCallback(m_handheld_key);
|
||||
m_player1->DeleteCallback(m_player1_key);
|
||||
m_stop = true;
|
||||
m_cv.notify_all();
|
||||
if (m_thread.joinable()) {
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonPoller::OnButtonStateChanged() {
|
||||
std::lock_guard lk{m_mutex};
|
||||
const bool home_button =
|
||||
m_handheld->GetHomeButtons().home.Value() || m_player1->GetHomeButtons().home.Value();
|
||||
const bool capture_button = m_handheld->GetCaptureButtons().capture.Value() ||
|
||||
@@ -63,22 +75,54 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||
// Buttons pressed which were not previously pressed
|
||||
if (home_button && !m_home_button_press_start) {
|
||||
m_home_button_press_start = std::chrono::steady_clock::now();
|
||||
m_home_button_long_sent = false;
|
||||
}
|
||||
if (capture_button && !m_capture_button_press_start) {
|
||||
m_capture_button_press_start = std::chrono::steady_clock::now();
|
||||
m_capture_button_long_sent = false;
|
||||
}
|
||||
// if (power_button && !m_power_button_press_start) {
|
||||
// m_power_button_press_start = std::chrono::steady_clock::now();
|
||||
// }
|
||||
|
||||
// While buttons are held, check whether they have crossed the long-press
|
||||
// threshold and, if so, send the long-press action immediately (only once).
|
||||
if (home_button && m_home_button_press_start && !m_home_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_home_button_press_start);
|
||||
if (duration != ButtonPressDuration::ShortPressing) {
|
||||
m_window_system.OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing);
|
||||
m_home_button_long_sent = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (capture_button && m_capture_button_press_start && !m_capture_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
|
||||
if (duration != ButtonPressDuration::ShortPressing) {
|
||||
m_window_system.OnSystemButtonPress(SystemButtonType::CaptureButtonLongPressing);
|
||||
m_capture_button_long_sent = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Buttons released which were previously held
|
||||
if (!home_button && m_home_button_press_start) {
|
||||
m_window_system.OnHomeButtonPressed(ClassifyPressDuration(*m_home_button_press_start));
|
||||
if(!m_home_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_home_button_press_start);
|
||||
m_window_system.OnSystemButtonPress(
|
||||
duration == ButtonPressDuration::ShortPressing ? SystemButtonType::HomeButtonShortPressing
|
||||
: SystemButtonType::HomeButtonLongPressing);
|
||||
}
|
||||
m_home_button_press_start = std::nullopt;
|
||||
m_home_button_long_sent = false;
|
||||
}
|
||||
if (!capture_button && m_capture_button_press_start) {
|
||||
// TODO
|
||||
if (!m_capture_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
|
||||
m_window_system.OnSystemButtonPress(
|
||||
duration == ButtonPressDuration::ShortPressing ? SystemButtonType::CaptureButtonShortPressing
|
||||
: SystemButtonType::CaptureButtonLongPressing);
|
||||
}
|
||||
m_capture_button_press_start = std::nullopt;
|
||||
m_capture_button_long_sent = false;
|
||||
}
|
||||
// if (!power_button && m_power_button_press_start) {
|
||||
// // TODO
|
||||
@@ -86,4 +130,16 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||
// }
|
||||
}
|
||||
|
||||
void ButtonPoller::ThreadLoop() {
|
||||
using namespace std::chrono_literals;
|
||||
std::unique_lock lk{m_mutex};
|
||||
while (!m_stop) {
|
||||
m_cv.wait_for(lk, 50ms);
|
||||
if (m_stop) break;
|
||||
lk.unlock();
|
||||
OnButtonStateChanged();
|
||||
lk.lock();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include "hid_core/frontend/emulated_controller.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -26,6 +33,7 @@ public:
|
||||
|
||||
private:
|
||||
void OnButtonStateChanged();
|
||||
void ThreadLoop();
|
||||
|
||||
private:
|
||||
WindowSystem& m_window_system;
|
||||
@@ -38,6 +46,15 @@ private:
|
||||
std::optional<std::chrono::steady_clock::time_point> m_home_button_press_start{};
|
||||
std::optional<std::chrono::steady_clock::time_point> m_capture_button_press_start{};
|
||||
std::optional<std::chrono::steady_clock::time_point> m_power_button_press_start{};
|
||||
|
||||
bool m_home_button_long_sent{};
|
||||
bool m_capture_button_long_sent{};
|
||||
bool m_power_button_long_sent{};
|
||||
|
||||
std::thread m_thread;
|
||||
std::atomic<bool> m_stop{false};
|
||||
std::condition_variable m_cv;
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -33,6 +36,10 @@ void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* pro
|
||||
m_buffer_sharing_enabled = false;
|
||||
m_blending_enabled = mode == LibraryAppletMode::PartialForeground ||
|
||||
mode == LibraryAppletMode::PartialForegroundIndirectDisplay;
|
||||
|
||||
if (m_applet_id != AppletId::Application) {
|
||||
(void)this->IsSystemBufferSharingEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayLayerManager::Finalize() {
|
||||
@@ -69,6 +76,18 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
|
||||
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
|
||||
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
|
||||
|
||||
if (m_applet_id != AppletId::Application) {
|
||||
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
|
||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||
static constexpr s32 kOverlayBackgroundZ = -1;
|
||||
(void)m_manager_display_service->SetLayerZIndex(kOverlayBackgroundZ, *out_layer_id);
|
||||
} else {
|
||||
static constexpr s32 kOverlayZ = 3;
|
||||
(void)m_manager_display_service->SetLayerZIndex(kOverlayZ, *out_layer_id);
|
||||
}
|
||||
}
|
||||
|
||||
m_managed_display_layers.emplace(*out_layer_id);
|
||||
|
||||
R_SUCCEED();
|
||||
@@ -105,7 +124,15 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
|
||||
|
||||
// We succeeded, so set up remaining state.
|
||||
m_buffer_sharing_enabled = true;
|
||||
|
||||
// Ensure the overlay layer is visible
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
||||
m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
|
||||
s32 initial_z = 1;
|
||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||
initial_z = -1;
|
||||
}
|
||||
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -128,10 +155,14 @@ void DisplayLayerManager::SetWindowVisibility(bool visible) {
|
||||
|
||||
if (m_manager_display_service) {
|
||||
if (m_system_shared_layer_id) {
|
||||
LOG_INFO(Service_VI, "shared_layer={} visible={} applet_id={}",
|
||||
m_system_shared_layer_id, m_visible, static_cast<u32>(m_applet_id));
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
||||
}
|
||||
|
||||
for (const auto layer_id : m_managed_display_layers) {
|
||||
LOG_INFO(Service_VI, "managed_layer={} visible={} applet_id={}",
|
||||
layer_id, m_visible, static_cast<u32>(m_applet_id));
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, layer_id);
|
||||
}
|
||||
}
|
||||
@@ -141,6 +172,22 @@ bool DisplayLayerManager::GetWindowVisibility() const {
|
||||
return m_visible;
|
||||
}
|
||||
|
||||
void DisplayLayerManager::SetOverlayZIndex(s32 z_index) {
|
||||
if (!m_manager_display_service) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_system_shared_layer_id) {
|
||||
m_manager_display_service->SetLayerZIndex(z_index, m_system_shared_layer_id);
|
||||
LOG_INFO(Service_VI, "called, shared_layer={} z={}", m_system_shared_layer_id, z_index);
|
||||
}
|
||||
|
||||
for (const auto layer_id : m_managed_display_layers) {
|
||||
m_manager_display_service->SetLayerZIndex(z_index, layer_id);
|
||||
LOG_INFO(Service_VI, "called, managed_layer={} z={}", layer_id, z_index);
|
||||
}
|
||||
}
|
||||
|
||||
Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written,
|
||||
s32* out_fbshare_layer_index) {
|
||||
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -43,6 +46,8 @@ public:
|
||||
void SetWindowVisibility(bool visible);
|
||||
bool GetWindowVisibility() const;
|
||||
|
||||
void SetOverlayZIndex(s32 z_index);
|
||||
|
||||
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index);
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -7,6 +10,7 @@
|
||||
#include "core/hle/service/am/service/application_proxy.h"
|
||||
#include "core/hle/service/am/service/library_applet_proxy.h"
|
||||
#include "core/hle/service/am/service/system_applet_proxy.h"
|
||||
#include "core/hle/service/am/service/overlay_applet_proxy.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
@@ -21,7 +25,7 @@ IAllSystemAppletProxiesService::IAllSystemAppletProxiesService(Core::System& sys
|
||||
{110, D<&IAllSystemAppletProxiesService::OpenSystemAppletProxy>, "OpenSystemAppletProxyEx"},
|
||||
{200, D<&IAllSystemAppletProxiesService::OpenLibraryAppletProxyOld>, "OpenLibraryAppletProxyOld"},
|
||||
{201, D<&IAllSystemAppletProxiesService::OpenLibraryAppletProxy>, "OpenLibraryAppletProxy"},
|
||||
{300, nullptr, "OpenOverlayAppletProxy"},
|
||||
{300, D<&IAllSystemAppletProxiesService::OpenOverlayAppletProxy>, "OpenOverlayAppletProxy"},
|
||||
{350, D<&IAllSystemAppletProxiesService::OpenSystemApplicationProxy>, "OpenSystemApplicationProxy"},
|
||||
{400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
|
||||
{410, nullptr, "GetSystemAppletControllerForDebug"},
|
||||
@@ -67,6 +71,22 @@ Result IAllSystemAppletProxiesService::OpenLibraryAppletProxy(
|
||||
}
|
||||
}
|
||||
|
||||
Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy(
|
||||
Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy, ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute) {
|
||||
LOG_WARNING(Service_AM, "called");
|
||||
|
||||
if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
|
||||
*out_overlay_applet_proxy = std::make_shared<IOverlayAppletProxy>(
|
||||
system, applet, process_handle.Get(), m_window_system);
|
||||
R_SUCCEED();
|
||||
} else {
|
||||
UNIMPLEMENTED();
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
}
|
||||
|
||||
Result IAllSystemAppletProxiesService::OpenSystemApplicationProxy(
|
||||
Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -5,6 +8,7 @@
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/am/service/overlay_applet_proxy.h"
|
||||
|
||||
namespace Service {
|
||||
|
||||
@@ -31,6 +35,9 @@ private:
|
||||
ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
|
||||
Result OpenOverlayAppletProxy(Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy,
|
||||
ClientProcessId pid, InCopyHandle<Kernel::KProcess> process_handle,
|
||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
|
||||
Result OpenLibraryAppletProxyOld(
|
||||
Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,7 +21,7 @@ IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_,
|
||||
{20, nullptr, "PushToAppletBoundChannel"},
|
||||
{21, nullptr, "TryPopFromAppletBoundChannel"},
|
||||
{40, nullptr, "GetDisplayLogicalResolution"},
|
||||
{42, nullptr, "SetDisplayMagnification"},
|
||||
{42, D<&IAppletCommonFunctions::SetDisplayMagnification>, "SetDisplayMagnification"},
|
||||
{50, D<&IAppletCommonFunctions::SetHomeButtonDoubleClickEnabled>, "SetHomeButtonDoubleClickEnabled"},
|
||||
{51, D<&IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled>, "GetHomeButtonDoubleClickEnabled"},
|
||||
{52, nullptr, "IsHomeButtonShortPressedBlocked"},
|
||||
@@ -58,6 +61,14 @@ Result IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAppletCommonFunctions::SetDisplayMagnification(f32 x, f32 y, f32 width, f32 height) {
|
||||
LOG_DEBUG(Service_AM, "(STUBBED) called, x={}, y={}, width={}, height={}", x, y, width,
|
||||
height);
|
||||
std::scoped_lock lk{applet->lock};
|
||||
applet->display_magnification = Common::Rectangle<f32>{x, y, x + width, y + height};
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAppletCommonFunctions::SetCpuBoostRequestPriority(s32 priority) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
std::scoped_lock lk{applet->lock};
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,6 +21,7 @@ public:
|
||||
private:
|
||||
Result SetHomeButtonDoubleClickEnabled(bool home_button_double_click_enabled);
|
||||
Result GetHomeButtonDoubleClickEnabled(Out<bool> out_home_button_double_click_enabled);
|
||||
Result SetDisplayMagnification(f32 x, f32 y, f32 width, f32 height);
|
||||
Result SetCpuBoostRequestPriority(s32 priority);
|
||||
Result GetCurrentApplicationId(Out<u64> out_application_id);
|
||||
Result Unknown350(Out<u16> out_unknown);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -56,7 +59,7 @@ IApplicationCreator::IApplicationCreator(Core::System& system_, WindowSystem& wi
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IApplicationCreator::CreateApplication>, "CreateApplication"},
|
||||
{1, nullptr, "PopLaunchRequestedApplication"},
|
||||
{10, nullptr, "CreateSystemApplication"},
|
||||
{10, D<&IApplicationCreator::CreateSystemApplication>, "CreateSystemApplication"},
|
||||
{100, nullptr, "PopFloatingApplicationForDevelopment"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -73,4 +76,34 @@ Result IApplicationCreator::CreateApplication(
|
||||
CreateGuestApplication(out_application_accessor, system, m_window_system, application_id));
|
||||
}
|
||||
|
||||
Result IApplicationCreator::CreateSystemApplication(
|
||||
Out<SharedPointer<IApplicationAccessor>> out_application_accessor, u64 application_id) {
|
||||
|
||||
FileSys::VirtualFile nca_raw{};
|
||||
|
||||
auto& storage = system.GetContentProviderUnion();
|
||||
nca_raw = storage.GetEntryRaw(application_id, FileSys::ContentRecordType::Program);
|
||||
|
||||
R_UNLESS(nca_raw != nullptr, ResultUnknown);
|
||||
|
||||
std::vector<u8> control;
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
|
||||
auto process =
|
||||
CreateProcess(system, application_id, 1, 21);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
applet->program_id = application_id;
|
||||
applet->applet_id = AppletId::Starter;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
applet->library_applet_mode = LibraryAppletMode::AllForeground;
|
||||
|
||||
m_window_system.TrackApplet(applet, true);
|
||||
|
||||
*out_application_accessor =
|
||||
std::make_shared<IApplicationAccessor>(system, applet, m_window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -19,6 +22,7 @@ public:
|
||||
|
||||
private:
|
||||
Result CreateApplication(Out<SharedPointer<IApplicationAccessor>>, u64 application_id);
|
||||
Result CreateSystemApplication(Out<SharedPointer<IApplicationAccessor>>, u64 application_id);
|
||||
|
||||
WindowSystem& m_window_system;
|
||||
};
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/service/common_state_getter.h"
|
||||
#include "core/hle/service/am/service/lock_accessor.h"
|
||||
#include "core/hle/service/am/service/storage.h"
|
||||
#include "core/hle/service/apm/apm_interface.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/pm/pm.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/hle/service/vi/vi_types.h"
|
||||
|
||||
namespace Service::AM {
|
||||
@@ -37,7 +37,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
|
||||
{12, D<&ICommonStateGetter::ReleaseSleepLockTransiently>, "ReleaseSleepLockTransiently"},
|
||||
{13, D<&ICommonStateGetter::GetAcquiredSleepLockEvent>, "GetAcquiredSleepLockEvent"},
|
||||
{14, nullptr, "GetWakeupCount"},
|
||||
{20, nullptr, "PushToGeneralChannel"},
|
||||
{20, D<&ICommonStateGetter::PushToGeneralChannel>, "PushToGeneralChannel"},
|
||||
{30, nullptr, "GetHomeButtonReaderLockAccessor"},
|
||||
{31, D<&ICommonStateGetter::GetReaderLockAccessorEx>, "GetReaderLockAccessorEx"},
|
||||
{32, D<&ICommonStateGetter::GetWriterLockAccessorEx>, "GetWriterLockAccessorEx"},
|
||||
@@ -61,7 +61,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
|
||||
{80, D<&ICommonStateGetter::PerformSystemButtonPressingIfInFocus>, "PerformSystemButtonPressingIfInFocus"},
|
||||
{90, nullptr, "SetPerformanceConfigurationChangedNotification"},
|
||||
{91, nullptr, "GetCurrentPerformanceConfiguration"},
|
||||
{100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"},
|
||||
{100, D<&ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
|
||||
{110, nullptr, "OpenMyGpuErrorHandler"},
|
||||
{120, D<&ICommonStateGetter::GetAppletLaunchedHistory>, "GetAppletLaunchedHistory"},
|
||||
{200, D<&ICommonStateGetter::GetOperationModeSystemInfo>, "GetOperationModeSystemInfo"},
|
||||
@@ -95,6 +95,9 @@ Result ICommonStateGetter::ReceiveMessage(Out<AppletMessage> out_applet_message)
|
||||
R_THROW(AM::ResultNoMessages);
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, returning message={} to applet_id={}",
|
||||
static_cast<u32>(*out_applet_message), static_cast<u32>(m_applet->applet_id));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -262,7 +265,40 @@ Result ICommonStateGetter::GetBuiltInDisplayType(Out<s32> out_display_type) {
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::PerformSystemButtonPressingIfInFocus(SystemButtonType type) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called, type={}", type);
|
||||
LOG_DEBUG(Service_AM, "called, type={}", type);
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
|
||||
switch (type) {
|
||||
case SystemButtonType::HomeButtonShortPressing:
|
||||
if (!m_applet->home_button_short_pressed_blocked) {
|
||||
m_applet->lifecycle_manager.PushUnorderedMessage(
|
||||
AppletMessage::DetectShortPressingHomeButton);
|
||||
}
|
||||
break;
|
||||
case SystemButtonType::HomeButtonLongPressing:
|
||||
if (!m_applet->home_button_long_pressed_blocked) {
|
||||
m_applet->lifecycle_manager.PushUnorderedMessage(
|
||||
AppletMessage::DetectLongPressingHomeButton);
|
||||
}
|
||||
break;
|
||||
case SystemButtonType::CaptureButtonShortPressing:
|
||||
if (m_applet->handling_capture_button_short_pressed_message_enabled_for_applet) {
|
||||
m_applet->lifecycle_manager.PushUnorderedMessage(
|
||||
AppletMessage::DetectShortPressingCaptureButton);
|
||||
}
|
||||
break;
|
||||
case SystemButtonType::CaptureButtonLongPressing:
|
||||
if (m_applet->handling_capture_button_long_pressed_message_enabled_for_applet) {
|
||||
m_applet->lifecycle_manager.PushUnorderedMessage(
|
||||
AppletMessage::DetectLongPressingCaptureButton);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Other buttons ignored for now
|
||||
break;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -304,4 +340,18 @@ Result ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnab
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::PushToGeneralChannel(SharedPointer<IStorage> storage) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
system.PushGeneralChannelData(storage->GetData());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled) {
|
||||
LOG_DEBUG(Service_AM, "called, enabled={} applet_id={}", enabled, m_applet->applet_id);
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->home_button_short_pressed_blocked = !enabled;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace Service::AM {
|
||||
|
||||
struct Applet;
|
||||
class ILockAccessor;
|
||||
class IStorage;
|
||||
|
||||
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
|
||||
public:
|
||||
@@ -60,6 +61,8 @@ private:
|
||||
OutArray<AppletId, BufferAttr_HipcMapAlias> out_applet_ids);
|
||||
Result GetSettingsPlatformRegion(Out<Set::PlatformRegion> out_settings_platform_region);
|
||||
Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled();
|
||||
Result PushToGeneralChannel(SharedPointer<IStorage> storage); // cmd 20
|
||||
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
|
||||
|
||||
void SetCpuBoostMode(HLERequestContext& ctx);
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -6,20 +9,21 @@
|
||||
#include "core/hle/service/am/service/home_menu_functions.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/am/am_results.h"
|
||||
#include "core/hle/service/am/service/storage.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
WindowSystem& window_system)
|
||||
: ServiceFramework{system_, "IHomeMenuFunctions"}, m_window_system{window_system},
|
||||
m_applet{std::move(applet)}, m_context{system, "IHomeMenuFunctions"},
|
||||
m_pop_from_general_channel_event{m_context} {
|
||||
m_applet{std::move(applet)}, m_context{system, "IHomeMenuFunctions"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{10, D<&IHomeMenuFunctions::RequestToGetForeground>, "RequestToGetForeground"},
|
||||
{11, D<&IHomeMenuFunctions::LockForeground>, "LockForeground"},
|
||||
{12, D<&IHomeMenuFunctions::UnlockForeground>, "UnlockForeground"},
|
||||
{20, nullptr, "PopFromGeneralChannel"},
|
||||
{20, D<&IHomeMenuFunctions::PopFromGeneralChannel>, "PopFromGeneralChannel"},
|
||||
{21, D<&IHomeMenuFunctions::GetPopFromGeneralChannelEvent>, "GetPopFromGeneralChannelEvent"},
|
||||
{30, nullptr, "GetHomeButtonWriterLockAccessor"},
|
||||
{31, nullptr, "GetWriterLockAccessorEx"},
|
||||
@@ -57,10 +61,22 @@ Result IHomeMenuFunctions::UnlockForeground() {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHomeMenuFunctions::PopFromGeneralChannel(Out<SharedPointer<IStorage>> out_storage) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
std::vector<u8> data;
|
||||
if (!system.TryPopGeneralChannel(data)) {
|
||||
R_THROW(AM::ResultNoDataInChannel);
|
||||
}
|
||||
|
||||
*out_storage = std::make_shared<IStorage>(system, std::move(data));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHomeMenuFunctions::GetPopFromGeneralChannelEvent(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
*out_event = m_pop_from_general_channel_event.GetHandle();
|
||||
*out_event = system.GetGeneralChannelEvent().GetHandle();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -12,6 +15,7 @@ namespace Service::AM {
|
||||
|
||||
struct Applet;
|
||||
class WindowSystem;
|
||||
class IStorage;
|
||||
|
||||
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
|
||||
public:
|
||||
@@ -23,6 +27,7 @@ private:
|
||||
Result RequestToGetForeground();
|
||||
Result LockForeground();
|
||||
Result UnlockForeground();
|
||||
Result PopFromGeneralChannel(Out<SharedPointer<IStorage>> out_storage);
|
||||
Result GetPopFromGeneralChannelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result IsRebootEnabled(Out<bool> out_is_reboot_enbaled);
|
||||
Result IsForceTerminateApplicationDisabledForDebug(
|
||||
@@ -31,7 +36,6 @@ private:
|
||||
WindowSystem& m_window_system;
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
KernelHelpers::ServiceContext m_context;
|
||||
Event m_pop_from_general_channel_event;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
120
src/core/hle/service/am/service/overlay_applet_proxy.cpp
Normal file
120
src/core/hle/service/am/service/overlay_applet_proxy.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/hle/service/am/service/common_state_getter.h"
|
||||
#include "core/hle/service/am/service/display_controller.h"
|
||||
#include "core/hle/service/am/service/global_state_controller.h"
|
||||
#include "core/hle/service/am/service/audio_controller.h"
|
||||
#include "core/hle/service/am/service/applet_common_functions.h"
|
||||
#include "core/hle/service/am/service/overlay_applet_proxy.h"
|
||||
#include "core/hle/service/am/service/library_applet_creator.h"
|
||||
#include "core/hle/service/am/service/process_winding_controller.h"
|
||||
#include "core/hle/service/am/service/self_controller.h"
|
||||
#include "core/hle/service/am/service/window_controller.h"
|
||||
#include "core/hle/service/am/service/debug_functions.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::AM {
|
||||
IOverlayAppletProxy::IOverlayAppletProxy(Core::System &system_, std::shared_ptr<Applet> applet,
|
||||
Kernel::KProcess *process, WindowSystem &window_system)
|
||||
: ServiceFramework{system_, "IOverlayAppletProxy"},
|
||||
m_window_system{window_system}, m_process{process}, m_applet{std::move(applet)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IOverlayAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"},
|
||||
{1, D<&IOverlayAppletProxy::GetSelfController>, "GetSelfController"},
|
||||
{2, D<&IOverlayAppletProxy::GetWindowController>, "GetWindowController"},
|
||||
{3, D<&IOverlayAppletProxy::GetAudioController>, "GetAudioController"},
|
||||
{4, D<&IOverlayAppletProxy::GetDisplayController>, "GetDisplayController"},
|
||||
{10, D<&IOverlayAppletProxy::GetProcessWindingController>, "GetProcessWindingController"},
|
||||
{11, D<&IOverlayAppletProxy::GetLibraryAppletCreator>, "GetLibraryAppletCreator"},
|
||||
{20, D<&IOverlayAppletProxy::GetOverlayFunctions>, "GetOverlayFunctions"},
|
||||
{21, D<&IOverlayAppletProxy::GetAppletCommonFunctions>, "GetAppletCommonFunctions"},
|
||||
{23, D<&IOverlayAppletProxy::GetGlobalStateController>, "GetGlobalStateController"},
|
||||
{1000, D<&IOverlayAppletProxy::GetDebugFunctions>, "GetDebugFunctions"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IOverlayAppletProxy::~IOverlayAppletProxy() = default;
|
||||
|
||||
Result IOverlayAppletProxy::GetCommonStateGetter(
|
||||
Out<SharedPointer<ICommonStateGetter> > out_common_state_getter) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_common_state_getter = std::make_shared<ICommonStateGetter>(system, m_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetSelfController(
|
||||
Out<SharedPointer<ISelfController> > out_self_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_self_controller = std::make_shared<ISelfController>(system, m_applet, m_process);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetWindowController(
|
||||
Out<SharedPointer<IWindowController> > out_window_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_window_controller = std::make_shared<IWindowController>(system, m_applet, m_window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetAudioController(
|
||||
Out<SharedPointer<IAudioController> > out_audio_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_audio_controller = std::make_shared<IAudioController>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetDisplayController(
|
||||
Out<SharedPointer<IDisplayController> > out_display_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_display_controller = std::make_shared<IDisplayController>(system, m_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetProcessWindingController(
|
||||
Out<SharedPointer<IProcessWindingController> > out_process_winding_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_process_winding_controller = std::make_shared<IProcessWindingController>(system, m_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetLibraryAppletCreator(
|
||||
Out<SharedPointer<ILibraryAppletCreator> > out_library_applet_creator) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_library_applet_creator =
|
||||
std::make_shared<ILibraryAppletCreator>(system, m_applet, m_window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetOverlayFunctions(
|
||||
Out<SharedPointer<IOverlayFunctions> > out_overlay_functions) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_overlay_functions = std::make_shared<IOverlayFunctions>(system, m_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetAppletCommonFunctions(
|
||||
Out<SharedPointer<IAppletCommonFunctions> > out_applet_common_functions) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_applet_common_functions = std::make_shared<IAppletCommonFunctions>(system, m_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetGlobalStateController(
|
||||
Out<SharedPointer<IGlobalStateController> > out_global_state_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_global_state_controller = std::make_shared<IGlobalStateController>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetDebugFunctions(
|
||||
Out<SharedPointer<IDebugFunctions> > out_debug_functions) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_debug_functions = std::make_shared<IDebugFunctions>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
} // namespace Service::AM
|
||||
55
src/core/hle/service/am/service/overlay_applet_proxy.h
Normal file
55
src/core/hle/service/am/service/overlay_applet_proxy.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/am/service/overlay_functions.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
struct Applet;
|
||||
class IAppletCommonFunctions;
|
||||
class IAudioController;
|
||||
class ICommonStateGetter;
|
||||
class IDebugFunctions;
|
||||
class IDisplayController;
|
||||
class IHomeMenuFunctions;
|
||||
class IGlobalStateController;
|
||||
class ILibraryAppletCreator;
|
||||
class ILibraryAppletSelfAccessor;
|
||||
class IProcessWindingController;
|
||||
class ISelfController;
|
||||
class IWindowController;
|
||||
class WindowSystem;
|
||||
|
||||
class IOverlayAppletProxy final : public ServiceFramework<IOverlayAppletProxy> {
|
||||
public:
|
||||
explicit IOverlayAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
Kernel::KProcess* process, WindowSystem& window_system);
|
||||
~IOverlayAppletProxy();
|
||||
|
||||
private:
|
||||
Result GetCommonStateGetter(Out<SharedPointer<ICommonStateGetter>> out_common_state_getter);
|
||||
Result GetSelfController(Out<SharedPointer<ISelfController>> out_self_controller);
|
||||
Result GetWindowController(Out<SharedPointer<IWindowController>> out_window_controller);
|
||||
Result GetAudioController(Out<SharedPointer<IAudioController>> out_audio_controller);
|
||||
Result GetDisplayController(Out<SharedPointer<IDisplayController>> out_display_controller);
|
||||
Result GetProcessWindingController(
|
||||
Out<SharedPointer<IProcessWindingController>> out_process_winding_controller);
|
||||
Result GetLibraryAppletCreator(
|
||||
Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator);
|
||||
Result GetOverlayFunctions(Out<SharedPointer<IOverlayFunctions>> out_overlay_functions);
|
||||
Result GetAppletCommonFunctions(
|
||||
Out<SharedPointer<IAppletCommonFunctions>> out_applet_common_functions);
|
||||
Result GetGlobalStateController(
|
||||
Out<SharedPointer<IGlobalStateController>> out_global_state_controller);
|
||||
Result GetDebugFunctions(Out<SharedPointer<IDebugFunctions>> out_debug_functions);
|
||||
|
||||
WindowSystem& m_window_system;
|
||||
Kernel::KProcess* const m_process;
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
100
src/core/hle/service/am/service/overlay_functions.cpp
Normal file
100
src/core/hle/service/am/service/overlay_functions.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/service/overlay_functions.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
|
||||
namespace Service::AM {
|
||||
IOverlayFunctions::IOverlayFunctions(Core::System &system_, std::shared_ptr<Applet> applet)
|
||||
: ServiceFramework{system_, "IOverlayFunctions"}, m_applet{std::move(applet)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IOverlayFunctions::BeginToWatchShortHomeButtonMessage>, "BeginToWatchShortHomeButtonMessage"},
|
||||
{1, D<&IOverlayFunctions::EndToWatchShortHomeButtonMessage>, "EndToWatchShortHomeButtonMessage"},
|
||||
{2, D<&IOverlayFunctions::GetApplicationIdForLogo>, "GetApplicationIdForLogo"},
|
||||
{3, nullptr, "SetGpuTimeSliceBoost"},
|
||||
{4, D<&IOverlayFunctions::SetAutoSleepTimeAndDimmingTimeEnabled>, "SetAutoSleepTimeAndDimmingTimeEnabled"},
|
||||
{5, nullptr, "TerminateApplicationAndSetReason"},
|
||||
{6, nullptr, "SetScreenShotPermissionGlobally"},
|
||||
{10, nullptr, "StartShutdownSequenceForOverlay"},
|
||||
{11, nullptr, "StartRebootSequenceForOverlay"},
|
||||
{20, D<&IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
|
||||
{21, nullptr, "SetHandlingTouchScreenInputEnabled"},
|
||||
{30, nullptr, "SetHealthWarningShowingState"},
|
||||
{31, nullptr, "IsHealthWarningRequired"},
|
||||
{40, nullptr, "GetApplicationNintendoLogo"},
|
||||
{41, nullptr, "GetApplicationStartupMovie"},
|
||||
{50, nullptr, "SetGpuTimeSliceBoostForApplication"},
|
||||
{60, nullptr, "Unknown60"},
|
||||
{70, D<&IOverlayFunctions::Unknown70>, "Unknown70"},
|
||||
{90, nullptr, "SetRequiresGpuResourceUse"},
|
||||
{101, nullptr, "BeginToObserveHidInputForDevelop"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IOverlayFunctions::~IOverlayFunctions() = default;
|
||||
|
||||
Result IOverlayFunctions::BeginToWatchShortHomeButtonMessage() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
m_applet->overlay_in_foreground = true;
|
||||
m_applet->home_button_short_pressed_blocked = false;
|
||||
|
||||
if (auto *window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
window_system->RequestUpdate();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::EndToWatchShortHomeButtonMessage() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
m_applet->overlay_in_foreground = false;
|
||||
m_applet->home_button_short_pressed_blocked = false;
|
||||
|
||||
if (auto *window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
window_system->RequestUpdate();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::GetApplicationIdForLogo(Out<u64> out_application_id) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
// Prefer explicit application_id if available, else fall back to program_id
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
u64 id = m_applet->screen_shot_identity.application_id;
|
||||
if (id == 0) {
|
||||
id = m_applet->program_id;
|
||||
}
|
||||
*out_application_id = id;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called, enabled={}", enabled);
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->auto_sleep_disabled = !enabled;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled(bool enabled) {
|
||||
LOG_DEBUG(Service_AM, "called, enabled={}", enabled);
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->home_button_short_pressed_blocked = !enabled;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::Unknown70() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
R_SUCCEED();
|
||||
}
|
||||
} // namespace Service::AM
|
||||
27
src/core/hle/service/am/service/overlay_functions.h
Normal file
27
src/core/hle/service/am/service/overlay_functions.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::AM {
|
||||
struct Applet;
|
||||
|
||||
class IOverlayFunctions final : public ServiceFramework<IOverlayFunctions> {
|
||||
public:
|
||||
explicit IOverlayFunctions(Core::System &system_, std::shared_ptr<Applet> applet);
|
||||
~IOverlayFunctions() override;
|
||||
|
||||
private:
|
||||
Result BeginToWatchShortHomeButtonMessage();
|
||||
Result EndToWatchShortHomeButtonMessage();
|
||||
Result GetApplicationIdForLogo(Out<u64> out_application_id);
|
||||
Result SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled);
|
||||
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
|
||||
Result Unknown70();
|
||||
|
||||
private:
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
};
|
||||
} // namespace Service::AM
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -30,14 +33,14 @@ public:
|
||||
~ISystemAppletProxy();
|
||||
|
||||
private:
|
||||
Result GetCommonStateGetter(Out<SharedPointer<ICommonStateGetter>> out_common_state_getter);
|
||||
Result GetSelfController(Out<SharedPointer<ISelfController>> out_self_controller);
|
||||
Result GetWindowController(Out<SharedPointer<IWindowController>> out_window_controller);
|
||||
Result GetAudioController(Out<SharedPointer<IAudioController>> out_audio_controller);
|
||||
Result GetDisplayController(Out<SharedPointer<IDisplayController>> out_display_controller);
|
||||
Result GetProcessWindingController(
|
||||
Out<SharedPointer<IProcessWindingController>> out_process_winding_controller);
|
||||
Result GetDebugFunctions(Out<SharedPointer<IDebugFunctions>> out_debug_functions);
|
||||
Result GetWindowController(Out<SharedPointer<IWindowController>> out_window_controller);
|
||||
Result GetSelfController(Out<SharedPointer<ISelfController>> out_self_controller);
|
||||
Result GetCommonStateGetter(Out<SharedPointer<ICommonStateGetter>> out_common_state_getter);
|
||||
Result GetLibraryAppletCreator(
|
||||
Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator);
|
||||
Result GetApplicationCreator(Out<SharedPointer<IApplicationCreator>> out_application_creator);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -21,9 +24,18 @@ void WindowSystem::SetEventObserver(EventObserver* observer) {
|
||||
m_system.GetAppletManager().SetWindowSystem(this);
|
||||
}
|
||||
|
||||
void WindowSystem::RequestUpdate() {
|
||||
if (m_event_observer) {
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void WindowSystem::Update() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, home_menu={} application={} overlay={}",
|
||||
m_home_menu != nullptr, m_application != nullptr, m_overlay_display != nullptr);
|
||||
|
||||
// Loop through all applets and remove terminated applets.
|
||||
this->PruneTerminatedAppletsLocked();
|
||||
|
||||
@@ -32,9 +44,16 @@ void WindowSystem::Update() {
|
||||
return;
|
||||
}
|
||||
|
||||
bool overlay_blocks_input = false;
|
||||
if (m_overlay_display) {
|
||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
||||
overlay_blocks_input = m_overlay_display->overlay_in_foreground;
|
||||
}
|
||||
|
||||
// Recursively update each applet root.
|
||||
this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu);
|
||||
this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application);
|
||||
this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu, overlay_blocks_input);
|
||||
this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application, overlay_blocks_input);
|
||||
this->UpdateAppletStateLocked(m_overlay_display, true, false); // overlay is always updated, never blocked
|
||||
}
|
||||
|
||||
void WindowSystem::TrackApplet(std::shared_ptr<Applet> applet, bool is_application) {
|
||||
@@ -43,6 +62,8 @@ void WindowSystem::TrackApplet(std::shared_ptr<Applet> applet, bool is_applicati
|
||||
if (applet->applet_id == AppletId::QLaunch) {
|
||||
ASSERT(m_home_menu == nullptr);
|
||||
m_home_menu = applet.get();
|
||||
} else if (applet->applet_id == AppletId::OverlayDisplay) {
|
||||
m_overlay_display = applet.get();
|
||||
} else if (is_application) {
|
||||
ASSERT(m_application == nullptr);
|
||||
m_application = applet.get();
|
||||
@@ -136,21 +157,66 @@ void WindowSystem::OnExitRequested() {
|
||||
}
|
||||
}
|
||||
|
||||
void WindowSystem::OnHomeButtonPressed(ButtonPressDuration type) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
// If we don't have a home menu, nothing to do.
|
||||
if (!m_home_menu) {
|
||||
return;
|
||||
void WindowSystem::SendButtonAppletMessageLocked(AppletMessage message) {
|
||||
if (m_home_menu) {
|
||||
std::scoped_lock lk_home{m_home_menu->lock};
|
||||
m_home_menu->lifecycle_manager.PushUnorderedMessage(message);
|
||||
}
|
||||
if (m_overlay_display) {
|
||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
||||
m_overlay_display->lifecycle_manager.PushUnorderedMessage(message);
|
||||
}
|
||||
if (m_application) {
|
||||
std::scoped_lock lk_application{m_application->lock};
|
||||
m_application->lifecycle_manager.PushUnorderedMessage(message);
|
||||
}
|
||||
if (m_event_observer) {
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
// Lock.
|
||||
std::scoped_lock lk2{m_home_menu->lock};
|
||||
void WindowSystem::OnSystemButtonPress(SystemButtonType type) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
switch (type) {
|
||||
case SystemButtonType::HomeButtonShortPressing:
|
||||
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingHomeButton);
|
||||
break;
|
||||
case SystemButtonType::HomeButtonLongPressing: {
|
||||
// Toggle overlay foreground visibility on long home press
|
||||
if (m_overlay_display) {
|
||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
||||
m_overlay_display->overlay_in_foreground = !m_overlay_display->overlay_in_foreground;
|
||||
// Tie window visibility to foreground state so hidden when not active
|
||||
m_overlay_display->window_visible = m_overlay_display->overlay_in_foreground;
|
||||
LOG_INFO(Service_AM, "Overlay long-press toggle: overlay_in_foreground={} window_visible={}", m_overlay_display->overlay_in_foreground, m_overlay_display->window_visible);
|
||||
}
|
||||
SendButtonAppletMessageLocked(AppletMessage::DetectLongPressingHomeButton);
|
||||
// Force a state update after toggling overlay
|
||||
if (m_event_observer) {
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
break; }
|
||||
case SystemButtonType::CaptureButtonShortPressing:
|
||||
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingCaptureButton);
|
||||
break;
|
||||
case SystemButtonType::CaptureButtonLongPressing:
|
||||
SendButtonAppletMessageLocked(AppletMessage::DetectLongPressingCaptureButton);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Send home button press event to home menu.
|
||||
if (type == ButtonPressDuration::ShortPressing) {
|
||||
m_home_menu->lifecycle_manager.PushUnorderedMessage(
|
||||
AppletMessage::DetectShortPressingHomeButton);
|
||||
void WindowSystem::OnHomeButtonPressed(ButtonPressDuration type) {
|
||||
// Map duration to SystemButtonType for legacy callers
|
||||
switch (type) {
|
||||
case ButtonPressDuration::ShortPressing:
|
||||
OnSystemButtonPress(SystemButtonType::HomeButtonShortPressing);
|
||||
break;
|
||||
case ButtonPressDuration::MiddlePressing:
|
||||
case ButtonPressDuration::LongPressing:
|
||||
OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,6 +274,10 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
|
||||
}
|
||||
}
|
||||
|
||||
if (applet.get() == m_overlay_display) {
|
||||
m_overlay_display = nullptr;
|
||||
}
|
||||
|
||||
// Finalize applet.
|
||||
applet->OnProcessTerminatedLocked();
|
||||
|
||||
@@ -258,7 +328,7 @@ void WindowSystem::TerminateChildAppletsLocked(Applet* applet) {
|
||||
applet->lock.lock();
|
||||
}
|
||||
|
||||
void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground) {
|
||||
void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking) {
|
||||
// With no applet, we don't have anything to do.
|
||||
if (!applet) {
|
||||
return;
|
||||
@@ -287,10 +357,23 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground) {
|
||||
}();
|
||||
|
||||
// Update visibility state.
|
||||
applet->display_layer_manager.SetWindowVisibility(is_foreground && applet->window_visible);
|
||||
// Overlay applets should always be visible when window_visible is true, regardless of foreground state
|
||||
const bool should_be_visible = (applet->applet_id == AppletId::OverlayDisplay)
|
||||
? applet->window_visible
|
||||
: (is_foreground && applet->window_visible);
|
||||
applet->display_layer_manager.SetWindowVisibility(should_be_visible);
|
||||
|
||||
// Update interactibility state.
|
||||
applet->SetInteractibleLocked(is_foreground && applet->window_visible);
|
||||
|
||||
const bool should_be_interactible = (applet->applet_id == AppletId::OverlayDisplay)
|
||||
? applet->overlay_in_foreground
|
||||
: (is_foreground && applet->window_visible && !overlay_blocking);
|
||||
|
||||
if (applet->applet_id == AppletId::OverlayDisplay || applet->applet_id == AppletId::Application) {
|
||||
LOG_DEBUG(Service_AM, "UpdateAppletStateLocked: applet={} overlay_in_foreground={} is_foreground={} window_visible={} overlay_blocking={} should_be_interactible={}",
|
||||
static_cast<u32>(applet->applet_id), applet->overlay_in_foreground, is_foreground, applet->window_visible, overlay_blocking, should_be_interactible);
|
||||
}
|
||||
|
||||
applet->SetInteractibleLocked(should_be_interactible);
|
||||
|
||||
// Update focus state and suspension.
|
||||
const bool is_obscured = has_obscuring_child_applets || !applet->window_visible;
|
||||
@@ -306,9 +389,23 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground) {
|
||||
applet->UpdateSuspensionStateLocked(true);
|
||||
}
|
||||
|
||||
// Z-index logic like in reference C# implementation (tuned for overlay extremes)
|
||||
s32 z_index = 0;
|
||||
const bool now_foreground = inherited_foreground;
|
||||
if (applet->applet_id == AppletId::OverlayDisplay) {
|
||||
z_index = applet->overlay_in_foreground ? 100000 : -100000;
|
||||
} else if (now_foreground && !is_obscured) {
|
||||
z_index = 2;
|
||||
} else if (now_foreground) {
|
||||
z_index = 1;
|
||||
} else {
|
||||
z_index = 0;
|
||||
}
|
||||
applet->display_layer_manager.SetOverlayZIndex(z_index);
|
||||
|
||||
// Recurse into child applets.
|
||||
for (const auto& child_applet : applet->child_applets) {
|
||||
this->UpdateAppletStateLocked(child_applet.get(), is_foreground);
|
||||
this->UpdateAppletStateLocked(child_applet.get(), is_foreground, overlay_blocking);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -8,6 +11,7 @@
|
||||
#include <mutex>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
@@ -32,6 +36,7 @@ public:
|
||||
public:
|
||||
void SetEventObserver(EventObserver* event_observer);
|
||||
void Update();
|
||||
void RequestUpdate();
|
||||
|
||||
public:
|
||||
void TrackApplet(std::shared_ptr<Applet> applet, bool is_application);
|
||||
@@ -49,6 +54,7 @@ public:
|
||||
void OnOperationModeChanged();
|
||||
void OnExitRequested();
|
||||
void OnHomeButtonPressed(ButtonPressDuration type);
|
||||
void OnSystemButtonPress(SystemButtonType type);
|
||||
void OnCaptureButtonPressed(ButtonPressDuration type) {}
|
||||
void OnPowerButtonPressed(ButtonPressDuration type) {}
|
||||
|
||||
@@ -56,7 +62,8 @@ private:
|
||||
void PruneTerminatedAppletsLocked();
|
||||
bool LockHomeMenuIntoForegroundLocked();
|
||||
void TerminateChildAppletsLocked(Applet* applet);
|
||||
void UpdateAppletStateLocked(Applet* applet, bool is_foreground);
|
||||
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking = false);
|
||||
void SendButtonAppletMessageLocked(AppletMessage message);
|
||||
|
||||
private:
|
||||
// System reference.
|
||||
@@ -75,6 +82,7 @@ private:
|
||||
// Foreground roots.
|
||||
Applet* m_home_menu{};
|
||||
Applet* m_application{};
|
||||
Applet* m_overlay_display{};
|
||||
|
||||
// Applet map by aruid.
|
||||
std::map<u64, std::shared_ptr<Applet>> m_applets{};
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "core/hle/service/audio/audio_controller.h"
|
||||
#include "core/hle/service/audio/audio_out_manager.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/set/system_settings_server.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "common/settings.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
@@ -17,12 +24,12 @@ IAudioController::IAudioController(Core::System& system_)
|
||||
: ServiceFramework{system_, "audctl"}, service_context{system, "audctl"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetTargetVolume"},
|
||||
{1, nullptr, "SetTargetVolume"},
|
||||
{0, D<&IAudioController::GetTargetVolume>, "GetTargetVolume"},
|
||||
{1, D<&IAudioController::SetTargetVolume>, "SetTargetVolume"},
|
||||
{2, D<&IAudioController::GetTargetVolumeMin>, "GetTargetVolumeMin"},
|
||||
{3, D<&IAudioController::GetTargetVolumeMax>, "GetTargetVolumeMax"},
|
||||
{4, nullptr, "IsTargetMute"},
|
||||
{5, nullptr, "SetTargetMute"},
|
||||
{4, D<&IAudioController::IsTargetMute>, "IsTargetMute"},
|
||||
{5, D<&IAudioController::SetTargetMute>, "SetTargetMute"},
|
||||
{6, nullptr, "IsTargetConnected"},
|
||||
{7, nullptr, "SetDefaultTarget"},
|
||||
{8, nullptr, "GetDefaultTarget"},
|
||||
@@ -49,7 +56,7 @@ IAudioController::IAudioController(Core::System& system_)
|
||||
{29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
|
||||
{30, D<&IAudioController::SetSpeakerAutoMuteEnabled>, "SetSpeakerAutoMuteEnabled"},
|
||||
{31, D<&IAudioController::IsSpeakerAutoMuteEnabled>, "IsSpeakerAutoMuteEnabled"},
|
||||
{32, nullptr, "GetActiveOutputTarget"},
|
||||
{32, D<&IAudioController::GetActiveOutputTarget>, "GetActiveOutputTarget"},
|
||||
{33, nullptr, "GetTargetDeviceInfo"},
|
||||
{34, D<&IAudioController::AcquireTargetNotification>, "AcquireTargetNotification"},
|
||||
{35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
|
||||
@@ -84,6 +91,38 @@ IAudioController::IAudioController(Core::System& system_)
|
||||
m_set_sys =
|
||||
system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
|
||||
notification_event = service_context.CreateEvent("IAudioController:NotificationEvent");
|
||||
|
||||
// Probably shouldn't do this in constructor?
|
||||
try {
|
||||
const int ui_volume = Settings::values.volume.GetValue();
|
||||
const int mapped = static_cast<int>(std::lround((static_cast<double>(ui_volume) / 100.0) * 15.0));
|
||||
const auto active_idx = static_cast<size_t>(m_active_target);
|
||||
if (active_idx < m_target_volumes.size()) {
|
||||
m_target_volumes[active_idx] = std::clamp(mapped, 0, 15);
|
||||
}
|
||||
|
||||
const bool muted = Settings::values.audio_muted.GetValue();
|
||||
for (auto& m : m_target_muted) {
|
||||
m = muted;
|
||||
}
|
||||
|
||||
if (!muted && active_idx < m_target_volumes.size()) {
|
||||
const float vol_f = static_cast<float>(m_target_volumes[active_idx]) / 15.0f;
|
||||
try {
|
||||
auto& sink = system.AudioCore().GetOutputSink();
|
||||
sink.SetSystemVolume(vol_f);
|
||||
sink.SetDeviceVolume(vol_f);
|
||||
} catch (...) {
|
||||
LOG_WARNING(Audio, "Failed to apply initial sink volume from settings");
|
||||
}
|
||||
|
||||
if (auto audout_mgr = system.ServiceManager().GetService<Service::Audio::IAudioOutManager>("audout:u")) {
|
||||
audout_mgr->SetAllAudioOutVolume(static_cast<float>(m_target_volumes[active_idx]) / 15.0f);
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
// Catch if something fails, since this is constructor
|
||||
}
|
||||
}
|
||||
|
||||
IAudioController::~IAudioController() {
|
||||
@@ -191,4 +230,120 @@ Result IAudioController::Unknown5000(Out<SharedPointer<IAudioController>> out_au
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioController::GetTargetVolume(Out<s32> out_target_volume, Set::AudioOutputModeTarget target) {
|
||||
LOG_DEBUG(Audio, "GetTargetVolume called, target={}", target);
|
||||
|
||||
const auto idx = static_cast<size_t>(target);
|
||||
if (idx >= m_target_volumes.size()) {
|
||||
LOG_ERROR(Audio, "GetTargetVolume invalid target {}", idx);
|
||||
R_THROW(ResultInvalidArgument);
|
||||
}
|
||||
|
||||
*out_target_volume = m_target_volumes[idx];
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioController::SetTargetVolume(Set::AudioOutputModeTarget target, s32 target_volume) {
|
||||
LOG_INFO(Audio, "SetTargetVolume called, target={}, volume={}", target, target_volume);
|
||||
|
||||
const auto idx = static_cast<size_t>(target);
|
||||
if (idx >= m_target_volumes.size()) {
|
||||
LOG_ERROR(Audio, "SetTargetVolume invalid target {}", idx);
|
||||
R_THROW(ResultInvalidArgument);
|
||||
}
|
||||
|
||||
if (target_volume < 0) {
|
||||
target_volume = 0;
|
||||
} else if (target_volume > 15) {
|
||||
target_volume = 15;
|
||||
}
|
||||
|
||||
m_target_volumes[idx] = target_volume;
|
||||
|
||||
if (m_active_target == target && !m_target_muted[idx]) {
|
||||
const float vol = static_cast<float>(target_volume) / 15.0f;
|
||||
// try catch this, as we don't know how it handles it when no output is set.
|
||||
// we already have audio issues when no output is set, so catch.
|
||||
try {
|
||||
auto& sink = system.AudioCore().GetOutputSink();
|
||||
sink.SetSystemVolume(vol);
|
||||
sink.SetDeviceVolume(vol);
|
||||
} catch (...) {
|
||||
LOG_WARNING(Audio, "Failed to set sink system volume");
|
||||
}
|
||||
|
||||
if (auto audout_mgr = system.ServiceManager().GetService<IAudioOutManager>("audout:u")) {
|
||||
audout_mgr->SetAllAudioOutVolume(vol);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_active_target == target) {
|
||||
const int ui_volume = static_cast<int>(std::lround((static_cast<double>(target_volume) / 15.0) * 100.0));
|
||||
Settings::values.volume.SetValue(static_cast<u8>(std::clamp(ui_volume, 0, 100)));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioController::IsTargetMute(Out<bool> out_is_target_muted, Set::AudioOutputModeTarget target) {
|
||||
LOG_DEBUG(Audio, "called, target={}", target);
|
||||
|
||||
const auto idx = static_cast<size_t>(target);
|
||||
if (idx >= m_target_muted.size()) {
|
||||
LOG_ERROR(Audio, "invalid target {}", idx);
|
||||
R_THROW(ResultInvalidArgument);
|
||||
}
|
||||
|
||||
*out_is_target_muted = m_target_muted[idx];
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioController::SetTargetMute(bool is_muted, Set::AudioOutputModeTarget target) {
|
||||
LOG_INFO(Audio, "called, target={}, muted={}", target, is_muted);
|
||||
|
||||
const auto idx = static_cast<size_t>(target);
|
||||
if (idx >= m_target_muted.size()) {
|
||||
LOG_ERROR(Audio, "invalid target {}", idx);
|
||||
R_THROW(ResultInvalidArgument);
|
||||
}
|
||||
|
||||
m_target_muted[idx] = is_muted;
|
||||
|
||||
if (m_active_target == target) {
|
||||
try {
|
||||
auto& sink = system.AudioCore().GetOutputSink();
|
||||
if (is_muted) {
|
||||
sink.SetSystemVolume(0.0f);
|
||||
sink.SetDeviceVolume(0.0f);
|
||||
} else {
|
||||
const float vol = static_cast<float>(m_target_volumes[idx]) / 15.0f;
|
||||
sink.SetSystemVolume(vol);
|
||||
sink.SetDeviceVolume(vol);
|
||||
}
|
||||
} catch (...) {
|
||||
LOG_WARNING(Audio, "Failed to set sink system volume on mute change");
|
||||
}
|
||||
|
||||
// Also update any open audout sessions via the audout:u service.
|
||||
if (auto audout_mgr = system.ServiceManager().GetService<IAudioOutManager>("audout:u")) {
|
||||
if (is_muted) {
|
||||
audout_mgr->SetAllAudioOutVolume(0.0f);
|
||||
} else {
|
||||
const float vol = static_cast<float>(m_target_volumes[idx]) / 15.0f;
|
||||
audout_mgr->SetAllAudioOutVolume(vol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Settings::values.audio_muted.SetValue(is_muted);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioController::GetActiveOutputTarget(Out<Set::AudioOutputModeTarget> out_active_target) {
|
||||
LOG_DEBUG(Audio, "GetActiveOutputTarget called");
|
||||
*out_active_target = m_active_target;
|
||||
R_SUCCEED();
|
||||
}
|
||||
} // namespace Service::Audio
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -35,6 +38,11 @@ private:
|
||||
|
||||
Result GetTargetVolumeMin(Out<s32> out_target_min_volume);
|
||||
Result GetTargetVolumeMax(Out<s32> out_target_max_volume);
|
||||
Result GetTargetVolume(Out<s32> out_target_volume, Set::AudioOutputModeTarget target);
|
||||
Result SetTargetVolume(Set::AudioOutputModeTarget target, s32 target_volume);
|
||||
Result IsTargetMute(Out<bool> out_is_target_muted, Set::AudioOutputModeTarget target);
|
||||
Result SetTargetMute(bool is_muted, Set::AudioOutputModeTarget target);
|
||||
Result GetActiveOutputTarget(Out<Set::AudioOutputModeTarget> out_active_target);
|
||||
Result GetAudioOutputMode(Out<Set::AudioOutputMode> out_output_mode,
|
||||
Set::AudioOutputModeTarget target);
|
||||
Result SetAudioOutputMode(Set::AudioOutputModeTarget target, Set::AudioOutputMode output_mode);
|
||||
@@ -55,6 +63,9 @@ private:
|
||||
|
||||
Kernel::KEvent* notification_event;
|
||||
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
|
||||
std::array<s32, 6> m_target_volumes{{15, 15, 15, 15, 15, 15}};
|
||||
std::array<bool, 6> m_target_muted{{false, false, false, false, false, false}};
|
||||
Set::AudioOutputModeTarget m_active_target{Set::AudioOutputModeTarget::Speaker};
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -98,4 +101,15 @@ Result IAudioOutManager::OpenAudioOutAuto(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioOutManager::SetAllAudioOutVolume(f32 volume) {
|
||||
std::scoped_lock l{impl->mutex};
|
||||
for (auto& session : impl->sessions) {
|
||||
if (session) {
|
||||
session->GetSystem().SetVolume(volume);
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,6 +21,8 @@ public:
|
||||
explicit IAudioOutManager(Core::System& system_);
|
||||
~IAudioOutManager() override;
|
||||
|
||||
Result SetAllAudioOutVolume(f32 volume);
|
||||
|
||||
private:
|
||||
Result ListAudioOuts(OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_audio_outs,
|
||||
Out<u32> out_count);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -19,6 +22,7 @@ constexpr Result ResultInvalidAddressInfo{ErrorModule::Audio, 42};
|
||||
constexpr Result ResultNotSupported{ErrorModule::Audio, 513};
|
||||
constexpr Result ResultInvalidHandle{ErrorModule::Audio, 1536};
|
||||
constexpr Result ResultInvalidRevision{ErrorModule::Audio, 1537};
|
||||
constexpr Result ResultInvalidArgument{ErrorModule::Audio, 900};
|
||||
|
||||
constexpr Result ResultLibOpusAllocFail{ErrorModule::HwOpus, 7};
|
||||
constexpr Result ResultInputDataTooSmall{ErrorModule::HwOpus, 8};
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -53,6 +56,7 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
|
||||
{8021, nullptr, "GetAlbumEntryFromApplicationAlbumEntryAruid"},
|
||||
{10011, nullptr, "SetInternalErrorConversionEnabled"},
|
||||
{50000, nullptr, "LoadMakerNoteInfoForDebug"},
|
||||
{50011, C<&IAlbumAccessorService::GetAlbumAccessResultForDebug>, "GetAlbumAccessResultForDebug"},
|
||||
{60002, nullptr, "OpenAccessorSession"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -197,4 +201,11 @@ Result IAlbumAccessorService::TranslateResult(Result in_result) {
|
||||
return in_result;
|
||||
}
|
||||
|
||||
|
||||
Result IAlbumAccessorService::GetAlbumAccessResultForDebug(Out<Result> out_result) {
|
||||
LOG_WARNING(Service_Capture, "(STUBBED) called");
|
||||
*out_result = ResultSuccess;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Capture
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -52,6 +55,8 @@ private:
|
||||
|
||||
Result TranslateResult(Result in_result);
|
||||
|
||||
Result GetAlbumAccessResultForDebug(Out<Result> out_result);
|
||||
|
||||
std::shared_ptr<AlbumManager> manager = nullptr;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -150,16 +153,17 @@ private:
|
||||
|
||||
class INpnsUser final : public ServiceFramework<INpnsUser> {
|
||||
public:
|
||||
explicit INpnsUser(Core::System& system_) : ServiceFramework{system_, "npns:u"} {
|
||||
explicit INpnsUser(Core::System& system_)
|
||||
: ServiceFramework{system_, "npns:u"}, service_context{system, "npns:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, nullptr, "ListenAll"},
|
||||
{2, nullptr, "ListenTo"},
|
||||
{3, nullptr, "Receive"},
|
||||
{4, nullptr, "ReceiveRaw"},
|
||||
{5, nullptr, "GetReceiveEvent"},
|
||||
{5, C<&INpnsUser::GetReceiveEvent>, "GetReceiveEvent"},
|
||||
{7, nullptr, "GetStateChangeEvent"},
|
||||
{8, nullptr, "ListenToByName"}, // 18.0.0+
|
||||
{8, C<&INpnsUser::ListenToByName>, "ListenToByName"}, // 18.0.0+
|
||||
{21, nullptr, "CreateToken"},
|
||||
{23, nullptr, "DestroyToken"},
|
||||
{25, nullptr, "QueryIsTokenValid"},
|
||||
@@ -178,7 +182,33 @@ public:
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
get_receive_event = service_context.CreateEvent("npns:u:GetReceiveEvent");
|
||||
}
|
||||
|
||||
~INpnsUser() override {
|
||||
service_context.CloseEvent(get_receive_event);
|
||||
}
|
||||
|
||||
private:
|
||||
Result ListenToByName(InBuffer<BufferAttr_HipcMapAlias> name_buffer) {
|
||||
const std::string name(reinterpret_cast<const char*>(name_buffer.data()), name_buffer.size());
|
||||
LOG_DEBUG(Service_NPNS, "called, name={}", name);
|
||||
|
||||
// Store the name for future use if needed
|
||||
// For now, just acknowledge the registration
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetReceiveEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_DEBUG(Service_NPNS, "called");
|
||||
|
||||
*out_event = &get_receive_event->GetReadableEvent();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* get_receive_event;
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
@@ -11,6 +12,7 @@
|
||||
#include "core/hle/service/ns/application_manager_interface.h"
|
||||
#include "core/hle/service/ns/content_management_interface.h"
|
||||
#include "core/hle/service/ns/read_only_application_control_data_interface.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
|
||||
namespace Service::NS {
|
||||
|
||||
@@ -19,7 +21,8 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
service_context{system, "IApplicationManagerInterface"},
|
||||
record_update_system_event{service_context}, sd_card_mount_status_event{service_context},
|
||||
gamecard_update_detection_event{service_context},
|
||||
gamecard_mount_status_event{service_context}, gamecard_mount_failure_event{service_context} {
|
||||
gamecard_mount_status_event{service_context}, gamecard_mount_failure_event{service_context},
|
||||
gamecard_waken_ready_event{service_context}, unknown_event{service_context} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IApplicationManagerInterface::ListApplicationRecord>, "ListApplicationRecord"},
|
||||
@@ -42,7 +45,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
{26, nullptr, "BeginInstallApplication"},
|
||||
{27, nullptr, "DeleteApplicationRecord"},
|
||||
{30, nullptr, "RequestApplicationUpdateInfo"},
|
||||
{31, nullptr, "Unknown31"},
|
||||
{31, nullptr, "RequestUpdateApplication"},
|
||||
{32, nullptr, "CancelApplicationDownload"},
|
||||
{33, nullptr, "ResumeApplicationDownload"},
|
||||
{35, nullptr, "UpdateVersionList"},
|
||||
@@ -138,6 +141,8 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
{508, nullptr, "GetLastGameCardMountFailureResult"},
|
||||
{509, nullptr, "ListApplicationIdOnGameCard"},
|
||||
{510, nullptr, "GetGameCardPlatformRegion"},
|
||||
{511, D<&IApplicationManagerInterface::GetGameCardWakenReadyEvent>, "GetGameCardWakenReadyEvent"},
|
||||
{512, D<&IApplicationManagerInterface::IsGameCardApplicationRunning>, "IsGameCardApplicationRunning"},
|
||||
{600, nullptr, "CountApplicationContentMeta"},
|
||||
{601, nullptr, "ListApplicationContentMetaStatus"},
|
||||
{602, nullptr, "ListAvailableAddOnContent"},
|
||||
@@ -162,7 +167,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
{901, nullptr, "GetApplicationRecordProperty"},
|
||||
{902, nullptr, "EnableApplicationAutoUpdate"},
|
||||
{903, nullptr, "DisableApplicationAutoUpdate"},
|
||||
{904, nullptr, "TouchApplication"},
|
||||
{904, D<&IApplicationManagerInterface::TouchApplication>, "TouchApplication"},
|
||||
{905, nullptr, "RequestApplicationUpdate"},
|
||||
{906, D<&IApplicationManagerInterface::IsApplicationUpdateRequested>, "IsApplicationUpdateRequested"},
|
||||
{907, nullptr, "WithdrawApplicationUpdateRequest"},
|
||||
@@ -329,7 +334,7 @@ Result IApplicationManagerInterface::GetApplicationControlData(
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_actual_size,
|
||||
ApplicationControlSource application_control_source, u64 application_id) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
R_RETURN(IReadOnlyApplicationControlDataInterface(system).GetApplicationControlDataOld(
|
||||
R_RETURN(IReadOnlyApplicationControlDataInterface(system).GetApplicationControlData(
|
||||
out_buffer, out_actual_size, application_control_source, application_id));
|
||||
}
|
||||
|
||||
@@ -403,6 +408,19 @@ Result IApplicationManagerInterface::GetGameCardMountFailureEvent(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::GetGameCardWakenReadyEvent(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||
*out_event = gamecard_waken_ready_event.GetHandle();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::IsGameCardApplicationRunning(Out<bool> out_is_running) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||
*out_is_running = false;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
|
||||
Out<bool> out_is_any_application_entity_installed) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||
@@ -526,6 +544,11 @@ Result IApplicationManagerInterface::GetStorageSize(Out<s64> out_total_space_siz
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::TouchApplication(u64 application_id) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called. application_id={:016X}", application_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::IsApplicationUpdateRequested(Out<bool> out_update_required,
|
||||
Out<u32> out_update_version,
|
||||
u64 application_id) {
|
||||
@@ -549,14 +572,18 @@ Result IApplicationManagerInterface::GetApplicationTerminateResult(Out<Result> o
|
||||
|
||||
Result IApplicationManagerInterface::RequestDownloadApplicationControlDataInBackground(
|
||||
u64 control_source, u64 application_id) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED), control_source={} app={:016X}", control_source, application_id);
|
||||
LOG_INFO(Service_NS, "called, control_source={} app={:016X}",
|
||||
control_source, application_id);
|
||||
|
||||
unknown_event.Signal();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::Unknown4022(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||
*out_event = gamecard_update_detection_event.GetHandle();
|
||||
unknown_event.Signal();
|
||||
*out_event = unknown_event.GetHandle();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ public:
|
||||
Out<s32> out_count, s32 offset);
|
||||
Result GetApplicationRecordUpdateSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetGameCardMountFailureEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetGameCardWakenReadyEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result IsGameCardApplicationRunning(Out<bool> out_is_running);
|
||||
Result IsAnyApplicationEntityInstalled(Out<bool> out_is_any_application_entity_installed);
|
||||
Result GetApplicationViewDeprecated(
|
||||
OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views,
|
||||
@@ -52,6 +54,7 @@ public:
|
||||
Result ResumeAll();
|
||||
Result GetStorageSize(Out<s64> out_total_space_size, Out<s64> out_free_space_size,
|
||||
FileSys::StorageId storage_id);
|
||||
Result TouchApplication(u64 application_id);
|
||||
Result IsApplicationUpdateRequested(Out<bool> out_update_required, Out<u32> out_update_version,
|
||||
u64 application_id);
|
||||
Result CheckApplicationLaunchVersion(u64 application_id);
|
||||
@@ -60,7 +63,7 @@ public:
|
||||
Result Unknown4023(Out<u64> out_result);
|
||||
Result Unknown4053();
|
||||
|
||||
Result RequestDownloadApplicationControlDataInBackground(u64 unk,
|
||||
Result RequestDownloadApplicationControlDataInBackground(u64 control_source,
|
||||
u64 application_id);
|
||||
|
||||
private:
|
||||
@@ -70,6 +73,8 @@ private:
|
||||
Event gamecard_update_detection_event;
|
||||
Event gamecard_mount_status_event;
|
||||
Event gamecard_mount_failure_event;
|
||||
Event gamecard_waken_ready_event;
|
||||
Event unknown_event;
|
||||
};
|
||||
|
||||
} // namespace Service::NS
|
||||
|
||||
@@ -22,13 +22,13 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
|
||||
: ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataOld>, "GetApplicationControlDataOld"},
|
||||
{0, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData>, "GetApplicationControlData"},
|
||||
{1, D<&IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage>, "GetApplicationDesiredLanguage"},
|
||||
{2, D<&IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"},
|
||||
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
||||
{4, nullptr, "SelectApplicationDesiredLanguage"},
|
||||
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithIconSize"},
|
||||
{19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithIconSize"},
|
||||
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithoutIcon"},
|
||||
{19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon>, "GetApplicationControlDataWithoutIcon"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -37,7 +37,7 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
|
||||
|
||||
IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default;
|
||||
|
||||
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataOld(
|
||||
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_actual_size,
|
||||
ApplicationControlSource application_control_source, u64 application_id) {
|
||||
LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}",
|
||||
@@ -125,8 +125,8 @@ Result IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLan
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_actual_size,
|
||||
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon(
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u64> out_total_size,
|
||||
ApplicationControlSource application_control_source, u64 application_id) {
|
||||
LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}",
|
||||
application_control_source, application_id);
|
||||
@@ -136,57 +136,44 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
|
||||
const auto control = pm.GetControlMetadata();
|
||||
const auto size = out_buffer.size();
|
||||
|
||||
const auto icon_size = control.second ? control.second->GetSize() : 0;
|
||||
const auto total_size = sizeof(FileSys::RawNACP) + icon_size;
|
||||
const auto nacp_size = sizeof(FileSys::RawNACP);
|
||||
|
||||
if (size < total_size) {
|
||||
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min=0x4000)",
|
||||
size);
|
||||
if (size < nacp_size) {
|
||||
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min={:08X})",
|
||||
size, nacp_size);
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
if (control.first != nullptr) {
|
||||
const auto bytes = control.first->GetRawBytes();
|
||||
std::memcpy(out_buffer.data(), bytes.data(), bytes.size());
|
||||
const auto copy_len = (std::min)(static_cast<size_t>(bytes.size()), static_cast<size_t>(nacp_size));
|
||||
std::memcpy(out_buffer.data(), bytes.data(), copy_len);
|
||||
if (copy_len < nacp_size) {
|
||||
std::memset(out_buffer.data() + copy_len, 0, nacp_size - copy_len);
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}, defaulting to zero",
|
||||
application_id);
|
||||
std::memset(out_buffer.data(), 0, sizeof(FileSys::RawNACP));
|
||||
std::memset(out_buffer.data(), 0, nacp_size);
|
||||
}
|
||||
|
||||
if (control.second != nullptr) {
|
||||
control.second->Read(out_buffer.data() + sizeof(FileSys::RawNACP), icon_size);
|
||||
} else {
|
||||
LOG_WARNING(Service_NS, "missing icon data for application_id={:016X}", application_id);
|
||||
const auto icon_area_size = size - nacp_size;
|
||||
if (icon_area_size > 0) {
|
||||
if (control.second != nullptr) {
|
||||
const auto icon_size = control.second->GetSize();
|
||||
const auto to_copy = static_cast<size_t>((std::min)(icon_size, icon_area_size));
|
||||
control.second->Read(out_buffer.data() + nacp_size, to_copy);
|
||||
if (to_copy < icon_area_size) {
|
||||
std::memset(out_buffer.data() + nacp_size + to_copy, 0, icon_area_size - to_copy);
|
||||
}
|
||||
} else {
|
||||
std::memset(out_buffer.data() + nacp_size, 0, icon_area_size);
|
||||
LOG_WARNING(Service_NS, "missing icon data for application_id={:016X}, zero-filling icon area",
|
||||
application_id);
|
||||
}
|
||||
}
|
||||
|
||||
*out_actual_size = static_cast<u32>(total_size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlDataWithoutIcon(
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||
Out<u64> out_total_size,
|
||||
ApplicationControlSource application_control_source,
|
||||
u64 application_id) {
|
||||
LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}",
|
||||
application_control_source, application_id);
|
||||
|
||||
constexpr size_t kExpectedBufferSize = 0x14000;
|
||||
constexpr size_t kNACPSize = sizeof(FileSys::RawNACP);
|
||||
|
||||
const FileSys::PatchManager pm{application_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
|
||||
if (control.first != nullptr) {
|
||||
const auto bytes = control.first->GetRawBytes();
|
||||
std::memcpy(out_buffer.data(), bytes.data(), bytes.size());
|
||||
} else {
|
||||
std::memset(out_buffer.data(), 0, kNACPSize);
|
||||
}
|
||||
|
||||
*out_total_size = kExpectedBufferSize;
|
||||
*out_total_size = static_cast<u64>(size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
|
||||
~IReadOnlyApplicationControlDataInterface() override;
|
||||
|
||||
Result GetApplicationControlDataOld(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||
Result GetApplicationControlData(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||
Out<u32> out_actual_size,
|
||||
ApplicationControlSource application_control_source,
|
||||
u64 application_id);
|
||||
@@ -27,10 +27,6 @@ public:
|
||||
u32 supported_languages);
|
||||
Result ConvertApplicationLanguageToLanguageCode(Out<u64> out_language_code,
|
||||
ApplicationLanguage application_language);
|
||||
Result GetApplicationControlData(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||
Out<u32> out_actual_size,
|
||||
ApplicationControlSource application_control_source,
|
||||
u64 application_id);
|
||||
Result GetApplicationControlDataWithoutIcon(
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||
Out<u64> out_total_size,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -12,7 +15,7 @@ struct Layer {
|
||||
explicit Layer(std::shared_ptr<android::BufferItemConsumer> buffer_item_consumer_,
|
||||
s32 consumer_id_)
|
||||
: buffer_item_consumer(std::move(buffer_item_consumer_)), consumer_id(consumer_id_),
|
||||
blending(LayerBlending::None), visible(true) {}
|
||||
blending(LayerBlending::None), visible(true), z_index(0) {}
|
||||
~Layer() {
|
||||
buffer_item_consumer->Abandon();
|
||||
}
|
||||
@@ -21,6 +24,7 @@ struct Layer {
|
||||
s32 consumer_id;
|
||||
LayerBlending blending;
|
||||
bool visible;
|
||||
s32 z_index;
|
||||
};
|
||||
|
||||
struct LayerStack {
|
||||
|
||||
@@ -95,7 +95,6 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
|
||||
const auto& item = buffer.item;
|
||||
const auto& igbp_buffer = *item.graphic_buffer;
|
||||
|
||||
// TODO: get proper Z-index from layer
|
||||
if (layer->visible) {
|
||||
composition_stack.emplace_back(HwcLayer{
|
||||
.buffer_handle = igbp_buffer.BufferId(),
|
||||
@@ -104,7 +103,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
|
||||
.width = igbp_buffer.Width(),
|
||||
.height = igbp_buffer.Height(),
|
||||
.stride = igbp_buffer.Stride(),
|
||||
.z_index = 0,
|
||||
.z_index = layer->z_index,
|
||||
.blending = layer->blending,
|
||||
.transform = static_cast<android::BufferTransformFlags>(item.transform),
|
||||
.crop_rect = item.crop,
|
||||
@@ -128,9 +127,9 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
|
||||
|
||||
// If any new buffers were acquired, we can present.
|
||||
if (has_acquired_buffer && !composition_stack.empty()) {
|
||||
// Sort by Z-index.
|
||||
// Sort back-to-front: lower z first, higher z last so top-most draws last (on top).
|
||||
std::stable_sort(composition_stack.begin(), composition_stack.end(),
|
||||
[&](auto& l, auto& r) { return l.z_index < r.z_index; });
|
||||
[&](const HwcLayer& l, const HwcLayer& r) { return l.z_index < r.z_index; });
|
||||
|
||||
// Composite.
|
||||
nvdisp.Composite(composition_stack);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -45,9 +48,10 @@ public:
|
||||
void SetLayerVisibility(s32 consumer_binder_id, bool visible);
|
||||
void SetLayerBlending(s32 consumer_binder_id, LayerBlending blending);
|
||||
|
||||
std::shared_ptr<Layer> FindLayer(s32 consumer_binder_id);
|
||||
|
||||
private:
|
||||
Display* FindDisplay(u64 display_id);
|
||||
std::shared_ptr<Layer> FindLayer(s32 consumer_binder_id);
|
||||
|
||||
public:
|
||||
// TODO: these don't belong here
|
||||
|
||||
@@ -13,16 +13,17 @@ IDaemonController::IDaemonController(Core::System& system_)
|
||||
: ServiceFramework{system_, "IDaemonController"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IDaemonController::GetAutoTransferEnabledForAccountAndApplication>, "GetAutoTransferEnabledForAccountAndApplication"},
|
||||
{1, nullptr, "SetAutoTransferEnabledForAccountAndApplication"},
|
||||
{2, nullptr, "GetGlobalUploadEnabledForAccount"},
|
||||
{3, nullptr, "SetGlobalUploadEnabledForAccount"},
|
||||
{4, nullptr, "TouchAccount"},
|
||||
{5, nullptr, "GetGlobalDownloadEnabledForAccount"},
|
||||
{6, nullptr, "SetGlobalDownloadEnabledForAccount"},
|
||||
{10, nullptr, "GetForbiddenSaveDataIndication"},
|
||||
{11, nullptr, "GetStopperObject"},
|
||||
{12, D<&IDaemonController::GetState>, "GetState"},
|
||||
{0, D<&IDaemonController::GetApplicationAutoTransferSetting>, "GetApplicationAutoTransferSetting"},
|
||||
{1, D<&IDaemonController::SetApplicationAutoTransferSetting>, "SetApplicationAutoTransferSetting"},
|
||||
{2, D<&IDaemonController::GetGlobalAutoUploadSetting>, "GetGlobalAutoUploadSetting"},
|
||||
{3, D<&IDaemonController::SetGlobalAutoUploadSetting>, "SetGlobalAutoUploadSetting"},
|
||||
{4, D<&IDaemonController::RunTransferTaskAutonomyRegistration>, "RunTransferTaskAutonomyRegistration"},
|
||||
{5, D<&IDaemonController::GetGlobalAutoDownloadSetting>, "GetGlobalAutoDownloadSetting"}, // 11.0.0+
|
||||
{6, D<&IDaemonController::SetGlobalAutoDownloadSetting>, "SetGlobalAutoDownloadSetting"}, // 11.0.0+
|
||||
{10, nullptr, "CreateForbiddenSaveDataInidication"},
|
||||
{11, nullptr, "StopAutonomyTaskExecution"},
|
||||
{12, D<&IDaemonController::GetAutonomyTaskStatus>, "GetAutonomyTaskStatus"},
|
||||
{13, nullptr, "Unknown13_20_0_0_Plus"}, // 20.0.0+
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -31,19 +32,73 @@ IDaemonController::IDaemonController(Core::System& system_)
|
||||
|
||||
IDaemonController::~IDaemonController() = default;
|
||||
|
||||
Result IDaemonController::GetAutoTransferEnabledForAccountAndApplication(Out<bool> out_is_enabled,
|
||||
Common::UUID user_id,
|
||||
u64 application_id) {
|
||||
LOG_WARNING(Service_OLSC, "(STUBBED) called, user_id={} application_id={:016X}",
|
||||
user_id.FormattedString(), application_id);
|
||||
*out_is_enabled = false;
|
||||
Result IDaemonController::GetApplicationAutoTransferSetting(Out<bool> out_is_enabled,
|
||||
Common::UUID user_id,
|
||||
u64 application_id) {
|
||||
LOG_INFO(Service_OLSC, "called, user_id={} application_id={:016X}", user_id.FormattedString(),
|
||||
application_id);
|
||||
AppKey key{user_id, application_id};
|
||||
const auto it = app_auto_transfer_.find(key);
|
||||
*out_is_enabled = (it != app_auto_transfer_.end()) ? it->second : false;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IDaemonController::GetState(Out<u8> state, Common::UUID user_id, u64 application_id) {
|
||||
LOG_WARNING(Service_OLSC, "(STUBBED) called, user_id={} application_id={:016X}",
|
||||
user_id.FormattedString(), application_id);
|
||||
*state = 0;
|
||||
Result IDaemonController::SetApplicationAutoTransferSetting(bool is_enabled, Common::UUID user_id,
|
||||
u64 application_id) {
|
||||
LOG_INFO(Service_OLSC, "called, user_id={} application_id={:016X} is_enabled={}",
|
||||
user_id.FormattedString(), application_id, is_enabled);
|
||||
AppKey key{user_id, application_id};
|
||||
app_auto_transfer_[key] = is_enabled;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IDaemonController::GetGlobalAutoUploadSetting(Out<bool> out_is_enabled,
|
||||
Common::UUID user_id) {
|
||||
LOG_INFO(Service_OLSC, "called, user_id={}", user_id.FormattedString());
|
||||
const auto it = global_auto_upload_.find(user_id);
|
||||
*out_is_enabled = (it != global_auto_upload_.end()) ? it->second : false;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IDaemonController::SetGlobalAutoUploadSetting(bool is_enabled, Common::UUID user_id) {
|
||||
LOG_INFO(Service_OLSC, "called, user_id={} is_enabled={}", user_id.FormattedString(), is_enabled);
|
||||
global_auto_upload_[user_id] = is_enabled;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IDaemonController::RunTransferTaskAutonomyRegistration(Common::UUID user_id,
|
||||
u64 application_id) {
|
||||
LOG_INFO(Service_OLSC, "called, user_id={} application_id={:016X}", user_id.FormattedString(),
|
||||
application_id);
|
||||
// Simulate starting an autonomy task: set status to 1 (running) then back to 0 (idle)
|
||||
AppKey key{user_id, application_id};
|
||||
autonomy_task_status_[key] = 1; // running
|
||||
// TODO: In a real implementation this would be asynchronous. We'll immediately set to idle.
|
||||
autonomy_task_status_[key] = 0; // idle
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IDaemonController::GetGlobalAutoDownloadSetting(Out<bool> out_is_enabled,
|
||||
Common::UUID user_id) {
|
||||
LOG_INFO(Service_OLSC, "called, user_id={}", user_id.FormattedString());
|
||||
const auto it = global_auto_download_.find(user_id);
|
||||
*out_is_enabled = (it != global_auto_download_.end()) ? it->second : false;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IDaemonController::SetGlobalAutoDownloadSetting(bool is_enabled, Common::UUID user_id) {
|
||||
LOG_INFO(Service_OLSC, "called, user_id={} is_enabled={}", user_id.FormattedString(), is_enabled);
|
||||
global_auto_download_[user_id] = is_enabled;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IDaemonController::GetAutonomyTaskStatus(Out<u8> out_state, Common::UUID user_id,
|
||||
u64 application_id) {
|
||||
LOG_INFO(Service_OLSC, "called, user_id={} application_id={:016X}", user_id.FormattedString(),
|
||||
application_id);
|
||||
AppKey key{user_id, application_id};
|
||||
const auto it = autonomy_task_status_.find(key);
|
||||
*out_state = (it != autonomy_task_status_.end()) ? it->second : 0; // default idle
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#pragma once
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/uuid.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
@@ -16,9 +19,43 @@ public:
|
||||
~IDaemonController() override;
|
||||
|
||||
private:
|
||||
Result GetAutoTransferEnabledForAccountAndApplication(Out<bool> out_is_enabled,
|
||||
Common::UUID user_id, u64 application_id);
|
||||
Result GetState(Out<u8> state, Common::UUID user_id, u64 application_id);
|
||||
Result GetApplicationAutoTransferSetting(Out<bool> out_is_enabled, Common::UUID user_id,
|
||||
u64 application_id);
|
||||
Result SetApplicationAutoTransferSetting(bool is_enabled, Common::UUID user_id,
|
||||
u64 application_id);
|
||||
|
||||
Result GetGlobalAutoUploadSetting(Out<bool> out_is_enabled, Common::UUID user_id);
|
||||
Result SetGlobalAutoUploadSetting(bool is_enabled, Common::UUID user_id);
|
||||
|
||||
Result RunTransferTaskAutonomyRegistration(Common::UUID user_id, u64 application_id);
|
||||
|
||||
Result GetGlobalAutoDownloadSetting(Out<bool> out_is_enabled, Common::UUID user_id);
|
||||
Result SetGlobalAutoDownloadSetting(bool is_enabled, Common::UUID user_id);
|
||||
|
||||
Result GetAutonomyTaskStatus(Out<u8> out_state, Common::UUID user_id, u64 application_id);
|
||||
|
||||
// Internal in-memory state to back the above APIs
|
||||
struct AppKey {
|
||||
Common::UUID user_id;
|
||||
u64 application_id{};
|
||||
friend constexpr bool operator==(const AppKey& a, const AppKey& b) {
|
||||
return a.user_id == b.user_id && a.application_id == b.application_id;
|
||||
}
|
||||
};
|
||||
struct AppKeyHash {
|
||||
size_t operator()(const AppKey& k) const noexcept {
|
||||
// Combine UUID hash and application_id
|
||||
size_t h1 = std::hash<Common::UUID>{}(k.user_id);
|
||||
size_t h2 = std::hash<u64>{}(k.application_id);
|
||||
// Simple hash combine
|
||||
return h1 ^ (h2 + 0x9e3779b97f4a7c15ULL + (h1 << 6) + (h1 >> 2));
|
||||
}
|
||||
};
|
||||
|
||||
std::unordered_map<AppKey, bool, AppKeyHash> app_auto_transfer_{};
|
||||
std::unordered_map<Common::UUID, bool> global_auto_upload_{};
|
||||
std::unordered_map<Common::UUID, bool> global_auto_download_{};
|
||||
std::unordered_map<AppKey, u8, AppKeyHash> autonomy_task_status_{};
|
||||
};
|
||||
|
||||
} // namespace Service::OLSC
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,4 +21,13 @@ union MessageFlags {
|
||||
};
|
||||
static_assert(sizeof(MessageFlags) == 0x8, "MessageFlags has incorrect size");
|
||||
|
||||
struct SourceName {
|
||||
char name[0x16];
|
||||
|
||||
const char* GetString() const {
|
||||
return name;
|
||||
}
|
||||
};
|
||||
;
|
||||
|
||||
} // namespace Service::PSC
|
||||
|
||||
@@ -1,24 +1,118 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/psc/ovln/receiver.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::PSC {
|
||||
|
||||
IReceiver::IReceiver(Core::System& system_) : ServiceFramework{system_, "IReceiver"} {
|
||||
IReceiver::IReceiver(Core::System& system_)
|
||||
: ServiceFramework{system_, "IReceiver"}, service_context{system_, "IReceiver"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "AddSource"},
|
||||
{1, nullptr, "RemoveSource"},
|
||||
{2, nullptr, "GetReceiveEventHandle"},
|
||||
{3, nullptr, "Receive"},
|
||||
{4, nullptr, "ReceiveWithTick"},
|
||||
{0, D<&IReceiver::AddSource>, "AddSource"},
|
||||
{1, D<&IReceiver::RemoveSource>, "RemoveSource"},
|
||||
{2, D<&IReceiver::GetReceiveEventHandle>, "GetReceiveEventHandle"},
|
||||
{3, D<&IReceiver::Receive>, "Receive"},
|
||||
{4, D<&IReceiver::ReceiveWithTick>, "ReceiveWithTick"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
receive_event = service_context.CreateEvent("IReceiver::ReceiveEvent");
|
||||
}
|
||||
|
||||
IReceiver::~IReceiver() = default;
|
||||
IReceiver::~IReceiver() {
|
||||
service_context.CloseEvent(receive_event);
|
||||
}
|
||||
|
||||
Result IReceiver::AddSource(SourceName source_name) {
|
||||
const std::string name = source_name.GetString();
|
||||
LOG_INFO(Service_PSC, "called: source_name={}", name);
|
||||
|
||||
// Add source if it doesn't already exist
|
||||
if (message_sources.find(name) == message_sources.end()) {
|
||||
message_sources[name] = {};
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IReceiver::RemoveSource(SourceName source_name) {
|
||||
const std::string name = source_name.GetString();
|
||||
LOG_INFO(Service_PSC, "called: source_name={}", name);
|
||||
|
||||
// Remove source if it exists
|
||||
message_sources.erase(name);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IReceiver::GetReceiveEventHandle(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_INFO(Service_PSC, "called");
|
||||
*out_event = &receive_event->GetReadableEvent();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IReceiver::Receive(Out<OverlayNotification> out_notification, Out<MessageFlags> out_flags) {
|
||||
u64 tick;
|
||||
return ReceiveWithTick(out_notification, out_flags, Out<u64>(&tick));
|
||||
}
|
||||
|
||||
Result IReceiver::ReceiveWithTick(Out<OverlayNotification> out_notification,
|
||||
Out<MessageFlags> out_flags, Out<u64> out_tick) {
|
||||
LOG_DEBUG(Service_PSC, "called");
|
||||
|
||||
// Find the message with the lowest ID across all sources
|
||||
const std::string* target_source = nullptr;
|
||||
size_t target_index = 0;
|
||||
|
||||
for (const auto& [source_name, messages] : message_sources) {
|
||||
if (!messages.empty()) {
|
||||
if (target_source == nullptr) {
|
||||
target_source = &source_name;
|
||||
target_index = 0;
|
||||
}
|
||||
// Note: In the real implementation, we would track message IDs
|
||||
// For now, just use FIFO order
|
||||
}
|
||||
}
|
||||
|
||||
if (target_source != nullptr) {
|
||||
auto& messages = message_sources[*target_source];
|
||||
*out_notification = messages[target_index].first;
|
||||
*out_flags = messages[target_index].second;
|
||||
*out_tick = 0; // TODO: Implement tick tracking
|
||||
|
||||
// Remove the message
|
||||
messages.erase(messages.begin() + target_index);
|
||||
|
||||
// Clear event if no more messages
|
||||
bool has_messages = false;
|
||||
for (const auto& [_, msgs] : message_sources) {
|
||||
if (!msgs.empty()) {
|
||||
has_messages = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_messages) {
|
||||
receive_event->Clear();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
// No messages available
|
||||
*out_notification = {};
|
||||
*out_flags = {};
|
||||
*out_tick = 0;
|
||||
|
||||
LOG_WARNING(Service_PSC, "No messages available");
|
||||
R_THROW(ResultUnknown); // TODO: Use proper OvlnResult::NoMessages when available
|
||||
}
|
||||
|
||||
} // namespace Service::PSC
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/psc/ovln/ovln_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KReadableEvent;
|
||||
}
|
||||
|
||||
namespace Service::PSC {
|
||||
|
||||
@@ -11,6 +25,22 @@ class IReceiver final : public ServiceFramework<IReceiver> {
|
||||
public:
|
||||
explicit IReceiver(Core::System& system_);
|
||||
~IReceiver() override;
|
||||
|
||||
IReceiver(const IReceiver&) = delete;
|
||||
IReceiver& operator=(const IReceiver&) = delete;
|
||||
|
||||
private:
|
||||
Result AddSource(SourceName source_name);
|
||||
Result RemoveSource(SourceName source_name);
|
||||
Result GetReceiveEventHandle(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result Receive(Out<OverlayNotification> out_notification, Out<MessageFlags> out_flags);
|
||||
Result ReceiveWithTick(Out<OverlayNotification> out_notification, Out<MessageFlags> out_flags,
|
||||
Out<u64> out_tick);
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* receive_event;
|
||||
|
||||
std::map<std::string, std::vector<std::pair<OverlayNotification, MessageFlags>>> message_sources;
|
||||
};
|
||||
|
||||
} // namespace Service::PSC
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -21,7 +24,7 @@ IReceiverService::~IReceiverService() = default;
|
||||
|
||||
Result IReceiverService::OpenReceiver(Out<SharedPointer<IReceiver>> out_receiver) {
|
||||
LOG_DEBUG(Service_PSC, "called");
|
||||
*out_receiver = std::make_shared<IReceiver>(system);
|
||||
*out_receiver = std::shared_ptr<IReceiver>(new IReceiver(system));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -131,6 +134,38 @@ Result Container::SetLayerBlending(u64 layer_id, bool enabled) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Container::SetLayerZIndex(u64 layer_id, s32 z_index) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
auto* const layer = m_layers.GetLayerById(layer_id);
|
||||
R_UNLESS(layer != nullptr, VI::ResultNotFound);
|
||||
|
||||
if (auto layer_ref = m_surface_flinger->FindLayer(layer->GetConsumerBinderId())) {
|
||||
LOG_DEBUG(Service_VI, "called, SetLayerZIndex layer_id={} z={} (cid={})", layer_id,
|
||||
z_index, layer->GetConsumerBinderId());
|
||||
layer_ref->z_index = z_index;
|
||||
} else {
|
||||
LOG_DEBUG(Service_VI, "called, SetLayerZIndex failed to find layer for layer_id={} (cid={})",
|
||||
layer_id, layer->GetConsumerBinderId());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Container::GetLayerZIndex(u64 layer_id, s32* out_z_index) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
auto* const layer = m_layers.GetLayerById(layer_id);
|
||||
R_UNLESS(layer != nullptr, VI::ResultNotFound);
|
||||
|
||||
if (auto layer_ref = m_surface_flinger->FindLayer(layer->GetConsumerBinderId())) {
|
||||
*out_z_index = layer_ref->z_index;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
R_RETURN(VI::ResultNotFound);
|
||||
}
|
||||
|
||||
void Container::LinkVsyncEvent(u64 display_id, Event* event) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
m_conductor->LinkVsyncEvent(display_id, event);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -62,6 +65,8 @@ public:
|
||||
|
||||
Result SetLayerVisibility(u64 layer_id, bool visible);
|
||||
Result SetLayerBlending(u64 layer_id, bool enabled);
|
||||
Result SetLayerZIndex(u64 layer_id, s32 z_index);
|
||||
Result GetLayerZIndex(u64 layer_id, s32* out_z_index);
|
||||
|
||||
void LinkVsyncEvent(u64 display_id, Event* event);
|
||||
void UnlinkVsyncEvent(u64 display_id, Event* event);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -116,6 +119,10 @@ Result IManagerDisplayService::SetLayerBlending(bool enabled, u64 layer_id) {
|
||||
R_RETURN(m_container->SetLayerBlending(layer_id, enabled));
|
||||
}
|
||||
|
||||
Result IManagerDisplayService::SetLayerZIndex(s32 z_index, u64 layer_id) {
|
||||
R_RETURN(m_container->SetLayerZIndex(layer_id, z_index));
|
||||
}
|
||||
|
||||
Result IManagerDisplayService::CreateManagedLayer(Out<u64> out_layer_id, u32 flags, u64 display_id,
|
||||
AppletResourceUserId aruid) {
|
||||
LOG_DEBUG(Service_VI, "called. flags={}, display={}, aruid={}", flags, display_id, aruid.pid);
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@@ -22,6 +27,7 @@ public:
|
||||
void DestroySharedLayerSession(Kernel::KProcess* owner_process);
|
||||
|
||||
Result SetLayerBlending(bool enabled, u64 layer_id);
|
||||
Result SetLayerZIndex(s32 z_index, u64 layer_id);
|
||||
|
||||
public:
|
||||
Result CreateManagedLayer(Out<u64> out_layer_id, u32 flags, u64 display_id,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -38,13 +41,13 @@ Result AllocateSharedBufferMemory(std::unique_ptr<Kernel::KPageGroup>* out_page_
|
||||
Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure,
|
||||
Kernel::KMemoryManager::Direction::FromBack)));
|
||||
|
||||
// Fill the output data with red.
|
||||
// Initialize to fully transparent black to avoid covering content before first present.
|
||||
for (auto& block : *pg) {
|
||||
u32* start = system.DeviceMemory().GetPointer<u32>(block.GetAddress());
|
||||
u32* end = system.DeviceMemory().GetPointer<u32>(block.GetAddress() + block.GetSize());
|
||||
|
||||
for (; start < end; start++) {
|
||||
*start = 0xFF0000FF;
|
||||
for (; start < end; ++start) {
|
||||
*start = 0x00000000; // ARGB/RGBA with alpha=0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,8 +255,9 @@ Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64*
|
||||
R_TRY(m_container.CreateStrayLayer(std::addressof(producer_binder_id),
|
||||
std::addressof(session.layer_id), display_id));
|
||||
|
||||
// Configure blending.
|
||||
// Configure blending and z-index
|
||||
R_ASSERT(m_container.SetLayerBlending(session.layer_id, enable_blending));
|
||||
R_ASSERT(m_container.SetLayerZIndex(session.layer_id, 100000));
|
||||
|
||||
// Get the producer and set preallocated buffers.
|
||||
std::shared_ptr<android::BufferQueueProducer> producer;
|
||||
@@ -370,6 +374,11 @@ Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence,
|
||||
android::Status::NoError,
|
||||
VI::ResultOperationFailed);
|
||||
|
||||
// Ensure the layer is visible when content is presented.
|
||||
// Re-assert overlay priority in case clients reset it.
|
||||
(void)m_container.SetLayerZIndex(layer_id, 100000);
|
||||
(void)m_container.SetLayerVisibility(layer_id, true);
|
||||
|
||||
// We succeeded.
|
||||
R_SUCCEED();
|
||||
}
|
||||
@@ -406,23 +415,51 @@ Result SharedBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32*
|
||||
// TODO: this could be optimized
|
||||
s64 e = -1280 * 768 * 4;
|
||||
for (auto& block : *m_buffer_page_group) {
|
||||
u8* start = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress());
|
||||
u8* end = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress() + block.GetSize());
|
||||
|
||||
for (; start < end; start++) {
|
||||
*start = 0;
|
||||
u8* const block_start = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress());
|
||||
u8* ptr = block_start;
|
||||
u8* const block_end = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress() + block.GetSize());
|
||||
|
||||
for (; ptr < block_end; ++ptr) {
|
||||
if (e >= 0 && e < static_cast<s64>(capture_buffer.size())) {
|
||||
*start = capture_buffer[e];
|
||||
*ptr = capture_buffer[static_cast<size_t>(e)];
|
||||
} else {
|
||||
*ptr = 0;
|
||||
}
|
||||
e++;
|
||||
++e;
|
||||
}
|
||||
|
||||
m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(start, scratch, [&](DAddr addr) {
|
||||
m_system.GPU().InvalidateRegion(addr, end - start);
|
||||
m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(block_start, scratch, [&](DAddr addr) {
|
||||
m_system.GPU().InvalidateRegion(addr, block_end - block_start);
|
||||
});
|
||||
}
|
||||
|
||||
// After writing, present a frame on each active shared layer so it becomes visible.
|
||||
for (auto& [aruid, session] : m_sessions) {
|
||||
std::shared_ptr<android::BufferQueueProducer> producer;
|
||||
if (R_FAILED(m_container.GetLayerProducerHandle(std::addressof(producer), session.layer_id))) {
|
||||
continue;
|
||||
}
|
||||
s32 slot = -1;
|
||||
android::Fence fence = android::Fence::NoFence();
|
||||
if (producer->DequeueBuffer(&slot, &fence, SharedBufferAsync != 0, SharedBufferWidth,
|
||||
SharedBufferHeight, SharedBufferBlockLinearFormat, 0) !=
|
||||
android::Status::NoError) {
|
||||
continue;
|
||||
}
|
||||
std::shared_ptr<android::GraphicBuffer> gb;
|
||||
if (producer->RequestBuffer(slot, &gb) != android::Status::NoError) {
|
||||
producer->CancelBuffer(slot, android::Fence::NoFence());
|
||||
continue;
|
||||
}
|
||||
android::QueueBufferInput qin{};
|
||||
android::QueueBufferOutput qout{};
|
||||
qin.crop = {0, 0, static_cast<s32>(SharedBufferWidth), static_cast<s32>(SharedBufferHeight)};
|
||||
qin.fence = android::Fence::NoFence();
|
||||
qin.transform = static_cast<android::NativeWindowTransform>(0);
|
||||
qin.swap_interval = 1;
|
||||
(void)producer->QueueBuffer(slot, qin, &qout);
|
||||
}
|
||||
|
||||
*out_was_written = true;
|
||||
*out_layer_index = 1;
|
||||
R_SUCCEED();
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -8,7 +11,6 @@
|
||||
#include "common/math_util.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
||||
#include "core/hle/service/nvnflinger/ui/fence.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -20,7 +23,7 @@ ISystemDisplayService::ISystemDisplayService(Core::System& system_,
|
||||
{1204, nullptr, "SetDisplayMagnification"},
|
||||
{2201, nullptr, "SetLayerPosition"},
|
||||
{2203, nullptr, "SetLayerSize"},
|
||||
{2204, nullptr, "GetLayerZ"},
|
||||
{2204, C<&ISystemDisplayService::GetLayerZ>, "GetLayerZ"},
|
||||
{2205, C<&ISystemDisplayService::SetLayerZ>, "SetLayerZ"},
|
||||
{2207, C<&ISystemDisplayService::SetLayerVisibility>, "SetLayerVisibility"},
|
||||
{2209, nullptr, "SetLayerAlpha"},
|
||||
@@ -68,16 +71,26 @@ ISystemDisplayService::ISystemDisplayService(Core::System& system_,
|
||||
|
||||
ISystemDisplayService::~ISystemDisplayService() = default;
|
||||
|
||||
Result ISystemDisplayService::SetLayerZ(u32 z_value, u64 layer_id) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called. layer_id={}, z_value={}", layer_id, z_value);
|
||||
Result ISystemDisplayService::GetLayerZ(Out<u64> out_z_value, u64 layer_id) {
|
||||
LOG_DEBUG(Service_VI, "called. layer_id={}", layer_id);
|
||||
s32 z{};
|
||||
const auto res = m_container->GetLayerZIndex(layer_id, &z);
|
||||
R_TRY(res);
|
||||
*out_z_value = static_cast<u64>(z);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ISystemDisplayService::SetLayerZ(u64 layer_id, u64 z_value) {
|
||||
LOG_DEBUG(Service_VI, "called. layer_id={}, z_value={}", layer_id, z_value);
|
||||
// Forward to container using internal API when available
|
||||
R_RETURN(m_container->SetLayerZIndex(layer_id, static_cast<s32>(z_value)));
|
||||
}
|
||||
|
||||
// This function currently does nothing but return a success error code in
|
||||
// the vi library itself, so do the same thing, but log out the passed in values.
|
||||
Result ISystemDisplayService::SetLayerVisibility(bool visible, u64 layer_id) {
|
||||
LOG_DEBUG(Service_VI, "called, layer_id={}, visible={}", layer_id, visible);
|
||||
R_SUCCEED();
|
||||
R_RETURN(m_container->SetLayerVisibility(layer_id, visible));
|
||||
}
|
||||
|
||||
Result ISystemDisplayService::ListDisplayModes(
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,7 +21,8 @@ public:
|
||||
~ISystemDisplayService() override;
|
||||
|
||||
private:
|
||||
Result SetLayerZ(u32 z_value, u64 layer_id);
|
||||
Result GetLayerZ(Out<u64> out_z_value, u64 layer_id);
|
||||
Result SetLayerZ(u64 layer_id, u64 z_value);
|
||||
Result SetLayerVisibility(bool visible, u64 layer_id);
|
||||
Result ListDisplayModes(Out<u64> out_count, u64 display_id,
|
||||
OutArray<DisplayMode, BufferAttr_HipcMapAlias> out_display_modes);
|
||||
|
||||
@@ -68,8 +68,7 @@ bool NPadData::IsNpadIdTypeSupported(Core::HID::NpadIdType npad_id) const {
|
||||
}
|
||||
|
||||
void NPadData::SetNpadSystemCommonPolicy(bool is_full_policy) {
|
||||
supported_npad_style_set = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::JoyDual |
|
||||
Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
|
||||
supported_npad_style_set = Core::HID::NpadStyleSet::All;
|
||||
handheld_activation_mode = NpadHandheldActivationMode::Dual;
|
||||
|
||||
status.is_supported_styleset_set.Assign(true);
|
||||
|
||||
@@ -47,6 +47,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent)
|
||||
INSERT(Settings, login_share_applet_mode, tr("Login share"), QString());
|
||||
INSERT(Settings, wifi_web_auth_applet_mode, tr("Wifi web auth"), QString());
|
||||
INSERT(Settings, my_page_applet_mode, tr("My page"), QString());
|
||||
INSERT(Settings, enable_overlay, tr("Enable Overlay Applet"), QString());
|
||||
|
||||
// Audio
|
||||
INSERT(Settings, sink_id, tr("Output Engine:"), QString());
|
||||
@@ -732,4 +733,3 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent)
|
||||
return translations;
|
||||
}
|
||||
} // namespace ConfigurationShared
|
||||
|
||||
|
||||
@@ -98,7 +98,6 @@ struct Values {
|
||||
|
||||
Setting<bool> single_window_mode{linkage, true, "singleWindowMode", Category::Ui};
|
||||
Setting<bool> fullscreen{linkage, false, "fullscreen", Category::Ui};
|
||||
Setting<bool> display_titlebar{linkage, true, "displayTitleBars", Category::Ui};
|
||||
Setting<bool> show_filter_bar{linkage, true, "showFilterBar", Category::Ui};
|
||||
Setting<bool> show_status_bar{linkage, true, "showStatusBar", Category::Ui};
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
</action>
|
||||
<addaction name="action_Fullscreen"/>
|
||||
<addaction name="action_Single_Window_Mode"/>
|
||||
<addaction name="action_Display_Dock_Widget_Headers"/>
|
||||
<addaction name="action_Enable_Overlay_Applet"/>
|
||||
<addaction name="action_Show_Filter_Bar"/>
|
||||
<addaction name="action_Show_Status_Bar"/>
|
||||
<addaction name="separator"/>
|
||||
@@ -298,12 +298,12 @@
|
||||
<enum>QAction::MenuRole::PreferencesRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Display_Dock_Widget_Headers">
|
||||
<action name="action_Enable_Overlay_Applet">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Display D&ock Widget Headers</string>
|
||||
<string>Enable Overlay Display Applet</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Show_Filter_Bar">
|
||||
|
||||
@@ -1513,8 +1513,7 @@ void MainWindow::RestoreUIState() {
|
||||
|
||||
ui->action_Fullscreen->setChecked(UISettings::values.fullscreen.GetValue());
|
||||
|
||||
ui->action_Display_Dock_Widget_Headers->setChecked(
|
||||
UISettings::values.display_titlebar.GetValue());
|
||||
ui->action_Enable_Overlay_Applet->setChecked(Settings::values.enable_overlay.GetValue());
|
||||
|
||||
ui->action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar.GetValue());
|
||||
game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked());
|
||||
@@ -4408,10 +4407,11 @@ void MainWindow::UpdateUISettings() {
|
||||
UISettings::values.state = saveState();
|
||||
UISettings::values.single_window_mode = ui->action_Single_Window_Mode->isChecked();
|
||||
UISettings::values.fullscreen = ui->action_Fullscreen->isChecked();
|
||||
UISettings::values.display_titlebar = ui->action_Display_Dock_Widget_Headers->isChecked();
|
||||
UISettings::values.show_filter_bar = ui->action_Show_Filter_Bar->isChecked();
|
||||
UISettings::values.show_status_bar = ui->action_Show_Status_Bar->isChecked();
|
||||
UISettings::values.first_start = false;
|
||||
|
||||
Settings::values.enable_overlay = ui->action_Enable_Overlay_Applet->isChecked();
|
||||
}
|
||||
|
||||
void MainWindow::UpdateInputDrivers() {
|
||||
|
||||
Reference in New Issue
Block a user