Compare commits
36 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
820bcee6a4 | ||
|
|
b6d2c64f4d | ||
|
|
e9265ac598 | ||
|
|
2caac4a395 | ||
|
|
5d46038c5c | ||
|
|
34e4aaddd9 | ||
|
|
a9fa890f14 | ||
|
|
d68795c665 | ||
|
|
32775125b7 | ||
|
|
048da7240d | ||
|
|
96535c13a5 | ||
|
|
ea680bea60 | ||
|
|
56cf5b7b17 | ||
|
|
02e6602baa | ||
|
|
4ee087fb3c | ||
|
|
19b2571aec | ||
|
|
6209fe0c27 | ||
|
|
8b433beff3 | ||
|
|
7cfb29de23 | ||
|
|
3cf7246e37 | ||
|
|
fed6ab14c3 | ||
|
|
e696ed1f4d | ||
|
|
a81645400f | ||
|
|
48fcb43585 | ||
|
|
5454494adb | ||
|
|
de16c1e453 | ||
|
|
ae53b84efd | ||
|
|
5b95de0c9c | ||
|
|
731b4bd691 | ||
|
|
ba03bfa430 | ||
|
|
5ce6b8fea7 | ||
|
|
0682a908c0 | ||
|
|
c7b6c9de9c | ||
|
|
76d515327b | ||
|
|
c70529c1ec | ||
|
|
7901de2b75 |
@@ -214,6 +214,15 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t
|
||||
return std::string(buffer, len);
|
||||
}
|
||||
|
||||
std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
|
||||
std::size_t max_len) {
|
||||
std::size_t len = 0;
|
||||
while (len < max_len && buffer[len] != '\0')
|
||||
++len;
|
||||
|
||||
return std::u16string(buffer.begin(), buffer.begin() + len);
|
||||
}
|
||||
|
||||
const char* TrimSourcePath(const char* path, const char* root) {
|
||||
const char* p = path;
|
||||
|
||||
|
||||
@@ -66,6 +66,14 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
|
||||
*/
|
||||
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len);
|
||||
|
||||
/**
|
||||
* Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
||||
* null-terminated, then the string ends at the greatest multiple of two less then or equal to
|
||||
* max_len_bytes.
|
||||
*/
|
||||
std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
|
||||
std::size_t max_len);
|
||||
|
||||
/**
|
||||
* Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
|
||||
* intended to be used to strip a system-specific build directory from the `__FILE__` macro,
|
||||
|
||||
@@ -77,6 +77,8 @@ add_library(core STATIC
|
||||
file_sys/vfs_vector.h
|
||||
file_sys/xts_archive.cpp
|
||||
file_sys/xts_archive.h
|
||||
frontend/applets/software_keyboard.cpp
|
||||
frontend/applets/software_keyboard.h
|
||||
frontend/emu_window.cpp
|
||||
frontend/emu_window.h
|
||||
frontend/framebuffer_layout.cpp
|
||||
@@ -150,6 +152,10 @@ add_library(core STATIC
|
||||
hle/service/am/applet_ae.h
|
||||
hle/service/am/applet_oe.cpp
|
||||
hle/service/am/applet_oe.h
|
||||
hle/service/am/applets/applets.cpp
|
||||
hle/service/am/applets/applets.h
|
||||
hle/service/am/applets/software_keyboard.cpp
|
||||
hle/service/am/applets/software_keyboard.h
|
||||
hle/service/am/idle.cpp
|
||||
hle/service/am/idle.h
|
||||
hle/service/am/omm.cpp
|
||||
|
||||
@@ -23,12 +23,14 @@
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/service/am/applets/software_keyboard.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "frontend/applets/software_keyboard.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
@@ -136,6 +138,10 @@ struct System::Impl {
|
||||
if (virtual_filesystem == nullptr)
|
||||
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||
|
||||
/// Create default implementations of applets if one is not provided.
|
||||
if (software_keyboard == nullptr)
|
||||
software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
|
||||
|
||||
auto main_process = Kernel::Process::Create(kernel, "main");
|
||||
kernel.MakeCurrentProcess(main_process.get());
|
||||
|
||||
@@ -289,6 +295,9 @@ struct System::Impl {
|
||||
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
|
||||
std::size_t active_core{}; ///< Active core, only used in single thread mode
|
||||
|
||||
/// Frontend applets
|
||||
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
|
||||
|
||||
/// Service manager
|
||||
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
||||
|
||||
@@ -488,6 +497,14 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
|
||||
return impl->virtual_filesystem;
|
||||
}
|
||||
|
||||
void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) {
|
||||
impl->software_keyboard = std::move(applet);
|
||||
}
|
||||
|
||||
const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
|
||||
return *impl->software_keyboard;
|
||||
}
|
||||
|
||||
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
|
||||
return impl->Init(emu_window);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
class SoftwareKeyboardApplet;
|
||||
} // namespace Core::Frontend
|
||||
|
||||
namespace FileSys {
|
||||
@@ -236,6 +237,10 @@ public:
|
||||
|
||||
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
|
||||
|
||||
void SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet);
|
||||
|
||||
const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
|
||||
|
||||
private:
|
||||
System();
|
||||
|
||||
|
||||
@@ -32,7 +32,18 @@ enum class GamecardSize : u8 {
|
||||
};
|
||||
|
||||
struct GamecardInfo {
|
||||
std::array<u8, 0x70> data;
|
||||
u64_le firmware_version;
|
||||
u32_le access_control_flags;
|
||||
u32_le read_wait_time1;
|
||||
u32_le read_wait_time2;
|
||||
u32_le write_wait_time1;
|
||||
u32_le write_wait_time2;
|
||||
u32_le firmware_mode;
|
||||
u32_le cup_version;
|
||||
std::array<u8, 4> reserved1;
|
||||
u64_le update_partition_hash;
|
||||
u64_le cup_id;
|
||||
std::array<u8, 0x38> reserved2;
|
||||
};
|
||||
static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size.");
|
||||
|
||||
|
||||
29
src/core/frontend/applets/software_keyboard.cpp
Normal file
29
src/core/frontend/applets/software_keyboard.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default;
|
||||
|
||||
void DefaultSoftwareKeyboardApplet::RequestText(
|
||||
std::function<void(std::optional<std::u16string>)> out,
|
||||
SoftwareKeyboardParameters parameters) const {
|
||||
if (parameters.initial_text.empty())
|
||||
out(u"yuzu");
|
||||
|
||||
out(parameters.initial_text);
|
||||
}
|
||||
|
||||
void DefaultSoftwareKeyboardApplet::SendTextCheckDialog(
|
||||
std::u16string error_message, std::function<void()> finished_check) const {
|
||||
LOG_WARNING(Service_AM,
|
||||
"(STUBBED) called - Default fallback software keyboard does not support text "
|
||||
"check! (error_message={})",
|
||||
Common::UTF16ToUTF8(error_message));
|
||||
finished_check();
|
||||
}
|
||||
} // namespace Core::Frontend
|
||||
54
src/core/frontend/applets/software_keyboard.h
Normal file
54
src/core/frontend/applets/software_keyboard.h
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
struct SoftwareKeyboardParameters {
|
||||
std::u16string submit_text;
|
||||
std::u16string header_text;
|
||||
std::u16string sub_text;
|
||||
std::u16string guide_text;
|
||||
std::u16string initial_text;
|
||||
std::size_t max_length;
|
||||
bool password;
|
||||
bool cursor_at_beginning;
|
||||
|
||||
union {
|
||||
u8 value;
|
||||
|
||||
BitField<1, 1, u8> disable_space;
|
||||
BitField<2, 1, u8> disable_address;
|
||||
BitField<3, 1, u8> disable_percent;
|
||||
BitField<4, 1, u8> disable_slash;
|
||||
BitField<6, 1, u8> disable_number;
|
||||
BitField<7, 1, u8> disable_download_code;
|
||||
};
|
||||
};
|
||||
|
||||
class SoftwareKeyboardApplet {
|
||||
public:
|
||||
virtual ~SoftwareKeyboardApplet();
|
||||
|
||||
virtual void RequestText(std::function<void(std::optional<std::u16string>)> out,
|
||||
SoftwareKeyboardParameters parameters) const = 0;
|
||||
virtual void SendTextCheckDialog(std::u16string error_message,
|
||||
std::function<void()> finished_check) const = 0;
|
||||
};
|
||||
|
||||
class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet {
|
||||
public:
|
||||
void RequestText(std::function<void(std::optional<std::u16string>)> out,
|
||||
SoftwareKeyboardParameters parameters) const override;
|
||||
void SendTextCheckDialog(std::u16string error_message,
|
||||
std::function<void()> finished_check) const override;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
@@ -105,7 +105,7 @@ struct KernelCore::Impl {
|
||||
void Initialize(KernelCore& kernel) {
|
||||
Shutdown();
|
||||
|
||||
InitializeResourceLimits(kernel);
|
||||
InitializeSystemResourceLimit(kernel);
|
||||
InitializeThreads();
|
||||
InitializeTimers();
|
||||
}
|
||||
@@ -118,7 +118,7 @@ struct KernelCore::Impl {
|
||||
process_list.clear();
|
||||
current_process = nullptr;
|
||||
|
||||
resource_limits.fill(nullptr);
|
||||
system_resource_limit = nullptr;
|
||||
|
||||
thread_wakeup_callback_handle_table.Clear();
|
||||
thread_wakeup_event_type = nullptr;
|
||||
@@ -129,63 +129,17 @@ struct KernelCore::Impl {
|
||||
named_ports.clear();
|
||||
}
|
||||
|
||||
void InitializeResourceLimits(KernelCore& kernel) {
|
||||
// Create the four resource limits that the system uses
|
||||
// Create the APPLICATION resource limit
|
||||
SharedPtr<ResourceLimit> resource_limit = ResourceLimit::Create(kernel, "Applications");
|
||||
resource_limit->max_priority = 0x18;
|
||||
resource_limit->max_commit = 0x4000000;
|
||||
resource_limit->max_threads = 0x20;
|
||||
resource_limit->max_events = 0x20;
|
||||
resource_limit->max_mutexes = 0x20;
|
||||
resource_limit->max_semaphores = 0x8;
|
||||
resource_limit->max_timers = 0x8;
|
||||
resource_limit->max_shared_mems = 0x10;
|
||||
resource_limit->max_address_arbiters = 0x2;
|
||||
resource_limit->max_cpu_time = 0x1E;
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::APPLICATION)] = resource_limit;
|
||||
// Creates the default system resource limit
|
||||
void InitializeSystemResourceLimit(KernelCore& kernel) {
|
||||
system_resource_limit = ResourceLimit::Create(kernel, "System");
|
||||
|
||||
// Create the SYS_APPLET resource limit
|
||||
resource_limit = ResourceLimit::Create(kernel, "System Applets");
|
||||
resource_limit->max_priority = 0x4;
|
||||
resource_limit->max_commit = 0x5E00000;
|
||||
resource_limit->max_threads = 0x1D;
|
||||
resource_limit->max_events = 0xB;
|
||||
resource_limit->max_mutexes = 0x8;
|
||||
resource_limit->max_semaphores = 0x4;
|
||||
resource_limit->max_timers = 0x4;
|
||||
resource_limit->max_shared_mems = 0x8;
|
||||
resource_limit->max_address_arbiters = 0x3;
|
||||
resource_limit->max_cpu_time = 0x2710;
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::SYS_APPLET)] = resource_limit;
|
||||
|
||||
// Create the LIB_APPLET resource limit
|
||||
resource_limit = ResourceLimit::Create(kernel, "Library Applets");
|
||||
resource_limit->max_priority = 0x4;
|
||||
resource_limit->max_commit = 0x600000;
|
||||
resource_limit->max_threads = 0xE;
|
||||
resource_limit->max_events = 0x8;
|
||||
resource_limit->max_mutexes = 0x8;
|
||||
resource_limit->max_semaphores = 0x4;
|
||||
resource_limit->max_timers = 0x4;
|
||||
resource_limit->max_shared_mems = 0x8;
|
||||
resource_limit->max_address_arbiters = 0x1;
|
||||
resource_limit->max_cpu_time = 0x2710;
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::LIB_APPLET)] = resource_limit;
|
||||
|
||||
// Create the OTHER resource limit
|
||||
resource_limit = ResourceLimit::Create(kernel, "Others");
|
||||
resource_limit->max_priority = 0x4;
|
||||
resource_limit->max_commit = 0x2180000;
|
||||
resource_limit->max_threads = 0xE1;
|
||||
resource_limit->max_events = 0x108;
|
||||
resource_limit->max_mutexes = 0x25;
|
||||
resource_limit->max_semaphores = 0x43;
|
||||
resource_limit->max_timers = 0x2C;
|
||||
resource_limit->max_shared_mems = 0x1F;
|
||||
resource_limit->max_address_arbiters = 0x2D;
|
||||
resource_limit->max_cpu_time = 0x3E8;
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::OTHER)] = resource_limit;
|
||||
// If setting the default system values fails, then something seriously wrong has occurred.
|
||||
ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000)
|
||||
.IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Threads, 800).IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Events, 700).IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(ResourceType::TransferMemory, 200).IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess());
|
||||
}
|
||||
|
||||
void InitializeThreads() {
|
||||
@@ -208,7 +162,7 @@ struct KernelCore::Impl {
|
||||
std::vector<SharedPtr<Process>> process_list;
|
||||
Process* current_process = nullptr;
|
||||
|
||||
std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
|
||||
SharedPtr<ResourceLimit> system_resource_limit;
|
||||
|
||||
/// The event type of the generic timer callback event
|
||||
CoreTiming::EventType* timer_callback_event_type = nullptr;
|
||||
@@ -239,9 +193,8 @@ void KernelCore::Shutdown() {
|
||||
impl->Shutdown();
|
||||
}
|
||||
|
||||
SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory(
|
||||
ResourceLimitCategory category) const {
|
||||
return impl->resource_limits.at(static_cast<std::size_t>(category));
|
||||
SharedPtr<ResourceLimit> KernelCore::GetSystemResourceLimit() const {
|
||||
return impl->system_resource_limit;
|
||||
}
|
||||
|
||||
SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const {
|
||||
|
||||
@@ -24,8 +24,6 @@ class ResourceLimit;
|
||||
class Thread;
|
||||
class Timer;
|
||||
|
||||
enum class ResourceLimitCategory : u8;
|
||||
|
||||
/// Represents a single instance of the kernel.
|
||||
class KernelCore {
|
||||
private:
|
||||
@@ -47,8 +45,8 @@ public:
|
||||
/// Clears all resources in use by the kernel instance.
|
||||
void Shutdown();
|
||||
|
||||
/// Retrieves a shared pointer to a ResourceLimit identified by the given category.
|
||||
SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const;
|
||||
/// Retrieves a shared pointer to the system resource limit instance.
|
||||
SharedPtr<ResourceLimit> GetSystemResourceLimit() const;
|
||||
|
||||
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
|
||||
SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const;
|
||||
|
||||
@@ -28,7 +28,7 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
|
||||
process->name = std::move(name);
|
||||
process->flags.raw = 0;
|
||||
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
|
||||
process->resource_limit = kernel.ResourceLimitForCategory(ResourceLimitCategory::APPLICATION);
|
||||
process->resource_limit = kernel.GetSystemResourceLimit();
|
||||
process->status = ProcessStatus::Created;
|
||||
process->program_id = 0;
|
||||
process->process_id = kernel.CreateNewProcessID();
|
||||
|
||||
@@ -2,12 +2,16 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
constexpr std::size_t ResourceTypeToIndex(ResourceType type) {
|
||||
return static_cast<std::size_t>(type);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
|
||||
ResourceLimit::~ResourceLimit() = default;
|
||||
@@ -19,59 +23,22 @@ SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel, std::string n
|
||||
return resource_limit;
|
||||
}
|
||||
|
||||
s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
|
||||
switch (resource) {
|
||||
case ResourceType::Commit:
|
||||
return current_commit;
|
||||
case ResourceType::Thread:
|
||||
return current_threads;
|
||||
case ResourceType::Event:
|
||||
return current_events;
|
||||
case ResourceType::Mutex:
|
||||
return current_mutexes;
|
||||
case ResourceType::Semaphore:
|
||||
return current_semaphores;
|
||||
case ResourceType::Timer:
|
||||
return current_timers;
|
||||
case ResourceType::SharedMemory:
|
||||
return current_shared_mems;
|
||||
case ResourceType::AddressArbiter:
|
||||
return current_address_arbiters;
|
||||
case ResourceType::CPUTime:
|
||||
return current_cpu_time;
|
||||
default:
|
||||
LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource));
|
||||
UNIMPLEMENTED();
|
||||
return 0;
|
||||
}
|
||||
s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
|
||||
return values.at(ResourceTypeToIndex(resource));
|
||||
}
|
||||
|
||||
u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
|
||||
switch (resource) {
|
||||
case ResourceType::Priority:
|
||||
return max_priority;
|
||||
case ResourceType::Commit:
|
||||
return max_commit;
|
||||
case ResourceType::Thread:
|
||||
return max_threads;
|
||||
case ResourceType::Event:
|
||||
return max_events;
|
||||
case ResourceType::Mutex:
|
||||
return max_mutexes;
|
||||
case ResourceType::Semaphore:
|
||||
return max_semaphores;
|
||||
case ResourceType::Timer:
|
||||
return max_timers;
|
||||
case ResourceType::SharedMemory:
|
||||
return max_shared_mems;
|
||||
case ResourceType::AddressArbiter:
|
||||
return max_address_arbiters;
|
||||
case ResourceType::CPUTime:
|
||||
return max_cpu_time;
|
||||
default:
|
||||
LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource));
|
||||
UNIMPLEMENTED();
|
||||
return 0;
|
||||
s64 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
|
||||
return limits.at(ResourceTypeToIndex(resource));
|
||||
}
|
||||
|
||||
ResultCode ResourceLimit::SetLimitValue(ResourceType resource, s64 value) {
|
||||
const auto index = ResourceTypeToIndex(resource);
|
||||
|
||||
if (value < values[index]) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
values[index] = value;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -4,31 +4,25 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
|
||||
enum class ResourceLimitCategory : u8 {
|
||||
APPLICATION = 0,
|
||||
SYS_APPLET = 1,
|
||||
LIB_APPLET = 2,
|
||||
OTHER = 3
|
||||
};
|
||||
|
||||
enum class ResourceType {
|
||||
Priority = 0,
|
||||
Commit = 1,
|
||||
Thread = 2,
|
||||
Event = 3,
|
||||
Mutex = 4,
|
||||
Semaphore = 5,
|
||||
Timer = 6,
|
||||
SharedMemory = 7,
|
||||
AddressArbiter = 8,
|
||||
CPUTime = 9,
|
||||
PhysicalMemory,
|
||||
Threads,
|
||||
Events,
|
||||
TransferMemory,
|
||||
Sessions,
|
||||
|
||||
// Used as a count, not an actual type.
|
||||
ResourceTypeCount
|
||||
};
|
||||
|
||||
class ResourceLimit final : public Object {
|
||||
@@ -55,61 +49,51 @@ public:
|
||||
* @param resource Requested resource type
|
||||
* @returns The current value of the resource type
|
||||
*/
|
||||
s32 GetCurrentResourceValue(ResourceType resource) const;
|
||||
s64 GetCurrentResourceValue(ResourceType resource) const;
|
||||
|
||||
/**
|
||||
* Gets the max value for the specified resource.
|
||||
* @param resource Requested resource type
|
||||
* @returns The max value of the resource type
|
||||
*/
|
||||
u32 GetMaxResourceValue(ResourceType resource) const;
|
||||
s64 GetMaxResourceValue(ResourceType resource) const;
|
||||
|
||||
/// Name of resource limit object.
|
||||
std::string name;
|
||||
|
||||
/// Max thread priority that a process in this category can create
|
||||
s32 max_priority = 0;
|
||||
|
||||
/// Max memory that processes in this category can use
|
||||
s32 max_commit = 0;
|
||||
|
||||
///< Max number of objects that can be collectively created by the processes in this category
|
||||
s32 max_threads = 0;
|
||||
s32 max_events = 0;
|
||||
s32 max_mutexes = 0;
|
||||
s32 max_semaphores = 0;
|
||||
s32 max_timers = 0;
|
||||
s32 max_shared_mems = 0;
|
||||
s32 max_address_arbiters = 0;
|
||||
|
||||
/// Max CPU time that the processes in this category can utilize
|
||||
s32 max_cpu_time = 0;
|
||||
|
||||
// TODO(Subv): Increment these in their respective Kernel::T::Create functions, keeping in mind
|
||||
// that APPLICATION resource limits should not be affected by the objects created by service
|
||||
// modules.
|
||||
// Currently we have no way of distinguishing if a Create was called by the running application,
|
||||
// or by a service module. Approach this once we have separated the service modules into their
|
||||
// own processes
|
||||
|
||||
/// Current memory that the processes in this category are using
|
||||
s32 current_commit = 0;
|
||||
|
||||
///< Current number of objects among all processes in this category
|
||||
s32 current_threads = 0;
|
||||
s32 current_events = 0;
|
||||
s32 current_mutexes = 0;
|
||||
s32 current_semaphores = 0;
|
||||
s32 current_timers = 0;
|
||||
s32 current_shared_mems = 0;
|
||||
s32 current_address_arbiters = 0;
|
||||
|
||||
/// Current CPU time that the processes in this category are utilizing
|
||||
s32 current_cpu_time = 0;
|
||||
/**
|
||||
* Sets the limit value for a given resource type.
|
||||
*
|
||||
* @param resource The resource type to apply the limit to.
|
||||
* @param value The limit to apply to the given resource type.
|
||||
*
|
||||
* @return A result code indicating if setting the limit value
|
||||
* was successful or not.
|
||||
*
|
||||
* @note The supplied limit value *must* be greater than or equal to
|
||||
* the current resource value for the given resource type,
|
||||
* otherwise ERR_INVALID_STATE will be returned.
|
||||
*/
|
||||
ResultCode SetLimitValue(ResourceType resource, s64 value);
|
||||
|
||||
private:
|
||||
explicit ResourceLimit(KernelCore& kernel);
|
||||
~ResourceLimit() override;
|
||||
|
||||
// TODO(Subv): Increment resource limit current values in their respective Kernel::T::Create
|
||||
// functions
|
||||
//
|
||||
// Currently we have no way of distinguishing if a Create was called by the running application,
|
||||
// or by a service module. Approach this once we have separated the service modules into their
|
||||
// own processes
|
||||
|
||||
using ResourceArray =
|
||||
std::array<s64, static_cast<std::size_t>(ResourceType::ResourceTypeCount)>;
|
||||
|
||||
/// Maximum values a resource type may reach.
|
||||
ResourceArray limits{};
|
||||
/// Current resource limit values.
|
||||
ResourceArray values{};
|
||||
|
||||
/// Name of resource limit object.
|
||||
std::string name;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -736,13 +736,6 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
|
||||
|
||||
const auto* const current_process = Core::CurrentProcess();
|
||||
|
||||
// Note: The kernel uses the current process's resource limit instead of
|
||||
// the one from the thread owner's resource limit.
|
||||
const ResourceLimit& resource_limit = current_process->GetResourceLimit();
|
||||
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
|
||||
if (!thread) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
@@ -885,10 +878,6 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
|
||||
}
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
const ResourceLimit& resource_limit = current_process->GetResourceLimit();
|
||||
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
if (processor_id == THREADPROCESSORID_DEFAULT) {
|
||||
// Set the target CPU to the one specified in the process' exheader.
|
||||
@@ -1194,9 +1183,39 @@ static ResultCode ResetSignal(Handle handle) {
|
||||
|
||||
/// Creates a TransferMemory object
|
||||
static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) {
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size,
|
||||
permissions);
|
||||
*handle = 0;
|
||||
LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size,
|
||||
permissions);
|
||||
|
||||
if (!Common::Is4KBAligned(addr)) {
|
||||
LOG_ERROR(Kernel_SVC, "Address ({:016X}) is not page aligned!", addr);
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (!Common::Is4KBAligned(size) || size == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Size ({:016X}) is not page aligned or equal to zero!", size);
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (!IsValidAddressRange(addr, size)) {
|
||||
LOG_ERROR(Kernel_SVC, "Address and size cause overflow! (address={:016X}, size={:016X})",
|
||||
addr, size);
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
const auto perms = static_cast<MemoryPermission>(permissions);
|
||||
if (perms != MemoryPermission::None && perms != MemoryPermission::Read &&
|
||||
perms != MemoryPermission::ReadWrite) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})",
|
||||
permissions);
|
||||
return ERR_INVALID_MEMORY_PERMISSIONS;
|
||||
}
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
const auto shared_mem_handle = SharedMemory::Create(
|
||||
kernel, handle_table.Get<Process>(CurrentProcess), size, perms, perms, addr);
|
||||
|
||||
CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle));
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,14 @@
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <stack>
|
||||
#include "applets/applets.h"
|
||||
#include "applets/software_keyboard.h"
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
@@ -28,6 +32,13 @@
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
|
||||
constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
|
||||
|
||||
enum class AppletId : u32 {
|
||||
SoftwareKeyboard = 0x11,
|
||||
};
|
||||
|
||||
constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
|
||||
|
||||
struct LaunchParameters {
|
||||
@@ -481,6 +492,24 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
IStorage::IStorage(std::vector<u8> buffer)
|
||||
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorage::Open, "Open"},
|
||||
{1, nullptr, "OpenTransferStorage"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IStorage::~IStorage() = default;
|
||||
|
||||
const std::vector<u8>& IStorage::GetData() const {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
|
||||
const bool use_docked_mode{Settings::values.use_docked_mode};
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
@@ -500,100 +529,15 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
|
||||
public:
|
||||
explicit IStorageAccessor(std::vector<u8> buffer)
|
||||
: ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorageAccessor::GetSize, "GetSize"},
|
||||
{10, &IStorageAccessor::Write, "Write"},
|
||||
{11, &IStorageAccessor::Read, "Read"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<u8> buffer;
|
||||
|
||||
void GetSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(static_cast<u64>(buffer.size()));
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void Write(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 offset{rp.Pop<u64>()};
|
||||
const std::vector<u8> data{ctx.ReadBuffer()};
|
||||
|
||||
ASSERT(offset + data.size() <= buffer.size());
|
||||
|
||||
std::memcpy(&buffer[offset], data.data(), data.size());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, offset={}", offset);
|
||||
}
|
||||
|
||||
void Read(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 offset{rp.Pop<u64>()};
|
||||
const std::size_t size{ctx.GetWriteBufferSize()};
|
||||
|
||||
ASSERT(offset + size <= buffer.size());
|
||||
|
||||
ctx.WriteBuffer(buffer.data() + offset, size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, offset={}", offset);
|
||||
}
|
||||
};
|
||||
|
||||
class IStorage final : public ServiceFramework<IStorage> {
|
||||
public:
|
||||
explicit IStorage(std::vector<u8> buffer)
|
||||
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorage::Open, "Open"},
|
||||
{1, nullptr, "OpenTransferStorage"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<u8> buffer;
|
||||
|
||||
void Open(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<AM::IStorageAccessor>(buffer);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
};
|
||||
|
||||
class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
|
||||
public:
|
||||
explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
|
||||
explicit ILibraryAppletAccessor(std::shared_ptr<Applets::Applet> applet)
|
||||
: ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)),
|
||||
broker(std::make_shared<Applets::AppletDataBroker>()) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
|
||||
{1, nullptr, "IsCompleted"},
|
||||
{1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"},
|
||||
{10, &ILibraryAppletAccessor::Start, "Start"},
|
||||
{20, nullptr, "RequestExit"},
|
||||
{25, nullptr, "Terminate"},
|
||||
@@ -602,10 +546,10 @@ public:
|
||||
{100, &ILibraryAppletAccessor::PushInData, "PushInData"},
|
||||
{101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
|
||||
{102, nullptr, "PushExtraStorage"},
|
||||
{103, nullptr, "PushInteractiveInData"},
|
||||
{104, nullptr, "PopInteractiveOutData"},
|
||||
{105, nullptr, "GetPopOutDataEvent"},
|
||||
{106, nullptr, "GetPopInteractiveOutDataEvent"},
|
||||
{103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"},
|
||||
{104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"},
|
||||
{105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"},
|
||||
{106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"},
|
||||
{110, nullptr, "NeedsToExitProcess"},
|
||||
{120, nullptr, "GetLibraryAppletInfo"},
|
||||
{150, nullptr, "RequestForAppletToGetForeground"},
|
||||
@@ -614,40 +558,50 @@ public:
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"ILibraryAppletAccessor:StateChangedEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
|
||||
state_changed_event->Signal();
|
||||
const auto event = broker->GetStateChangedEvent();
|
||||
event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(state_changed_event);
|
||||
rb.PushCopyObjects(event);
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void IsCompleted(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(applet->TransactionComplete());
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetResult(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(applet->GetStatus());
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void Start(Kernel::HLERequestContext& ctx) {
|
||||
ASSERT(applet != nullptr);
|
||||
|
||||
applet->Initialize(broker);
|
||||
applet->Execute();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void PushInData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
storage_stack.push(rp.PopIpcInterface<AM::IStorage>());
|
||||
broker->PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -657,25 +611,146 @@ private:
|
||||
|
||||
void PopOutData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<AM::IStorage>(std::move(storage_stack.top()));
|
||||
|
||||
storage_stack.pop();
|
||||
const auto storage = broker->PopNormalDataToGame();
|
||||
if (storage == nullptr) {
|
||||
rb.Push(ERR_NO_DATA_IN_CHANNEL);
|
||||
return;
|
||||
}
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IStorage>(std::move(*storage));
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
std::stack<std::shared_ptr<AM::IStorage>> storage_stack;
|
||||
Kernel::SharedPtr<Kernel::Event> state_changed_event;
|
||||
void PushInteractiveInData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
broker->PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>());
|
||||
|
||||
ASSERT(applet->IsInitialized());
|
||||
applet->ExecuteInteractive();
|
||||
applet->Execute();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
const auto storage = broker->PopInteractiveDataToGame();
|
||||
if (storage == nullptr) {
|
||||
rb.Push(ERR_NO_DATA_IN_CHANNEL);
|
||||
return;
|
||||
}
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IStorage>(std::move(*storage));
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(broker->GetNormalDataEvent());
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(broker->GetInteractiveDataEvent());
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
std::shared_ptr<Applets::Applet> applet;
|
||||
std::shared_ptr<Applets::AppletDataBroker> broker;
|
||||
};
|
||||
|
||||
void IStorage::Open(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IStorageAccessor>(*this);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
IStorageAccessor::IStorageAccessor(IStorage& storage)
|
||||
: ServiceFramework("IStorageAccessor"), backing(storage) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorageAccessor::GetSize, "GetSize"},
|
||||
{10, &IStorageAccessor::Write, "Write"},
|
||||
{11, &IStorageAccessor::Read, "Read"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IStorageAccessor::~IStorageAccessor() = default;
|
||||
|
||||
void IStorageAccessor::GetSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(static_cast<u64>(backing.buffer.size()));
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 offset{rp.Pop<u64>()};
|
||||
const std::vector<u8> data{ctx.ReadBuffer()};
|
||||
|
||||
if (data.size() > backing.buffer.size() - offset) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
|
||||
}
|
||||
|
||||
std::memcpy(backing.buffer.data() + offset, data.data(), data.size());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, offset={}", offset);
|
||||
}
|
||||
|
||||
void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 offset{rp.Pop<u64>()};
|
||||
const std::size_t size{ctx.GetWriteBufferSize()};
|
||||
|
||||
if (size > backing.buffer.size() - offset) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(backing.buffer.data() + offset, size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, offset={}", offset);
|
||||
}
|
||||
|
||||
ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
|
||||
{1, nullptr, "TerminateAllLibraryApplets"},
|
||||
{2, nullptr, "AreAnyLibraryAppletsLeft"},
|
||||
{10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
|
||||
{11, nullptr, "CreateTransferMemoryStorage"},
|
||||
{11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"},
|
||||
{12, nullptr, "CreateHandleStorage"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
@@ -683,11 +758,36 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
|
||||
|
||||
ILibraryAppletCreator::~ILibraryAppletCreator() = default;
|
||||
|
||||
static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
|
||||
switch (id) {
|
||||
case AppletId::SoftwareKeyboard:
|
||||
return std::make_shared<Applets::SoftwareKeyboard>();
|
||||
default:
|
||||
UNREACHABLE_MSG("Unimplemented AppletId [{:08X}]!", static_cast<u32>(id));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_id = rp.PopRaw<AppletId>();
|
||||
const auto applet_mode = rp.PopRaw<u32>();
|
||||
|
||||
LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}",
|
||||
static_cast<u32>(applet_id), applet_mode);
|
||||
|
||||
const auto applet = GetAppletFromId(applet_id);
|
||||
|
||||
if (applet == nullptr) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<AM::ILibraryAppletAccessor>();
|
||||
rb.PushIpcInterface<AM::ILibraryAppletAccessor>(applet);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
@@ -704,6 +804,31 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called, size={}", size);
|
||||
}
|
||||
|
||||
void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
rp.SetCurrentOffset(3);
|
||||
const auto handle{rp.Pop<Kernel::Handle>()};
|
||||
|
||||
const auto shared_mem =
|
||||
Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::SharedMemory>(
|
||||
handle);
|
||||
|
||||
if (shared_mem == nullptr) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto mem_begin = shared_mem->backing_block->begin() + shared_mem->backing_block_offset;
|
||||
const auto mem_end = mem_begin + shared_mem->size;
|
||||
std::vector<u8> memory{mem_begin, mem_end};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory)));
|
||||
}
|
||||
|
||||
IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
|
||||
@@ -155,6 +155,34 @@ private:
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
class IStorage final : public ServiceFramework<IStorage> {
|
||||
public:
|
||||
explicit IStorage(std::vector<u8> buffer);
|
||||
~IStorage() override;
|
||||
|
||||
const std::vector<u8>& GetData() const;
|
||||
|
||||
private:
|
||||
void Open(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::vector<u8> buffer;
|
||||
|
||||
friend class IStorageAccessor;
|
||||
};
|
||||
|
||||
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
|
||||
public:
|
||||
explicit IStorageAccessor(IStorage& backing);
|
||||
~IStorageAccessor() override;
|
||||
|
||||
private:
|
||||
void GetSize(Kernel::HLERequestContext& ctx);
|
||||
void Write(Kernel::HLERequestContext& ctx);
|
||||
void Read(Kernel::HLERequestContext& ctx);
|
||||
|
||||
IStorage& backing;
|
||||
};
|
||||
|
||||
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
|
||||
public:
|
||||
ILibraryAppletCreator();
|
||||
@@ -163,6 +191,7 @@ public:
|
||||
private:
|
||||
void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
|
||||
void CreateStorage(Kernel::HLERequestContext& ctx);
|
||||
void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
|
||||
|
||||
115
src/core/hle/service/am/applets/applets.cpp
Normal file
115
src/core/hle/service/am/applets/applets.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
AppletDataBroker::AppletDataBroker() {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"ILibraryAppletAccessor:StateChangedEvent");
|
||||
pop_out_data_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"ILibraryAppletAccessor:PopDataOutEvent");
|
||||
pop_interactive_out_data_event = Kernel::Event::Create(
|
||||
kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
|
||||
}
|
||||
|
||||
AppletDataBroker::~AppletDataBroker() = default;
|
||||
|
||||
std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
|
||||
if (out_channel.empty())
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(out_channel.front());
|
||||
out_channel.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
|
||||
if (in_channel.empty())
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(in_channel.front());
|
||||
in_channel.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
|
||||
if (out_interactive_channel.empty())
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(out_interactive_channel.front());
|
||||
out_interactive_channel.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
|
||||
if (in_interactive_channel.empty())
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(in_interactive_channel.front());
|
||||
in_interactive_channel.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushNormalDataFromGame(IStorage storage) {
|
||||
in_channel.push(std::make_unique<IStorage>(storage));
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) {
|
||||
out_channel.push(std::make_unique<IStorage>(storage));
|
||||
pop_out_data_event->Signal();
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) {
|
||||
in_interactive_channel.push(std::make_unique<IStorage>(storage));
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) {
|
||||
out_interactive_channel.push(std::make_unique<IStorage>(storage));
|
||||
pop_interactive_out_data_event->Signal();
|
||||
}
|
||||
|
||||
void AppletDataBroker::SignalStateChanged() const {
|
||||
state_changed_event->Signal();
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetNormalDataEvent() const {
|
||||
return pop_out_data_event;
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetInteractiveDataEvent() const {
|
||||
return pop_interactive_out_data_event;
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetStateChangedEvent() const {
|
||||
return state_changed_event;
|
||||
}
|
||||
|
||||
Applet::Applet() = default;
|
||||
|
||||
Applet::~Applet() = default;
|
||||
|
||||
void Applet::Initialize(std::shared_ptr<AppletDataBroker> broker_) {
|
||||
broker = std::move(broker_);
|
||||
|
||||
const auto common = broker->PopNormalDataToApplet();
|
||||
ASSERT(common != nullptr);
|
||||
|
||||
const auto common_data = common->GetData();
|
||||
|
||||
ASSERT(common_data.size() >= sizeof(CommonArguments));
|
||||
std::memcpy(&common_args, common_data.data(), sizeof(CommonArguments));
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
94
src/core/hle/service/am/applets/applets.h
Normal file
94
src/core/hle/service/am/applets/applets.h
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
class IStorage;
|
||||
|
||||
namespace Applets {
|
||||
|
||||
class AppletDataBroker final {
|
||||
public:
|
||||
AppletDataBroker();
|
||||
~AppletDataBroker();
|
||||
|
||||
std::unique_ptr<IStorage> PopNormalDataToGame();
|
||||
std::unique_ptr<IStorage> PopNormalDataToApplet();
|
||||
|
||||
std::unique_ptr<IStorage> PopInteractiveDataToGame();
|
||||
std::unique_ptr<IStorage> PopInteractiveDataToApplet();
|
||||
|
||||
void PushNormalDataFromGame(IStorage storage);
|
||||
void PushNormalDataFromApplet(IStorage storage);
|
||||
|
||||
void PushInteractiveDataFromGame(IStorage storage);
|
||||
void PushInteractiveDataFromApplet(IStorage storage);
|
||||
|
||||
void SignalStateChanged() const;
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> GetNormalDataEvent() const;
|
||||
Kernel::SharedPtr<Kernel::Event> GetInteractiveDataEvent() const;
|
||||
Kernel::SharedPtr<Kernel::Event> GetStateChangedEvent() const;
|
||||
|
||||
private:
|
||||
// Queues are named from applet's perspective
|
||||
std::queue<std::unique_ptr<IStorage>>
|
||||
in_channel; // PopNormalDataToApplet and PushNormalDataFromGame
|
||||
std::queue<std::unique_ptr<IStorage>>
|
||||
out_channel; // PopNormalDataToGame and PushNormalDataFromApplet
|
||||
std::queue<std::unique_ptr<IStorage>>
|
||||
in_interactive_channel; // PopInteractiveDataToApplet and PushInteractiveDataFromGame
|
||||
std::queue<std::unique_ptr<IStorage>>
|
||||
out_interactive_channel; // PopInteractiveDataToGame and PushInteractiveDataFromApplet
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> state_changed_event;
|
||||
Kernel::SharedPtr<Kernel::Event> pop_out_data_event; // Signaled on PushNormalDataFromApplet
|
||||
Kernel::SharedPtr<Kernel::Event>
|
||||
pop_interactive_out_data_event; // Signaled on PushInteractiveDataFromApplet
|
||||
};
|
||||
|
||||
class Applet {
|
||||
public:
|
||||
Applet();
|
||||
virtual ~Applet();
|
||||
|
||||
virtual void Initialize(std::shared_ptr<AppletDataBroker> broker);
|
||||
|
||||
virtual bool TransactionComplete() const = 0;
|
||||
virtual ResultCode GetStatus() const = 0;
|
||||
virtual void ExecuteInteractive() = 0;
|
||||
virtual void Execute() = 0;
|
||||
|
||||
bool IsInitialized() const {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
protected:
|
||||
struct CommonArguments {
|
||||
u32_le arguments_version;
|
||||
u32_le size;
|
||||
u32_le library_version;
|
||||
u32_le theme_color;
|
||||
u8 play_startup_sound;
|
||||
u64_le system_tick;
|
||||
};
|
||||
static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size.");
|
||||
|
||||
CommonArguments common_args;
|
||||
std::shared_ptr<AppletDataBroker> broker;
|
||||
bool initialized = false;
|
||||
};
|
||||
|
||||
} // namespace Applets
|
||||
} // namespace Service::AM
|
||||
161
src/core/hle/service/am/applets/software_keyboard.cpp
Normal file
161
src/core/hle/service/am/applets/software_keyboard.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/software_keyboard.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8;
|
||||
constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4;
|
||||
constexpr std::size_t DEFAULT_MAX_LENGTH = 500;
|
||||
constexpr bool INTERACTIVE_STATUS_OK = false;
|
||||
|
||||
static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
|
||||
KeyboardConfig config, std::u16string initial_text) {
|
||||
Core::Frontend::SoftwareKeyboardParameters params{};
|
||||
|
||||
params.submit_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
config.submit_text.data(), config.submit_text.size());
|
||||
params.header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
config.header_text.data(), config.header_text.size());
|
||||
params.sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.sub_text.data(),
|
||||
config.sub_text.size());
|
||||
params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(),
|
||||
config.guide_text.size());
|
||||
params.initial_text = initial_text;
|
||||
params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit;
|
||||
params.password = static_cast<bool>(config.is_password);
|
||||
params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position);
|
||||
params.value = static_cast<u8>(config.keyset_disable_bitmask);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
SoftwareKeyboard::SoftwareKeyboard() = default;
|
||||
|
||||
SoftwareKeyboard::~SoftwareKeyboard() = default;
|
||||
|
||||
void SoftwareKeyboard::Initialize(std::shared_ptr<AppletDataBroker> broker_) {
|
||||
complete = false;
|
||||
initial_text.clear();
|
||||
final_data.clear();
|
||||
|
||||
Applet::Initialize(std::move(broker_));
|
||||
|
||||
const auto keyboard_config_storage = broker->PopNormalDataToApplet();
|
||||
ASSERT(keyboard_config_storage != nullptr);
|
||||
const auto& keyboard_config = keyboard_config_storage->GetData();
|
||||
|
||||
ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig));
|
||||
std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig));
|
||||
|
||||
const auto work_buffer_storage = broker->PopNormalDataToApplet();
|
||||
ASSERT(work_buffer_storage != nullptr);
|
||||
const auto& work_buffer = work_buffer_storage->GetData();
|
||||
|
||||
if (config.initial_string_size == 0)
|
||||
return;
|
||||
|
||||
std::vector<char16_t> string(config.initial_string_size);
|
||||
std::memcpy(string.data(), work_buffer.data() + config.initial_string_offset,
|
||||
string.size() * 2);
|
||||
initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size());
|
||||
}
|
||||
|
||||
bool SoftwareKeyboard::TransactionComplete() const {
|
||||
return complete;
|
||||
}
|
||||
|
||||
ResultCode SoftwareKeyboard::GetStatus() const {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::ExecuteInteractive() {
|
||||
if (complete)
|
||||
return;
|
||||
|
||||
const auto storage = broker->PopInteractiveDataToApplet();
|
||||
ASSERT(storage != nullptr);
|
||||
const auto data = storage->GetData();
|
||||
const auto status = static_cast<bool>(data[0]);
|
||||
|
||||
if (status == INTERACTIVE_STATUS_OK) {
|
||||
complete = true;
|
||||
} else {
|
||||
const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
|
||||
|
||||
std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
|
||||
std::memcpy(string.data(), data.data() + 4, string.size() * 2);
|
||||
frontend.SendTextCheckDialog(
|
||||
Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()),
|
||||
[this] { broker->SignalStateChanged(); });
|
||||
}
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::Execute() {
|
||||
if (complete) {
|
||||
broker->PushNormalDataFromApplet(IStorage{final_data});
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
|
||||
|
||||
const auto parameters = ConvertToFrontendParameters(config, initial_text);
|
||||
|
||||
frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); },
|
||||
parameters);
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
|
||||
std::vector<u8> output_main(SWKBD_OUTPUT_BUFFER_SIZE);
|
||||
|
||||
if (text.has_value()) {
|
||||
std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE);
|
||||
|
||||
if (config.utf_8) {
|
||||
const u64 size = text->size() + 8;
|
||||
const auto new_text = Common::UTF16ToUTF8(*text);
|
||||
|
||||
std::memcpy(output_sub.data(), &size, sizeof(u64));
|
||||
std::memcpy(output_sub.data() + 8, new_text.data(),
|
||||
std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 8));
|
||||
|
||||
output_main[0] = INTERACTIVE_STATUS_OK;
|
||||
std::memcpy(output_main.data() + 4, new_text.data(),
|
||||
std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4));
|
||||
} else {
|
||||
const u64 size = text->size() * 2 + 8;
|
||||
std::memcpy(output_sub.data(), &size, sizeof(u64));
|
||||
std::memcpy(output_sub.data() + 8, text->data(),
|
||||
std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8));
|
||||
|
||||
output_main[0] = INTERACTIVE_STATUS_OK;
|
||||
std::memcpy(output_main.data() + 4, text->data(),
|
||||
std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4));
|
||||
}
|
||||
|
||||
complete = !config.text_check;
|
||||
final_data = output_main;
|
||||
|
||||
if (complete) {
|
||||
broker->PushNormalDataFromApplet(IStorage{output_main});
|
||||
} else {
|
||||
broker->PushInteractiveDataFromApplet(IStorage{output_sub});
|
||||
}
|
||||
|
||||
broker->SignalStateChanged();
|
||||
} else {
|
||||
output_main[0] = 1;
|
||||
complete = true;
|
||||
broker->PushNormalDataFromApplet(IStorage{output_main});
|
||||
broker->SignalStateChanged();
|
||||
}
|
||||
}
|
||||
} // namespace Service::AM::Applets
|
||||
69
src/core/hle/service/am/applets/software_keyboard.h
Normal file
69
src/core/hle/service/am/applets/software_keyboard.h
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
enum class KeysetDisable : u32 {
|
||||
Space = 0x02,
|
||||
Address = 0x04,
|
||||
Percent = 0x08,
|
||||
Slashes = 0x10,
|
||||
Numbers = 0x40,
|
||||
DownloadCode = 0x80,
|
||||
};
|
||||
|
||||
struct KeyboardConfig {
|
||||
INSERT_PADDING_BYTES(4);
|
||||
std::array<char16_t, 9> submit_text;
|
||||
u16_le left_symbol_key;
|
||||
u16_le right_symbol_key;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
KeysetDisable keyset_disable_bitmask;
|
||||
u32_le initial_cursor_position;
|
||||
std::array<char16_t, 65> header_text;
|
||||
std::array<char16_t, 129> sub_text;
|
||||
std::array<char16_t, 257> guide_text;
|
||||
u32_le length_limit;
|
||||
INSERT_PADDING_BYTES(4);
|
||||
u32_le is_password;
|
||||
INSERT_PADDING_BYTES(5);
|
||||
bool utf_8;
|
||||
bool draw_background;
|
||||
u32_le initial_string_offset;
|
||||
u32_le initial_string_size;
|
||||
u32_le user_dictionary_offset;
|
||||
u32_le user_dictionary_size;
|
||||
bool text_check;
|
||||
u64_le text_check_callback;
|
||||
};
|
||||
static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect size.");
|
||||
|
||||
class SoftwareKeyboard final : public Applet {
|
||||
public:
|
||||
SoftwareKeyboard();
|
||||
~SoftwareKeyboard() override;
|
||||
|
||||
void Initialize(std::shared_ptr<AppletDataBroker> broker) override;
|
||||
|
||||
bool TransactionComplete() const override;
|
||||
ResultCode GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
|
||||
void WriteText(std::optional<std::u16string> text);
|
||||
|
||||
private:
|
||||
KeyboardConfig config;
|
||||
std::u16string initial_text;
|
||||
bool complete = false;
|
||||
std::vector<u8> final_data;
|
||||
};
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
@@ -18,7 +18,7 @@ public:
|
||||
ILogger() : ServiceFramework("ILogger") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x00000000, &ILogger::Initialize, "Initialize"},
|
||||
{0x00000001, nullptr, "SetDestination"},
|
||||
{0x00000001, &ILogger::SetDestination, "SetDestination"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
@@ -178,6 +178,17 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// This service function is intended to be used as a way to
|
||||
// redirect logging output to different destinations, however,
|
||||
// given we always want to see the logging output, it's sufficient
|
||||
// to do nothing and return success here.
|
||||
void SetDestination(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LM, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
std::ostringstream log_stream;
|
||||
};
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ add_executable(yuzu
|
||||
Info.plist
|
||||
about_dialog.cpp
|
||||
about_dialog.h
|
||||
applets/software_keyboard.cpp
|
||||
applets/software_keyboard.h
|
||||
bootmanager.cpp
|
||||
bootmanager.h
|
||||
compatibility_list.cpp
|
||||
|
||||
152
src/yuzu/applets/software_keyboard.cpp
Normal file
152
src/yuzu/applets/software_keyboard.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFont>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QVBoxLayout>
|
||||
#include "core/hle/lock.h"
|
||||
#include "yuzu/applets/software_keyboard.h"
|
||||
#include "yuzu/main.h"
|
||||
|
||||
QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator(
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters)
|
||||
: parameters(std::move(parameters)) {}
|
||||
|
||||
QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int& pos) const {
|
||||
if (input.size() > parameters.max_length)
|
||||
return Invalid;
|
||||
if (parameters.disable_space && input.contains(' '))
|
||||
return Invalid;
|
||||
if (parameters.disable_address && input.contains('@'))
|
||||
return Invalid;
|
||||
if (parameters.disable_percent && input.contains('%'))
|
||||
return Invalid;
|
||||
if (parameters.disable_slash && (input.contains('/') || input.contains('\\')))
|
||||
return Invalid;
|
||||
if (parameters.disable_number &&
|
||||
std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) {
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
if (parameters.disable_download_code &&
|
||||
std::any_of(input.begin(), input.end(), [](QChar c) { return c == 'O' || c == 'I'; })) {
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
return Acceptable;
|
||||
}
|
||||
|
||||
QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog(
|
||||
QWidget* parent, Core::Frontend::SoftwareKeyboardParameters parameters_)
|
||||
: QDialog(parent), parameters(std::move(parameters_)) {
|
||||
layout = new QVBoxLayout;
|
||||
|
||||
header_label = new QLabel(QString::fromStdU16String(parameters.header_text));
|
||||
header_label->setFont({header_label->font().family(), 11, QFont::Bold});
|
||||
if (header_label->text().isEmpty())
|
||||
header_label->setText(tr("Enter text:"));
|
||||
|
||||
sub_label = new QLabel(QString::fromStdU16String(parameters.sub_text));
|
||||
sub_label->setFont({sub_label->font().family(), sub_label->font().pointSize(),
|
||||
sub_label->font().weight(), true});
|
||||
sub_label->setHidden(parameters.sub_text.empty());
|
||||
|
||||
guide_label = new QLabel(QString::fromStdU16String(parameters.guide_text));
|
||||
guide_label->setHidden(parameters.guide_text.empty());
|
||||
|
||||
length_label = new QLabel(QStringLiteral("0/%1").arg(parameters.max_length));
|
||||
length_label->setAlignment(Qt::AlignRight);
|
||||
length_label->setFont({length_label->font().family(), 8});
|
||||
|
||||
line_edit = new QLineEdit;
|
||||
line_edit->setValidator(new QtSoftwareKeyboardValidator(parameters));
|
||||
line_edit->setMaxLength(static_cast<int>(parameters.max_length));
|
||||
line_edit->setText(QString::fromStdU16String(parameters.initial_text));
|
||||
line_edit->setCursorPosition(
|
||||
parameters.cursor_at_beginning ? 0 : static_cast<int>(parameters.initial_text.size()));
|
||||
line_edit->setEchoMode(parameters.password ? QLineEdit::Password : QLineEdit::Normal);
|
||||
|
||||
connect(line_edit, &QLineEdit::textChanged, this, [this](const QString& text) {
|
||||
length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length));
|
||||
});
|
||||
|
||||
buttons = new QDialogButtonBox;
|
||||
buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
|
||||
buttons->addButton(parameters.submit_text.empty()
|
||||
? tr("OK")
|
||||
: QString::fromStdU16String(parameters.submit_text),
|
||||
QDialogButtonBox::AcceptRole);
|
||||
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::Submit);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::Reject);
|
||||
layout->addWidget(header_label);
|
||||
layout->addWidget(sub_label);
|
||||
layout->addWidget(guide_label);
|
||||
layout->addWidget(length_label);
|
||||
layout->addWidget(line_edit);
|
||||
layout->addWidget(buttons);
|
||||
setLayout(layout);
|
||||
setWindowTitle(tr("Software Keyboard"));
|
||||
}
|
||||
|
||||
QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default;
|
||||
|
||||
void QtSoftwareKeyboardDialog::Submit() {
|
||||
ok = true;
|
||||
text = line_edit->text().toStdU16String();
|
||||
accept();
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboardDialog::Reject() {
|
||||
ok = false;
|
||||
text.clear();
|
||||
accept();
|
||||
}
|
||||
|
||||
std::u16string QtSoftwareKeyboardDialog::GetText() const {
|
||||
return text;
|
||||
}
|
||||
|
||||
bool QtSoftwareKeyboardDialog::GetStatus() const {
|
||||
return ok;
|
||||
}
|
||||
|
||||
QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) {
|
||||
connect(this, &QtSoftwareKeyboard::MainWindowGetText, &main_window,
|
||||
&GMainWindow::SoftwareKeyboardGetText, Qt::QueuedConnection);
|
||||
connect(this, &QtSoftwareKeyboard::MainWindowTextCheckDialog, &main_window,
|
||||
&GMainWindow::SoftwareKeyboardInvokeCheckDialog, Qt::BlockingQueuedConnection);
|
||||
connect(&main_window, &GMainWindow::SoftwareKeyboardFinishedText, this,
|
||||
&QtSoftwareKeyboard::MainWindowFinishedText, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
QtSoftwareKeyboard::~QtSoftwareKeyboard() = default;
|
||||
|
||||
void QtSoftwareKeyboard::RequestText(std::function<void(std::optional<std::u16string>)> out,
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters) const {
|
||||
text_output = out;
|
||||
emit MainWindowGetText(parameters);
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message,
|
||||
std::function<void()> finished_check) const {
|
||||
this->finished_check = finished_check;
|
||||
emit MainWindowTextCheckDialog(error_message);
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) {
|
||||
// Acquire the HLE mutex
|
||||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
|
||||
text_output(text);
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() {
|
||||
// Acquire the HLE mutex
|
||||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
|
||||
finished_check();
|
||||
}
|
||||
80
src/yuzu/applets/software_keyboard.h
Normal file
80
src/yuzu/applets/software_keyboard.h
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QValidator>
|
||||
#include "common/assert.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
|
||||
class GMainWindow;
|
||||
class QDialogButtonBox;
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QVBoxLayout;
|
||||
class QtSoftwareKeyboard;
|
||||
|
||||
class QtSoftwareKeyboardValidator final : public QValidator {
|
||||
public:
|
||||
explicit QtSoftwareKeyboardValidator(Core::Frontend::SoftwareKeyboardParameters parameters);
|
||||
State validate(QString& input, int& pos) const override;
|
||||
|
||||
private:
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters;
|
||||
};
|
||||
|
||||
class QtSoftwareKeyboardDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QtSoftwareKeyboardDialog(QWidget* parent,
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters);
|
||||
~QtSoftwareKeyboardDialog() override;
|
||||
|
||||
void Submit();
|
||||
void Reject();
|
||||
|
||||
std::u16string GetText() const;
|
||||
bool GetStatus() const;
|
||||
|
||||
private:
|
||||
bool ok = false;
|
||||
std::u16string text;
|
||||
|
||||
QDialogButtonBox* buttons;
|
||||
QLabel* header_label;
|
||||
QLabel* sub_label;
|
||||
QLabel* guide_label;
|
||||
QLabel* length_label;
|
||||
QLineEdit* line_edit;
|
||||
QVBoxLayout* layout;
|
||||
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters;
|
||||
};
|
||||
|
||||
class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtSoftwareKeyboard(GMainWindow& parent);
|
||||
~QtSoftwareKeyboard() override;
|
||||
|
||||
void RequestText(std::function<void(std::optional<std::u16string>)> out,
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters) const override;
|
||||
void SendTextCheckDialog(std::u16string error_message,
|
||||
std::function<void()> finished_check) const override;
|
||||
|
||||
signals:
|
||||
void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const;
|
||||
void MainWindowTextCheckDialog(std::u16string error_message) const;
|
||||
|
||||
public slots:
|
||||
void MainWindowFinishedText(std::optional<std::u16string> text);
|
||||
void MainWindowFinishedCheckDialog();
|
||||
|
||||
private:
|
||||
mutable std::function<void(std::optional<std::u16string>)> text_output;
|
||||
mutable std::function<void()> finished_check;
|
||||
};
|
||||
@@ -8,9 +8,11 @@
|
||||
#include <thread>
|
||||
|
||||
// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
|
||||
#include "applets/software_keyboard.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
|
||||
// defines.
|
||||
@@ -59,6 +61,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/filesystem/fsp_ldr.h"
|
||||
@@ -204,6 +207,27 @@ GMainWindow::~GMainWindow() {
|
||||
delete render_window;
|
||||
}
|
||||
|
||||
void GMainWindow::SoftwareKeyboardGetText(
|
||||
const Core::Frontend::SoftwareKeyboardParameters& parameters) {
|
||||
QtSoftwareKeyboardDialog dialog(this, parameters);
|
||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
dialog.exec();
|
||||
|
||||
if (!dialog.GetStatus()) {
|
||||
emit SoftwareKeyboardFinishedText(std::nullopt);
|
||||
return;
|
||||
}
|
||||
|
||||
emit SoftwareKeyboardFinishedText(dialog.GetText());
|
||||
}
|
||||
|
||||
void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message) {
|
||||
QMessageBox::warning(this, tr("Text Check Failed"), QString::fromStdU16String(error_message));
|
||||
emit SoftwareKeyboardFinishedCheckDialog();
|
||||
}
|
||||
|
||||
void GMainWindow::InitializeWidgets() {
|
||||
#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING
|
||||
ui.action_Report_Compatibility->setVisible(true);
|
||||
@@ -559,6 +583,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
|
||||
system.SetGPUDebugContext(debug_context);
|
||||
|
||||
system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this));
|
||||
|
||||
const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
|
||||
|
||||
const auto drd_callout =
|
||||
@@ -1228,8 +1254,13 @@ void GMainWindow::OnMenuRecentFile() {
|
||||
|
||||
void GMainWindow::OnStartGame() {
|
||||
emu_thread->SetRunning(true);
|
||||
|
||||
qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>(
|
||||
"Core::Frontend::SoftwareKeyboardParameters");
|
||||
qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");
|
||||
qRegisterMetaType<std::string>("std::string");
|
||||
qRegisterMetaType<std::optional<std::u16string>>("std::optional<std::u16string>");
|
||||
|
||||
connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError);
|
||||
|
||||
ui.action_Start->setEnabled(false);
|
||||
|
||||
@@ -29,6 +29,10 @@ class ProfilerWidget;
|
||||
class WaitTreeWidget;
|
||||
enum class GameListOpenTarget;
|
||||
|
||||
namespace Core::Frontend {
|
||||
struct SoftwareKeyboardParameters;
|
||||
} // namespace Core::Frontend
|
||||
|
||||
namespace FileSys {
|
||||
class RegisteredCacheUnion;
|
||||
class VfsFilesystem;
|
||||
@@ -95,6 +99,13 @@ signals:
|
||||
// Signal that tells widgets to update icons to use the current theme
|
||||
void UpdateThemedIcons();
|
||||
|
||||
void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
|
||||
void SoftwareKeyboardFinishedCheckDialog();
|
||||
|
||||
public slots:
|
||||
void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
|
||||
void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message);
|
||||
|
||||
private:
|
||||
void InitializeWidgets();
|
||||
void InitializeDebugWidgets();
|
||||
|
||||
Reference in New Issue
Block a user