Compare commits

..

36 Commits

Author SHA1 Message Date
Lioncash
820bcee6a4 file_sys/card_image: Provide named members for the GamecardInfo struct
Fills out the struct according to information provided by SwitchBrew
2018-11-20 18:40:53 -05:00
bunnei
b6d2c64f4d Merge pull request #1667 from DarkLordZach/swkbd
am: Implement HLE software keyboard applet
2018-11-20 08:24:11 -08:00
bunnei
e9265ac598 Merge pull request #1739 from lioncash/lm
lm: Implement SetDestination by doing nothing
2018-11-19 18:40:17 -08:00
bunnei
2caac4a395 Merge pull request #1738 from lioncash/res-limit
kernel/resource_limit: Clean up interface
2018-11-19 18:40:02 -08:00
Lioncash
5d46038c5c kernel/resource_limit: Clean up interface
Cleans out the citra/3DS-specific implementation details that don't
apply to the Switch. Sets the stage for implementing ResourceLimit
instances properly.

While we're at it, remove the erroneous checks within CreateThread() and
SetThreadPriority(). While these are indeed checked in some capacity,
they are not checked via a ResourceLimit instance.

In the process of moving out Citra-specifics, this also replaces the
system ResourceLimit instance's values with ones from the Switch.
2018-11-19 18:16:39 -05:00
Lioncash
34e4aaddd9 lm: Implement SetDestination by doing nothing
This service function was likely intended to be a way to redirect where
the output of a log went. e.g. Firing a log over a network, dumping over
a tunneling session, etc.

Given we always want to see the log and not change its output. It's one
of the lucky service functions where the easiest implementation is to
just do nothing at all and return success.
2018-11-19 18:09:40 -05:00
Zach Hilman
a9fa890f14 software_keyboard: Fix erroneous extra PushNormalData 2018-11-19 16:30:17 -05:00
Zach Hilman
d68795c665 software_keyboard: Return correct result code on user cancel operation 2018-11-19 15:10:01 -05:00
Zach Hilman
32775125b7 applet: Add AppletDataBroker to manage HLE to AM service interaction
This cleans up most of the callbacks and such in the Applets::Applet interface, while also properly implementing all four data channels.
2018-11-19 14:24:36 -05:00
bunnei
048da7240d Merge pull request #1634 from DarkLordZach/better-hid-2
hid: Add support for multiplayer and multilayout controllers
2018-11-19 08:30:10 -08:00
Zach Hilman
96535c13a5 software_keyboard: Use correct offset for inital text string 2018-11-19 11:22:04 -05:00
Zach Hilman
ea680bea60 software_keyboard: Check for UTF-8 config flag 2018-11-18 23:14:48 -05:00
Zach Hilman
56cf5b7b17 software_keyboard: Add max and current length display to dialog 2018-11-18 10:53:47 -05:00
Zach Hilman
02e6602baa software_keyboard: Push all data over all channels on dialog completion 2018-11-18 10:53:47 -05:00
Zach Hilman
4ee087fb3c applet: Use std::queue instead of std::vector for storage stack 2018-11-18 10:53:47 -05:00
Zach Hilman
19b2571aec applet: Add operation completed callback 2018-11-18 10:53:47 -05:00
Zach Hilman
6209fe0c27 software_keyboard: Push buffer size to offset 0x4 in output data 2018-11-18 10:53:47 -05:00
Zach Hilman
8b433beff3 software_keyboard: Make GetText asynchronous
a
2018-11-18 10:53:47 -05:00
Zach Hilman
7cfb29de23 am: Allow applets to push multiple and different channels of data 2018-11-18 10:53:47 -05:00
Zach Hilman
3cf7246e37 am: Implement ILibraryAppletAccessor IsCompleted and GetResult 2018-11-18 10:53:47 -05:00
Zach Hilman
fed6ab14c3 am: Implement text check software keyboard mode
Allows the game to verify and send a message to the frontend.
2018-11-18 10:53:47 -05:00
Zach Hilman
e696ed1f4d am: Deglobalize software keyboard applet 2018-11-18 10:53:47 -05:00
Zach Hilman
a81645400f qt/main: Register Qt Software Keyboard frontend with AM
Allows using Qt provider over default.
2018-11-18 10:53:47 -05:00
Zach Hilman
48fcb43585 am: Construct and use proper applets with ILibraryAppletAccessor
Allows use of software keyboard applet and future applets to be easily added by adding enum ID and a switch case.
2018-11-18 10:53:47 -05:00
Zach Hilman
5454494adb qt/applets: Provide Qt frontend implementation of software keyboard
Implements all of the features of the keyboard, including length, default text, character validation, and UTF-16 character support.
2018-11-18 10:53:47 -05:00
Zach Hilman
de16c1e453 am/applets: Add connector between frontend and AM applet classes
Provides a middleman between the Frontend provider class and the expected AM::Applets::Applet class needed by ILibraryAppletAccessor
2018-11-18 10:53:47 -05:00
Zach Hilman
ae53b84efd frontend/applets: Add frontend software keyboard provider and default
Default implementation will return "yuzu" for any string. GUI clients (or CLI) can implement the Frontend::SoftwareKeyboardApplet class and register an instance to provide functionality.
2018-11-18 10:53:47 -05:00
Zach Hilman
5b95de0c9c am/applets: Add Applet superclass to describe a generic applet
Adds an Initialize and Execute methods which are used by the ILibraryAppletAccessor to start and control the applet.
2018-11-18 10:53:47 -05:00
Zach Hilman
731b4bd691 am: Unstub ILibraryAppletAccessor::Start
Now starts the applet provided in constructor.
2018-11-18 10:53:47 -05:00
Zach Hilman
ba03bfa430 am: Implement PopInteractiveOutData and PushInteractiveInData
Used by software keyboard applet for data transfer.
2018-11-18 10:53:47 -05:00
Zach Hilman
5ce6b8fea7 am: Convert storage stack to vector
std::stack was no longer suitable for non-trivial operations
2018-11-18 10:53:47 -05:00
Zach Hilman
0682a908c0 am: Move AM::IStorage to header
Needs to be accessible by applet files.
2018-11-18 10:53:47 -05:00
Zach Hilman
c7b6c9de9c am: Move IStorageAccessor to header and update backing buffer
Writes to an AM::IStorage object through an IStorageAccessor will now be preserved once the accessor is destroyed.
2018-11-18 10:53:47 -05:00
Zach Hilman
76d515327b am: Implement CreateTransferMemoryStorage
Creates an AM::IStorage object with the contents of the transfer memory located at the handle provided.
2018-11-18 10:53:47 -05:00
Zach Hilman
c70529c1ec string_util: Implement buffer to UTF-16 string helper function
Needed as most all software keyboard functions use fixed-length UTF16 string buffers.
2018-11-18 10:53:47 -05:00
Zach Hilman
7901de2b75 svc: Implement svcCreateTransferMemory
Seems to be used and created identically to SharedMemory, so just reuse that.
2018-11-18 10:53:47 -05:00
26 changed files with 1249 additions and 309 deletions

View File

@@ -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;

View File

@@ -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,

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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.");

View 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

View 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

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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[] = {

View File

@@ -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> {

View 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

View 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

View 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

View 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

View File

@@ -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;
};

View File

@@ -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

View 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();
}

View 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;
};

View File

@@ -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);

View File

@@ -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();