mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-26 12:56:54 +01:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ca7c8f0074 | |||
| d7063d0b81 | |||
| 091e6b31dc | |||
| 4cd6132758 | |||
| 9c263a9886 | |||
| c11fc96019 | |||
| 4f2b5eb749 | |||
| d7013dc2b3 | |||
| 0bbe301120 | |||
| 37aa6f88b0 | |||
| e3890febaa | |||
| a4da2a9f00 | |||
| 3005a210c6 | |||
| 027433fcdc |
@@ -1,6 +1,6 @@
|
||||
name: Game Emulation Bug Report
|
||||
description: Report KytyPS5 game boot, crash, freeze, rendering, audio, input, or gameplay behavior problems.
|
||||
title: "[GAME BUG]: "
|
||||
name: Game Emulation Status Report
|
||||
description: Submit a KytyPS5 game compatibility result, including boot, rendering, audio, input, or gameplay behavior.
|
||||
title: "[GAME STATUS]: "
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -10,6 +10,7 @@ body:
|
||||
Before submitting, please:
|
||||
- Search existing issues first.
|
||||
- Re-test with a clean game dump.
|
||||
- Use the latest KytyPS5 build available to you.
|
||||
- Attach the complete log file instead of edited snippets.
|
||||
|
||||
- type: markdown
|
||||
@@ -44,6 +45,28 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: compatibility-status
|
||||
attributes:
|
||||
label: Compatibility status
|
||||
description: Select the furthest point the game consistently reaches on this build.
|
||||
options:
|
||||
- "Doesn't boot"
|
||||
- Logo
|
||||
- Main menu
|
||||
- In game
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**Status guide**
|
||||
- **Doesn't boot:** The game does not show its first logo or startup screen.
|
||||
- **Logo:** The game shows a logo or startup screen but does not reach the main menu.
|
||||
- **Main menu:** The game reaches its menus but does not enter gameplay.
|
||||
- **In game:** The game reaches controllable gameplay, even if bugs prevent completion.
|
||||
|
||||
- type: input
|
||||
id: regression-builds
|
||||
attributes:
|
||||
@@ -54,22 +77,22 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Problem details
|
||||
## Test result
|
||||
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: Describe the failure or incorrect behavior as directly as possible.
|
||||
placeholder: "Example: The game reaches the main menu, then freezes after selecting Continue."
|
||||
label: Result details
|
||||
description: Describe what worked, what failed, and any visible or audible problems.
|
||||
placeholder: "Example: The game reaches the main menu, then freezes after selecting Continue. Audio plays correctly until the freeze."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: steps-to-reproduce
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: List the shortest path that triggers the problem from a fresh KytyPS5 start.
|
||||
label: Steps to reproduce the result
|
||||
description: List the shortest path to the reported status or failure from a fresh KytyPS5 start.
|
||||
placeholder: |
|
||||
1. Open KytyPS5.
|
||||
2. Boot the game.
|
||||
@@ -81,7 +104,7 @@ body:
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: Explain what should happen at the same point in the game.
|
||||
description: Explain what should happen at the same point in the game. If there is no known problem, write "No known issue".
|
||||
placeholder: "Example: The save should load and gameplay should continue normally."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -31,6 +31,9 @@ macOS support is experimental. The emulator is built for x86-64 and runs on Appl
|
||||
Rosetta 2, with Vulkan provided by MoltenVK. A small number of titles have been verified in-game
|
||||
on Apple Silicon hardware; see [Building on macOS](#building-on-macos).
|
||||
|
||||
Community game test results are available in the
|
||||
[KytyPS5 Compatibility List](https://kytyps5.github.io/).
|
||||
|
||||
## Bugs and Issues
|
||||
|
||||
The project is in an early stage, so please be mindful when opening new issues. Expect crashes,
|
||||
|
||||
@@ -183,6 +183,10 @@ static void HwCtxIgnoreAaMaskRegister([[maybe_unused]] uint32_t cmd_offset,
|
||||
|
||||
static void HwCtxIgnoreAlphaToMaskRegister([[maybe_unused]] uint32_t value) {}
|
||||
|
||||
static void HwCtxIgnoreDisabledUserClipPlane(CommandProcessor& cp, uint32_t value) {
|
||||
EXIT_NOT_IMPLEMENTED(value != 0 || cp.GetCtx().GetClipControl().user_clip_planes != 0);
|
||||
}
|
||||
|
||||
static void HwCtxIgnoreDrawPayloadControl([[maybe_unused]] uint32_t value) {}
|
||||
|
||||
static void HwCtxIgnoreObjprimIdControl([[maybe_unused]] uint32_t value) {}
|
||||
@@ -3343,6 +3347,12 @@ void GraphicsInitJmpTablesCxIndirect() {
|
||||
};
|
||||
}
|
||||
|
||||
for (auto cmd_offset = Pm4::PA_CL_UCP_0_X; cmd_offset <= Pm4::PA_CL_UCP_5_W; cmd_offset++) {
|
||||
g_hw_ctx_indirect_func[cmd_offset] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
HwCtxIgnoreDisabledUserClipPlane(cp, value);
|
||||
};
|
||||
}
|
||||
|
||||
for (auto cmd_offset = Pm4::PA_SC_VPORT_SCISSOR_0_TL;
|
||||
cmd_offset <= Pm4::PA_SC_VPORT_SCISSOR_15_TL; cmd_offset += 2) {
|
||||
g_hw_ctx_indirect_func[cmd_offset] = [](KYTY_HW_CTX_INDIRECT_ARGS) {
|
||||
@@ -4441,6 +4451,11 @@ void GraphicsInitJmpTablesUcIndirect() {
|
||||
r.vertex_group_size = KYTY_PM4_GET(value, GE_CNTL, VERT_GRP_SIZE);
|
||||
cp.GetUcfg().SetGeControl(r);
|
||||
};
|
||||
g_hw_uc_indirect_func[Pm4::GE_PC_ALLOC] = [](KYTY_HW_UC_INDIRECT_ARGS) {
|
||||
(void)cp;
|
||||
(void)cmd_offset;
|
||||
(void)value;
|
||||
};
|
||||
|
||||
g_hw_uc_indirect_func[Pm4::GE_USER_VGPR_EN] = [](KYTY_HW_UC_INDIRECT_ARGS) {
|
||||
HW::GeUserVgprEn r;
|
||||
|
||||
@@ -473,7 +473,6 @@ void CommandProcessor::DmaData(uint8_t engine, uint8_t dst_sel, uint8_t dst_cach
|
||||
if (num_bytes == 0) {
|
||||
return;
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED((num_bytes & 3u) != 0);
|
||||
EXIT_NOT_IMPLEMENTED(dst_cache_policy > 3);
|
||||
EXIT_NOT_IMPLEMENTED(src_cache_policy > 3);
|
||||
EXIT_NOT_IMPLEMENTED(wait_for_previous > 1);
|
||||
|
||||
@@ -372,6 +372,7 @@ constexpr uint32_t PA_CL_UCP_0_X = 0x16F;
|
||||
constexpr uint32_t PA_CL_UCP_0_Y = 0x170;
|
||||
constexpr uint32_t PA_CL_UCP_0_Z = 0x171;
|
||||
constexpr uint32_t PA_CL_UCP_0_W = 0x172;
|
||||
constexpr uint32_t PA_CL_UCP_5_W = 0x186;
|
||||
constexpr uint32_t SPI_PS_INPUT_CNTL_0 = 0x191;
|
||||
constexpr uint32_t SPI_PS_INPUT_CNTL_31 = 0x1B0;
|
||||
constexpr uint32_t SPI_VS_OUT_CONFIG = 0x1B1;
|
||||
@@ -1047,6 +1048,7 @@ constexpr uint32_t GE_USER_VGPR1 = 0x25C;
|
||||
constexpr uint32_t GE_USER_VGPR2 = 0x25D;
|
||||
constexpr uint32_t GE_USER_VGPR3 = 0x25E;
|
||||
constexpr uint32_t GE_STEREO_CNTL = 0x25F;
|
||||
constexpr uint32_t GE_PC_ALLOC = 0x260;
|
||||
|
||||
constexpr uint32_t GE_USER_VGPR_EN = 0x262;
|
||||
constexpr uint32_t GE_USER_VGPR_EN_EN_USER_VGPR1_SHIFT = 0;
|
||||
|
||||
+2
-2
@@ -722,8 +722,8 @@ void BufferCache::CopyBuffer(uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t si
|
||||
const bool dst_memory = !dst_gds;
|
||||
const bool src_memory = !src_gds;
|
||||
if ((dst_memory && dst_vaddr == 0) || (src_memory && src_vaddr == 0) || size == 0 ||
|
||||
((dst_vaddr | src_vaddr | size) & 3u) != 0 || size > UINT64_MAX - dst_vaddr ||
|
||||
size > UINT64_MAX - src_vaddr || (dst_gds && src_gds) ||
|
||||
((dst_gds || src_gds) && ((dst_vaddr | src_vaddr | size) & 3u) != 0) ||
|
||||
size > UINT64_MAX - dst_vaddr || size > UINT64_MAX - src_vaddr || (dst_gds && src_gds) ||
|
||||
(dst_gds == src_gds && src_vaddr < dst_vaddr + size && dst_vaddr < src_vaddr + size) ||
|
||||
(dst_gds && (dst_vaddr > m_gds_buffer.Size() || size > m_gds_buffer.Size() - dst_vaddr)) ||
|
||||
(src_gds && (src_vaddr > m_gds_buffer.Size() || size > m_gds_buffer.Size() - src_vaddr))) {
|
||||
|
||||
@@ -205,8 +205,6 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
|
||||
if (z.depth_view.slice_max >= 32) {
|
||||
DepthFatal("HTile clear tracking supports at most 32 slices");
|
||||
}
|
||||
} else if (z.htile_data_base_addr != 0) {
|
||||
DepthFatal("HTile address without an enabled tile surface");
|
||||
}
|
||||
const bool size_xy_valid = z.size.valid;
|
||||
const bool wh_valid = z.width_height_valid && z.width != 0 && z.height != 0;
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
#ifndef EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEDIALOGOVERLAY_H_
|
||||
#define EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEDIALOGOVERLAY_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
union SDL_Event;
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
struct GraphicContext;
|
||||
|
||||
void InitializeImeDialogInput();
|
||||
void ShutdownImeDialogInput();
|
||||
bool ProcessImeDialogInput(const SDL_Event& event);
|
||||
|
||||
class ImeDialogOverlay final {
|
||||
public:
|
||||
explicit ImeDialogOverlay(GraphicContext& graphics);
|
||||
~ImeDialogOverlay();
|
||||
KYTY_CLASS_NO_COPY(ImeDialogOverlay);
|
||||
|
||||
[[nodiscard]] bool PrepareFrame(vk::Extent2D extent, vk::Format format, uint32_t image_count);
|
||||
void Record(vk::CommandBuffer command, vk::ImageView target);
|
||||
void ReleaseVulkan();
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
#endif // EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEDIALOGOVERLAY_H_
|
||||
+215
-31
@@ -1,4 +1,4 @@
|
||||
#include "graphics/presentation/imeDialogOverlay.h"
|
||||
#include "graphics/presentation/imeOverlay.h"
|
||||
|
||||
#include "SDL.h"
|
||||
#include "common/assert.h"
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_vulkan.h"
|
||||
#include "libs/controller.h"
|
||||
#include "libs/ime.h"
|
||||
#include "libs/imeDialog.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -26,7 +27,181 @@ namespace Libs::Graphics {
|
||||
|
||||
namespace {
|
||||
|
||||
namespace Ime = Libs::Dialog::ImeDialog;
|
||||
namespace CoreIme = Libs::Ime;
|
||||
namespace DialogIme = Libs::Dialog::ImeDialog;
|
||||
|
||||
namespace Ime {
|
||||
|
||||
enum class Type : uint32_t { Default = 0, BasicLatin = 1, Url = 2, Mail = 3, Number = 4 };
|
||||
enum class EnterLabel : uint32_t { Default = 0, Send = 1, Search = 2, Go = 3 };
|
||||
enum class Alignment : uint32_t { Start = 0, Center = 1, End = 2 };
|
||||
enum class ExternalAction : uint8_t {
|
||||
None,
|
||||
Text,
|
||||
Backspace,
|
||||
MoveLeft,
|
||||
MoveRight,
|
||||
Cancel,
|
||||
Accept,
|
||||
Newline,
|
||||
};
|
||||
|
||||
struct Keycode {
|
||||
uint16_t keycode = 0;
|
||||
char16_t character = u'\0';
|
||||
uint32_t status = 0;
|
||||
uint32_t type = 0;
|
||||
int32_t user_id = 0;
|
||||
uint32_t resource_id = 0;
|
||||
uint64_t timestamp = 0;
|
||||
};
|
||||
|
||||
struct ExternalInput {
|
||||
Keycode key;
|
||||
ExternalAction action = ExternalAction::None;
|
||||
std::u16string text;
|
||||
};
|
||||
|
||||
struct HostSnapshot {
|
||||
uint64_t generation = 0;
|
||||
Type type = Type::Default;
|
||||
EnterLabel enter_label = EnterLabel::Default;
|
||||
uint32_t option = 0;
|
||||
uint32_t max_text_length = 0;
|
||||
uint32_t cursor = 0;
|
||||
uint32_t disable_device = 0;
|
||||
bool key_panel_visible = true;
|
||||
float posx = 0.0f;
|
||||
float posy = 0.0f;
|
||||
Alignment horizontal_alignment = Alignment::Start;
|
||||
Alignment vertical_alignment = Alignment::Start;
|
||||
uint32_t panel_width = 0;
|
||||
uint32_t panel_height = 0;
|
||||
std::u16string text;
|
||||
std::u16string title;
|
||||
std::u16string placeholder;
|
||||
};
|
||||
|
||||
constexpr uint32_t OPTION_MULTILINE = 0x00000001;
|
||||
constexpr uint32_t OPTION_NO_AUTO_CAPITALIZE = 0x00000002;
|
||||
constexpr uint32_t OPTION_PASSWORD = 0x00000004;
|
||||
constexpr uint32_t OPTION_FIXED_POSITION = 0x00000040;
|
||||
constexpr uint32_t OPTION_DISABLE_POSITION_ADJ = 0x00000800;
|
||||
constexpr uint32_t OPTION_USE_OVER_2K = 0x00004000;
|
||||
constexpr uint32_t DISABLE_DEVICE_CONTROLLER = 0x00000001;
|
||||
constexpr uint32_t DISABLE_DEVICE_EXT_KEYBOARD = 0x00000002;
|
||||
constexpr uint64_t CORE_GENERATION_BIT = uint64_t {1} << 63;
|
||||
|
||||
uint64_t PackCoreGeneration(uint64_t generation) {
|
||||
return generation | CORE_GENERATION_BIT;
|
||||
}
|
||||
|
||||
bool IsCoreGeneration(uint64_t generation) {
|
||||
return (generation & CORE_GENERATION_BIT) != 0;
|
||||
}
|
||||
|
||||
uint64_t UnpackGeneration(uint64_t generation) {
|
||||
return generation & ~CORE_GENERATION_BIT;
|
||||
}
|
||||
|
||||
bool GetHostSnapshot(HostSnapshot* snapshot) {
|
||||
if (snapshot == nullptr) {
|
||||
return false;
|
||||
}
|
||||
CoreIme::HostSnapshot core;
|
||||
if (CoreIme::GetHostSnapshot(&core)) {
|
||||
snapshot->generation = PackCoreGeneration(core.generation);
|
||||
snapshot->type = static_cast<Type>(core.type);
|
||||
snapshot->enter_label = static_cast<EnterLabel>(core.enter_label);
|
||||
snapshot->option = core.option;
|
||||
snapshot->max_text_length = core.max_text_length;
|
||||
snapshot->cursor = core.cursor;
|
||||
snapshot->disable_device = core.disable_device;
|
||||
snapshot->key_panel_visible = core.key_panel_visible;
|
||||
snapshot->posx = core.posx;
|
||||
snapshot->posy = core.posy;
|
||||
snapshot->horizontal_alignment = static_cast<Alignment>(core.horizontal_alignment);
|
||||
snapshot->vertical_alignment = static_cast<Alignment>(core.vertical_alignment);
|
||||
snapshot->panel_width = core.panel_width;
|
||||
snapshot->panel_height = core.panel_height;
|
||||
snapshot->text = std::move(core.text);
|
||||
snapshot->title.clear();
|
||||
snapshot->placeholder.clear();
|
||||
return true;
|
||||
}
|
||||
DialogIme::HostSnapshot dialog;
|
||||
if (!DialogIme::GetHostSnapshot(&dialog)) {
|
||||
return false;
|
||||
}
|
||||
snapshot->generation = dialog.generation;
|
||||
snapshot->type = static_cast<Type>(dialog.type);
|
||||
snapshot->enter_label = static_cast<EnterLabel>(dialog.enter_label);
|
||||
snapshot->option = dialog.option;
|
||||
snapshot->max_text_length = dialog.max_text_length;
|
||||
snapshot->cursor = dialog.cursor;
|
||||
snapshot->disable_device = dialog.disable_device;
|
||||
snapshot->key_panel_visible = dialog.key_panel_visible;
|
||||
snapshot->posx = dialog.posx;
|
||||
snapshot->posy = dialog.posy;
|
||||
snapshot->horizontal_alignment = static_cast<Alignment>(dialog.horizontal_alignment);
|
||||
snapshot->vertical_alignment = static_cast<Alignment>(dialog.vertical_alignment);
|
||||
snapshot->panel_width = dialog.panel_width;
|
||||
snapshot->panel_height = dialog.panel_height;
|
||||
snapshot->text = std::move(dialog.text);
|
||||
snapshot->title = std::move(dialog.title);
|
||||
snapshot->placeholder = std::move(dialog.placeholder);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HostInsertText(uint64_t generation, std::u16string_view text) {
|
||||
return IsCoreGeneration(generation)
|
||||
? CoreIme::HostInsertText(UnpackGeneration(generation), text)
|
||||
: DialogIme::HostInsertText(generation, text);
|
||||
}
|
||||
|
||||
bool HostBackspace(uint64_t generation) {
|
||||
return IsCoreGeneration(generation) ? CoreIme::HostBackspace(UnpackGeneration(generation))
|
||||
: DialogIme::HostBackspace(generation);
|
||||
}
|
||||
|
||||
bool HostAccept(uint64_t generation) {
|
||||
return IsCoreGeneration(generation) ? CoreIme::HostAccept(UnpackGeneration(generation))
|
||||
: DialogIme::HostAccept(generation);
|
||||
}
|
||||
|
||||
bool HostCancel(uint64_t generation) {
|
||||
return IsCoreGeneration(generation) ? CoreIme::HostCancel(UnpackGeneration(generation))
|
||||
: DialogIme::HostCancel(generation);
|
||||
}
|
||||
|
||||
bool HostQueueExternalInput(uint64_t generation, ExternalInput input) {
|
||||
if (!IsCoreGeneration(generation)) {
|
||||
DialogIme::ExternalInput dialog;
|
||||
dialog.key.keycode = input.key.keycode;
|
||||
dialog.key.character = input.key.character;
|
||||
dialog.key.status = input.key.status;
|
||||
dialog.key.type = input.key.type;
|
||||
dialog.key.user_id = input.key.user_id;
|
||||
dialog.key.resource_id = input.key.resource_id;
|
||||
dialog.key.timestamp = input.key.timestamp;
|
||||
dialog.action = static_cast<DialogIme::ExternalAction>(input.action);
|
||||
dialog.text = std::move(input.text);
|
||||
return DialogIme::HostQueueExternalInput(generation, std::move(dialog));
|
||||
}
|
||||
CoreIme::ExternalInput core;
|
||||
core.key.keycode = input.key.keycode;
|
||||
core.key.character = input.key.character;
|
||||
core.key.status = input.key.status;
|
||||
core.key.type = input.key.type;
|
||||
core.key.user_id = input.key.user_id;
|
||||
core.key.resource_id = input.key.resource_id;
|
||||
core.key.timestamp = input.key.timestamp;
|
||||
core.action = static_cast<CoreIme::ExternalAction>(input.action);
|
||||
core.text = std::move(input.text);
|
||||
return CoreIme::HostQueueExternalInput(UnpackGeneration(generation), std::move(core));
|
||||
}
|
||||
|
||||
} // namespace Ime
|
||||
|
||||
constexpr size_t INPUT_QUEUE_CAPACITY = 128;
|
||||
|
||||
@@ -114,28 +289,25 @@ void RetryVisibilityWakeup() {
|
||||
}
|
||||
}
|
||||
|
||||
void OnVisibilityChanged(bool visible, uint64_t generation) {
|
||||
void RefreshVisibility() {
|
||||
std::scoped_lock visibility_lock(g_visibility_mutex);
|
||||
if (!g_input_lifecycle_active) {
|
||||
return;
|
||||
}
|
||||
if (generation < g_generation || (generation == g_generation && visible == g_active)) {
|
||||
Ime::HostSnapshot snapshot;
|
||||
const bool visible = Ime::GetHostSnapshot(&snapshot);
|
||||
const uint64_t generation = visible ? snapshot.generation : g_generation;
|
||||
if (visible == g_active && (!visible || generation == g_generation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_generation = generation;
|
||||
bool capture_controller = false;
|
||||
bool capture_keyboard = false;
|
||||
bool multiline = false;
|
||||
if (visible) {
|
||||
Ime::HostSnapshot snapshot;
|
||||
if (Ime::GetHostSnapshot(&snapshot) && snapshot.generation == generation) {
|
||||
capture_controller = (snapshot.disable_device & Ime::DISABLE_DEVICE_CONTROLLER) == 0;
|
||||
capture_keyboard = (snapshot.disable_device & Ime::DISABLE_DEVICE_EXT_KEYBOARD) == 0;
|
||||
multiline = (snapshot.option & Ime::OPTION_MULTILINE) != 0;
|
||||
} else {
|
||||
visible = false;
|
||||
}
|
||||
capture_controller = (snapshot.disable_device & Ime::DISABLE_DEVICE_CONTROLLER) == 0;
|
||||
capture_keyboard = (snapshot.disable_device & Ime::DISABLE_DEVICE_EXT_KEYBOARD) == 0;
|
||||
multiline = (snapshot.option & Ime::OPTION_MULTILINE) != 0;
|
||||
}
|
||||
const bool was_controller = std::exchange(g_controller_captured, capture_controller);
|
||||
if (capture_controller || was_controller) {
|
||||
@@ -156,6 +328,14 @@ void OnVisibilityChanged(bool visible, uint64_t generation) {
|
||||
}
|
||||
}
|
||||
|
||||
void OnCoreVisibilityChanged(bool, uint64_t) {
|
||||
RefreshVisibility();
|
||||
}
|
||||
|
||||
void OnDialogVisibilityChanged(bool, uint64_t) {
|
||||
RefreshVisibility();
|
||||
}
|
||||
|
||||
uint32_t ExternalKeyStatus(SDL_Keymod modifiers, bool character_valid) {
|
||||
uint32_t status = 0x00000001 | (character_valid ? 0x00000002 : 0);
|
||||
if ((modifiers & KMOD_LCTRL) != 0) status |= 0x00000100;
|
||||
@@ -305,7 +485,7 @@ void CheckVulkanResult(VkResult result) {
|
||||
|
||||
} // namespace
|
||||
|
||||
void InitializeImeDialogInput() {
|
||||
void InitializeImeInput() {
|
||||
const Uint32 type = SDL_RegisterEvents(1);
|
||||
EXIT_IF(type == static_cast<Uint32>(-1));
|
||||
{
|
||||
@@ -313,15 +493,14 @@ void InitializeImeDialogInput() {
|
||||
g_input_lifecycle_active = true;
|
||||
g_visibility_event.store(type, std::memory_order_release);
|
||||
}
|
||||
Ime::SetVisibilityCallback(OnVisibilityChanged);
|
||||
Ime::HostSnapshot snapshot;
|
||||
if (Ime::GetHostSnapshot(&snapshot)) {
|
||||
OnVisibilityChanged(true, snapshot.generation);
|
||||
}
|
||||
CoreIme::SetVisibilityCallback(OnCoreVisibilityChanged);
|
||||
DialogIme::SetVisibilityCallback(OnDialogVisibilityChanged);
|
||||
RefreshVisibility();
|
||||
}
|
||||
|
||||
void ShutdownImeDialogInput() {
|
||||
Ime::SetVisibilityCallback(nullptr);
|
||||
void ShutdownImeInput() {
|
||||
CoreIme::SetVisibilityCallback(nullptr);
|
||||
DialogIme::SetVisibilityCallback(nullptr);
|
||||
{
|
||||
std::scoped_lock lock(g_visibility_mutex);
|
||||
g_input_lifecycle_active = false;
|
||||
@@ -346,7 +525,13 @@ void ShutdownImeDialogInput() {
|
||||
}
|
||||
}
|
||||
|
||||
bool ProcessImeDialogInput(const SDL_Event& event) {
|
||||
ImeVisualState GetImeVisualState() noexcept {
|
||||
const auto core = CoreIme::GetVisualState();
|
||||
const auto dialog = DialogIme::GetVisualState();
|
||||
return {core.active || dialog.active, core.revision + dialog.revision};
|
||||
}
|
||||
|
||||
bool ProcessImeInput(const SDL_Event& event) {
|
||||
RetryVisibilityWakeup();
|
||||
if (event.type == g_visibility_event.load(std::memory_order_acquire)) {
|
||||
VisibilityUpdate update {};
|
||||
@@ -465,7 +650,7 @@ bool ProcessImeDialogInput(const SDL_Event& event) {
|
||||
}
|
||||
}
|
||||
|
||||
struct ImeDialogOverlay::Impl {
|
||||
struct ImeOverlay::Impl {
|
||||
explicit Impl(GraphicContext& context): graphics(context) {}
|
||||
|
||||
~Impl() {
|
||||
@@ -488,7 +673,7 @@ struct ImeDialogOverlay::Impl {
|
||||
io.LogFilename = nullptr;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||
io.BackendPlatformName = "Kyty ImeDialog input";
|
||||
io.BackendPlatformName = "Kyty IME input";
|
||||
ImGui::StyleColorsDark();
|
||||
auto& style = ImGui::GetStyle();
|
||||
style.WindowRounding = 10.0f;
|
||||
@@ -680,7 +865,7 @@ struct ImeDialogOverlay::Impl {
|
||||
ImGui::SetNextWindowSize({width, height}, ImGuiCond_Always);
|
||||
constexpr ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoSavedSettings;
|
||||
ImGui::Begin("##ImeDialog", nullptr, flags);
|
||||
ImGui::Begin("##Ime", nullptr, flags);
|
||||
|
||||
const std::u16string title = snapshot.title.empty() ? u"Enter text" : snapshot.title;
|
||||
if (!snapshot.key_panel_visible) {
|
||||
@@ -856,20 +1041,19 @@ struct ImeDialogOverlay::Impl {
|
||||
std::chrono::steady_clock::time_point last_frame;
|
||||
};
|
||||
|
||||
ImeDialogOverlay::ImeDialogOverlay(GraphicContext& graphics)
|
||||
: m_impl(std::make_unique<Impl>(graphics)) {}
|
||||
ImeOverlay::ImeOverlay(GraphicContext& graphics): m_impl(std::make_unique<Impl>(graphics)) {}
|
||||
|
||||
ImeDialogOverlay::~ImeDialogOverlay() = default;
|
||||
ImeOverlay::~ImeOverlay() = default;
|
||||
|
||||
bool ImeDialogOverlay::PrepareFrame(vk::Extent2D extent, vk::Format format, uint32_t image_count) {
|
||||
bool ImeOverlay::PrepareFrame(vk::Extent2D extent, vk::Format format, uint32_t image_count) {
|
||||
return m_impl->PrepareFrame(extent, format, image_count);
|
||||
}
|
||||
|
||||
void ImeDialogOverlay::Record(vk::CommandBuffer command, vk::ImageView target) {
|
||||
void ImeOverlay::Record(vk::CommandBuffer command, vk::ImageView target) {
|
||||
m_impl->Record(command, target);
|
||||
}
|
||||
|
||||
void ImeDialogOverlay::ReleaseVulkan() {
|
||||
void ImeOverlay::ReleaseVulkan() {
|
||||
m_impl->ReleaseVulkan();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEOVERLAY_H_
|
||||
#define EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEOVERLAY_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
union SDL_Event;
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
struct GraphicContext;
|
||||
|
||||
struct ImeVisualState {
|
||||
bool active;
|
||||
uint64_t revision;
|
||||
};
|
||||
|
||||
void InitializeImeInput();
|
||||
void ShutdownImeInput();
|
||||
bool ProcessImeInput(const SDL_Event& event);
|
||||
ImeVisualState GetImeVisualState() noexcept;
|
||||
|
||||
class ImeOverlay final {
|
||||
public:
|
||||
explicit ImeOverlay(GraphicContext& graphics);
|
||||
~ImeOverlay();
|
||||
KYTY_CLASS_NO_COPY(ImeOverlay);
|
||||
|
||||
[[nodiscard]] bool PrepareFrame(vk::Extent2D extent, vk::Format format, uint32_t image_count);
|
||||
void Record(vk::CommandBuffer command, vk::ImageView target);
|
||||
void ReleaseVulkan();
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
#endif // EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEOVERLAY_H_
|
||||
@@ -18,35 +18,57 @@ namespace Libs::Graphics {
|
||||
|
||||
namespace {
|
||||
|
||||
struct ControlInfo {
|
||||
std::string_view name;
|
||||
uint32_t button = 0;
|
||||
Controller::Axis axis = Controller::Axis::AxisMax;
|
||||
bool positive = false;
|
||||
};
|
||||
|
||||
static constexpr std::array CONTROL_INFO = {
|
||||
ControlInfo {"L3", Controller::PAD_BUTTON_L3},
|
||||
ControlInfo {"R3", Controller::PAD_BUTTON_R3},
|
||||
ControlInfo {"Options", Controller::PAD_BUTTON_OPTIONS},
|
||||
ControlInfo {"Up", Controller::PAD_BUTTON_UP},
|
||||
ControlInfo {"Right", Controller::PAD_BUTTON_RIGHT},
|
||||
ControlInfo {"Down", Controller::PAD_BUTTON_DOWN},
|
||||
ControlInfo {"Left", Controller::PAD_BUTTON_LEFT},
|
||||
ControlInfo {"L2", Controller::PAD_BUTTON_L2},
|
||||
ControlInfo {"R2", Controller::PAD_BUTTON_R2},
|
||||
ControlInfo {"L1", Controller::PAD_BUTTON_L1},
|
||||
ControlInfo {"R1", Controller::PAD_BUTTON_R1},
|
||||
ControlInfo {"Triangle", Controller::PAD_BUTTON_TRIANGLE},
|
||||
ControlInfo {"Circle", Controller::PAD_BUTTON_CIRCLE},
|
||||
ControlInfo {"Cross", Controller::PAD_BUTTON_CROSS},
|
||||
ControlInfo {"Square", Controller::PAD_BUTTON_SQUARE},
|
||||
ControlInfo {"TouchPad", Controller::PAD_BUTTON_TOUCH_PAD},
|
||||
ControlInfo {"LeftStickLeft", 0, Controller::Axis::LeftX, false},
|
||||
ControlInfo {"LeftStickRight", 0, Controller::Axis::LeftX, true},
|
||||
ControlInfo {"LeftStickUp", 0, Controller::Axis::LeftY, false},
|
||||
ControlInfo {"LeftStickDown", 0, Controller::Axis::LeftY, true},
|
||||
ControlInfo {"RightStickLeft", 0, Controller::Axis::RightX, false},
|
||||
ControlInfo {"RightStickRight", 0, Controller::Axis::RightX, true},
|
||||
ControlInfo {"RightStickUp", 0, Controller::Axis::RightY, false},
|
||||
ControlInfo {"RightStickDown", 0, Controller::Axis::RightY, true},
|
||||
};
|
||||
|
||||
constexpr std::size_t INVALID_CONTROL = CONTROL_INFO.size();
|
||||
|
||||
struct Binding {
|
||||
SDL_Keycode key = SDLK_UNKNOWN;
|
||||
uint8_t mouse_button = 0;
|
||||
uint32_t pad_button = 0;
|
||||
std::size_t control = INVALID_CONTROL;
|
||||
};
|
||||
|
||||
static constexpr std::array PAD_BUTTONS = {
|
||||
std::pair {std::string_view("L3"), Controller::PAD_BUTTON_L3},
|
||||
std::pair {std::string_view("R3"), Controller::PAD_BUTTON_R3},
|
||||
std::pair {std::string_view("Options"), Controller::PAD_BUTTON_OPTIONS},
|
||||
std::pair {std::string_view("Up"), Controller::PAD_BUTTON_UP},
|
||||
std::pair {std::string_view("Right"), Controller::PAD_BUTTON_RIGHT},
|
||||
std::pair {std::string_view("Down"), Controller::PAD_BUTTON_DOWN},
|
||||
std::pair {std::string_view("Left"), Controller::PAD_BUTTON_LEFT},
|
||||
std::pair {std::string_view("L2"), Controller::PAD_BUTTON_L2},
|
||||
std::pair {std::string_view("R2"), Controller::PAD_BUTTON_R2},
|
||||
std::pair {std::string_view("L1"), Controller::PAD_BUTTON_L1},
|
||||
std::pair {std::string_view("R1"), Controller::PAD_BUTTON_R1},
|
||||
std::pair {std::string_view("Triangle"), Controller::PAD_BUTTON_TRIANGLE},
|
||||
std::pair {std::string_view("Circle"), Controller::PAD_BUTTON_CIRCLE},
|
||||
std::pair {std::string_view("Cross"), Controller::PAD_BUTTON_CROSS},
|
||||
std::pair {std::string_view("Square"), Controller::PAD_BUTTON_SQUARE},
|
||||
std::pair {std::string_view("TouchPad"), Controller::PAD_BUTTON_TOUCH_PAD},
|
||||
};
|
||||
std::size_t ControlFromName(std::string_view name) {
|
||||
const auto info = std::find_if(CONTROL_INFO.begin(), CONTROL_INFO.end(),
|
||||
[name](const auto& item) { return item.name == name; });
|
||||
return static_cast<std::size_t>(std::distance(CONTROL_INFO.begin(), info));
|
||||
}
|
||||
|
||||
uint32_t PadButtonFromName(std::string_view name) {
|
||||
const auto button = std::find_if(PAD_BUTTONS.begin(), PAD_BUTTONS.end(),
|
||||
[name](const auto& item) { return item.first == name; });
|
||||
return button != PAD_BUTTONS.end() ? button->second : 0;
|
||||
const ControlInfo& Info(std::size_t control) {
|
||||
EXIT_IF(control >= CONTROL_INFO.size());
|
||||
return CONTROL_INFO[control];
|
||||
}
|
||||
|
||||
SDL_Keycode NormalizeKey(SDL_Keycode key) {
|
||||
@@ -74,7 +96,7 @@ uint8_t MouseButtonFromName(std::string_view name) {
|
||||
}
|
||||
|
||||
bool Conflicts(const Binding& first, const Binding& second) {
|
||||
return first.pad_button == second.pad_button ||
|
||||
return first.control == second.control ||
|
||||
(first.key != SDLK_UNKNOWN && first.key == second.key) ||
|
||||
(first.mouse_button != 0 && first.mouse_button == second.mouse_button);
|
||||
}
|
||||
@@ -88,7 +110,7 @@ public:
|
||||
|
||||
Binding binding;
|
||||
if (split != std::string_view::npos) {
|
||||
binding.pad_button = PadButtonFromName(entry.substr(0, split));
|
||||
binding.control = ControlFromName(entry.substr(0, split));
|
||||
const auto host_input = entry.substr(split + 1);
|
||||
binding.mouse_button = MouseButtonFromName(host_input);
|
||||
if (binding.mouse_button == 0) {
|
||||
@@ -98,7 +120,7 @@ public:
|
||||
|
||||
const bool reserved =
|
||||
binding.key == SDLK_ESCAPE || binding.key == SDLK_SPACE || binding.key == SDLK_F1;
|
||||
if (binding.pad_button == 0 || reserved ||
|
||||
if (binding.control == INVALID_CONTROL || reserved ||
|
||||
(binding.key == SDLK_UNKNOWN && binding.mouse_button == 0)) {
|
||||
EXIT("Invalid input mapping: %s\n", value.c_str());
|
||||
}
|
||||
@@ -107,19 +129,19 @@ public:
|
||||
}
|
||||
[[nodiscard]] bool Custom() const { return m_custom; }
|
||||
|
||||
[[nodiscard]] uint32_t FindKey(int key_code) const {
|
||||
[[nodiscard]] std::size_t FindKey(int key_code) const {
|
||||
key_code = NormalizeKey(static_cast<SDL_Keycode>(key_code));
|
||||
const auto binding =
|
||||
std::find_if(m_bindings.begin(), m_bindings.begin() + m_size,
|
||||
[key_code](const auto& item) { return item.key == key_code; });
|
||||
return binding != m_bindings.begin() + m_size ? binding->pad_button : 0;
|
||||
return binding != m_bindings.begin() + m_size ? binding->control : INVALID_CONTROL;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint32_t FindMouseButton(uint8_t mouse_button) const {
|
||||
[[nodiscard]] std::size_t FindMouseButton(uint8_t mouse_button) const {
|
||||
const auto binding = std::find_if(
|
||||
m_bindings.begin(), m_bindings.begin() + m_size,
|
||||
[mouse_button](const auto& item) { return item.mouse_button == mouse_button; });
|
||||
return binding != m_bindings.begin() + m_size ? binding->pad_button : 0;
|
||||
return binding != m_bindings.begin() + m_size ? binding->control : INVALID_CONTROL;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -135,9 +157,9 @@ private:
|
||||
m_bindings[m_size++] = binding;
|
||||
}
|
||||
|
||||
std::array<Binding, PAD_BUTTONS.size()> m_bindings {};
|
||||
std::size_t m_size = 0;
|
||||
bool m_custom;
|
||||
std::array<Binding, CONTROL_INFO.size()> m_bindings {};
|
||||
std::size_t m_size = 0;
|
||||
bool m_custom;
|
||||
};
|
||||
|
||||
const InputMap& GetInputMap() {
|
||||
@@ -191,6 +213,33 @@ void SetStickAxis(Controller::Axis axis, bool negative, bool positive) {
|
||||
Controller::ControllerAxis(Controller::HOST_INPUT_CONTROLLER_ID, axis, value);
|
||||
}
|
||||
|
||||
void SetControl(std::size_t control, bool down) {
|
||||
if (control == INVALID_CONTROL) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& info = Info(control);
|
||||
if (info.button != 0) {
|
||||
SetButton(info.button, down);
|
||||
return;
|
||||
}
|
||||
|
||||
struct AxisKeys {
|
||||
bool negative = false;
|
||||
bool positive = false;
|
||||
};
|
||||
static std::array<AxisKeys, 4> axes;
|
||||
|
||||
const auto axis = static_cast<std::size_t>(info.axis);
|
||||
EXIT_IF(axis >= axes.size());
|
||||
if (info.positive) {
|
||||
axes[axis].positive = down;
|
||||
} else {
|
||||
axes[axis].negative = down;
|
||||
}
|
||||
SetStickAxis(info.axis, axes[axis].negative, axes[axis].positive);
|
||||
}
|
||||
|
||||
void DefaultKeyboardInput(int key_code, bool down) {
|
||||
static StickKeys left;
|
||||
static StickKeys right;
|
||||
@@ -240,7 +289,7 @@ void HostInputInit() {
|
||||
void HostInputKey(int key_code, bool down) {
|
||||
const auto& map = GetInputMap();
|
||||
if (map.Custom()) {
|
||||
SetButton(map.FindKey(key_code), down);
|
||||
SetControl(map.FindKey(key_code), down);
|
||||
} else {
|
||||
DefaultKeyboardInput(key_code, down);
|
||||
}
|
||||
@@ -249,7 +298,7 @@ void HostInputKey(int key_code, bool down) {
|
||||
void HostInputMouseButton(uint8_t mouse_button, bool down) {
|
||||
const auto& map = GetInputMap();
|
||||
if (map.Custom() && mouse_button != 0) {
|
||||
SetButton(map.FindMouseButton(mouse_button), down);
|
||||
SetControl(map.FindMouseButton(mouse_button), down);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,13 +31,12 @@
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/presentation/imeDialogOverlay.h"
|
||||
#include "graphics/presentation/imeOverlay.h"
|
||||
#include "graphics/presentation/presenter.h"
|
||||
#include "graphics/presentation/renderDoc.h"
|
||||
#include "graphics/presentation/videoOut.h"
|
||||
#include "graphics/presentation/window/windowInternal.h"
|
||||
#include "libs/controller.h"
|
||||
#include "libs/imeDialog.h"
|
||||
#include "loader/systemContent.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -345,17 +344,17 @@ private:
|
||||
void Destroy();
|
||||
void RefreshSurfaceSize();
|
||||
|
||||
WindowContext& m_window;
|
||||
vk::SwapchainKHR m_handle = nullptr;
|
||||
vk::Format m_format = vk::Format::eUndefined;
|
||||
vk::Extent2D m_extent {};
|
||||
std::vector<vk::Image> m_images;
|
||||
std::vector<vk::ImageView> m_image_views;
|
||||
std::vector<vk::Semaphore> m_image_acquired;
|
||||
std::vector<vk::Semaphore> m_render_complete;
|
||||
std::unique_ptr<ImeDialogOverlay> m_ime_overlay;
|
||||
uint32_t m_image_index = static_cast<uint32_t>(-1);
|
||||
uint32_t m_frame_index = 0;
|
||||
WindowContext& m_window;
|
||||
vk::SwapchainKHR m_handle = nullptr;
|
||||
vk::Format m_format = vk::Format::eUndefined;
|
||||
vk::Extent2D m_extent {};
|
||||
std::vector<vk::Image> m_images;
|
||||
std::vector<vk::ImageView> m_image_views;
|
||||
std::vector<vk::Semaphore> m_image_acquired;
|
||||
std::vector<vk::Semaphore> m_render_complete;
|
||||
std::unique_ptr<ImeOverlay> m_ime_overlay;
|
||||
uint32_t m_image_index = static_cast<uint32_t>(-1);
|
||||
uint32_t m_frame_index = 0;
|
||||
};
|
||||
|
||||
struct Presenter::Impl {
|
||||
@@ -618,7 +617,7 @@ Swapchain::Status Swapchain::AcquireNextImage() {
|
||||
|
||||
bool Swapchain::PrepareImeOverlay() {
|
||||
if (m_ime_overlay == nullptr) {
|
||||
m_ime_overlay = std::make_unique<ImeDialogOverlay>(m_window.graphic_ctx);
|
||||
m_ime_overlay = std::make_unique<ImeOverlay>(m_window.graphic_ctx);
|
||||
}
|
||||
return m_ime_overlay->PrepareFrame(m_extent, m_format, ImageCount());
|
||||
}
|
||||
@@ -807,7 +806,7 @@ bool Presenter::IsGuestPaused() const noexcept {
|
||||
}
|
||||
|
||||
bool Presenter::NeedsImeRefresh() const noexcept {
|
||||
const auto visual = Dialog::ImeDialog::GetVisualState();
|
||||
const auto visual = GetImeVisualState();
|
||||
return visual.active ||
|
||||
visual.revision != m_impl->presented_ime_revision.load(std::memory_order_acquire);
|
||||
}
|
||||
@@ -843,7 +842,7 @@ void Presenter::Present(Frame& frame, bool reuse) {
|
||||
m_impl->RecoverSwapchain(Swapchain::Status::Recreate);
|
||||
}
|
||||
|
||||
const auto ime_visual = Dialog::ImeDialog::GetVisualState();
|
||||
const auto ime_visual = GetImeVisualState();
|
||||
auto& swapchain = m_impl->swapchain;
|
||||
for (uint32_t attempt = 0; attempt < 2; attempt++) {
|
||||
auto status = swapchain.AcquireNextImage();
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/presentation/imeDialogOverlay.h"
|
||||
#include "graphics/presentation/imeOverlay.h"
|
||||
#include "graphics/presentation/presenter.h"
|
||||
#include "graphics/presentation/renderDoc.h"
|
||||
#include "graphics/presentation/videoOut.h"
|
||||
@@ -607,10 +607,6 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
|
||||
create_info.flags = {};
|
||||
create_info.pQueueCreateInfos = &queue_create_info;
|
||||
create_info.queueCreateInfoCount = 1;
|
||||
create_info.enabledLayerCount =
|
||||
(r.enable_validation_layers ? static_cast<uint32_t>(r.required_layers.size()) : 0);
|
||||
create_info.ppEnabledLayerNames =
|
||||
(r.enable_validation_layers ? r.required_layers.data() : nullptr);
|
||||
create_info.enabledExtensionCount = static_cast<uint32_t>(device_extensions.size());
|
||||
create_info.ppEnabledExtensionNames = device_extensions.data();
|
||||
create_info.pEnabledFeatures = &device_features;
|
||||
@@ -1015,7 +1011,7 @@ void WindowContext::RecreateSurface() {
|
||||
}
|
||||
|
||||
WindowContext::~WindowContext() {
|
||||
ShutdownImeDialogInput();
|
||||
ShutdownImeInput();
|
||||
presenter.reset();
|
||||
LibKernel::Memory::InstallGpuResources(nullptr);
|
||||
render_context.reset();
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/presentation/imeDialogOverlay.h"
|
||||
#include "graphics/presentation/imeOverlay.h"
|
||||
#include "graphics/presentation/renderDoc.h"
|
||||
#include "graphics/presentation/window/hostInput.h"
|
||||
#include "graphics/presentation/window/windowInternal.h"
|
||||
@@ -477,7 +477,7 @@ void WindowContext::ProcessEvent(double time_s) {
|
||||
auto& game = loop;
|
||||
auto* event = &game.event;
|
||||
EXIT_IF(SDL_GetEventState(SDL_DISPLAYEVENT) != SDL_ENABLE);
|
||||
if (ProcessImeDialogInput(*event)) {
|
||||
if (ProcessImeInput(*event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -783,7 +783,7 @@ static void WindowCreate(WindowContext& context) {
|
||||
EXIT("%s\n", SDL_GetError());
|
||||
}
|
||||
HostInputInit();
|
||||
InitializeImeDialogInput();
|
||||
InitializeImeInput();
|
||||
|
||||
LOGF("WindowCreate(): width = %d, height = %d\n", width, height);
|
||||
|
||||
@@ -938,7 +938,6 @@ void WindowContext::UpdateTitle() {
|
||||
static constexpr auto build_type = "Unknown";
|
||||
#endif
|
||||
|
||||
|
||||
const auto now = Common::Timer::QueryPerformanceCounter();
|
||||
const auto frequency = Common::Timer::QueryPerformanceFrequency();
|
||||
frame_num++;
|
||||
@@ -950,11 +949,11 @@ void WindowContext::UpdateTitle() {
|
||||
fps_frames = 0;
|
||||
}
|
||||
|
||||
auto fps = fmt::format("[{} | {}] {}{}{}{}{}{}[{}] [{}], frame: {}, fps: {:f}", KYTY_BUILD_LABEL,
|
||||
build_type, (has_title ? title : ""), (has_title ? ", " : ""),
|
||||
(has_title_id ? title_id : ""), (has_title_id ? ", " : ""),
|
||||
(has_app_ver ? app_ver : ""), (has_app_ver ? " " : ""), device_name,
|
||||
processor_name, frame_num, current_fps);
|
||||
auto fps = fmt::format(
|
||||
"[{} | {}] {}{}{}{}{}{}[{}] [{}], frame: {}, fps: {:f}", KYTY_BUILD_LABEL, build_type,
|
||||
(has_title ? title : ""), (has_title ? ", " : ""), (has_title_id ? title_id : ""),
|
||||
(has_title_id ? ", " : ""), (has_app_ver ? app_ver : ""), (has_app_ver ? " " : ""),
|
||||
device_name, processor_name, frame_num, current_fps);
|
||||
|
||||
#if defined(__APPLE__)
|
||||
// AppKit traps on title changes off the main thread; fire-and-forget keeps present pacing.
|
||||
|
||||
@@ -21,11 +21,10 @@
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char FILE_NAME[] = "compatibility_db.json";
|
||||
constexpr char URL[] =
|
||||
"https://github.com/Nmzik/KytyPS5/releases/download/compat-db/compatibility_db.json";
|
||||
constexpr int RETRY_COUNT = 3;
|
||||
constexpr int RETRY_DELAY_MS = 750;
|
||||
constexpr char FILE_NAME[] = "compatibility_db.json";
|
||||
constexpr char URL[] = "https://kytyps5.github.io/data/compatibility.json";
|
||||
constexpr int RETRY_COUNT = 3;
|
||||
constexpr int RETRY_DELAY_MS = 750;
|
||||
|
||||
struct LoadResult {
|
||||
QMap<QString, CompatibilityEntry> entries;
|
||||
|
||||
@@ -18,17 +18,25 @@ namespace {
|
||||
|
||||
constexpr int BINDING_COLUMN = 1;
|
||||
|
||||
struct PadButton {
|
||||
struct PadControl {
|
||||
const char* id;
|
||||
const char* label;
|
||||
const char* default_binding;
|
||||
};
|
||||
|
||||
constexpr PadButton PAD_BUTTONS[] = {
|
||||
constexpr PadControl PAD_CONTROLS[] = {
|
||||
{"Up", "D-pad Up", "Up"},
|
||||
{"Down", "D-pad Down", "Down"},
|
||||
{"Left", "D-pad Left", "Left"},
|
||||
{"Right", "D-pad Right", "Right"},
|
||||
{"LeftStickUp", "Left stick Up", "W"},
|
||||
{"LeftStickDown", "Left stick Down", "S"},
|
||||
{"LeftStickLeft", "Left stick Left", "A"},
|
||||
{"LeftStickRight", "Left stick Right", "D"},
|
||||
{"RightStickUp", "Right stick Up", "T"},
|
||||
{"RightStickDown", "Right stick Down", "G"},
|
||||
{"RightStickLeft", "Right stick Left", "F"},
|
||||
{"RightStickRight", "Right stick Right", "H"},
|
||||
{"Triangle", "Triangle", "I"},
|
||||
{"Circle", "Circle", "L"},
|
||||
{"Cross", "Cross", "J"},
|
||||
@@ -190,16 +198,14 @@ QHash<QString, QString> ParseMapping(const QStringList& mapping) {
|
||||
InputMappingDialog::InputMappingDialog(const QStringList& mapping, QWidget* parent)
|
||||
: QDialog(parent), m_customized(!mapping.isEmpty()) {
|
||||
setWindowTitle(tr("Input Mapping"));
|
||||
resize(440, 520);
|
||||
resize(460, 650);
|
||||
|
||||
auto* layout = new QVBoxLayout(this);
|
||||
layout->addWidget(new QLabel(tr("Map keyboard or mouse input to DualSense buttons.\n"
|
||||
"Defaults: left stick W/A/S/D, right stick T/F/G/H."),
|
||||
this));
|
||||
layout->addWidget(new QLabel(tr("Map keyboard or mouse input to DualSense controls."), this));
|
||||
|
||||
m_bindings = new QTreeWidget(this);
|
||||
m_bindings->setColumnCount(2);
|
||||
m_bindings->setHeaderLabels({tr("DualSense button"), tr("Host input")});
|
||||
m_bindings->setHeaderLabels({tr("DualSense control"), tr("Host input")});
|
||||
m_bindings->setRootIsDecorated(false);
|
||||
m_bindings->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
m_bindings->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
@@ -207,12 +213,12 @@ InputMappingDialog::InputMappingDialog(const QStringList& mapping, QWidget* pare
|
||||
layout->addWidget(m_bindings);
|
||||
|
||||
const auto parsed = ParseMapping(mapping);
|
||||
for (const auto& button: PAD_BUTTONS) {
|
||||
for (const auto& control: PAD_CONTROLS) {
|
||||
auto* item = new QTreeWidgetItem(m_bindings);
|
||||
item->setText(0, tr(button.label));
|
||||
item->setData(0, Qt::UserRole, QString::fromLatin1(button.id));
|
||||
SetBinding(item, m_customized ? parsed.value(QString::fromLatin1(button.id))
|
||||
: QString::fromLatin1(button.default_binding));
|
||||
item->setText(0, tr(control.label));
|
||||
item->setData(0, Qt::UserRole, QString::fromLatin1(control.id));
|
||||
SetBinding(item, m_customized ? parsed.value(QString::fromLatin1(control.id))
|
||||
: QString::fromLatin1(control.default_binding));
|
||||
}
|
||||
m_bindings->setCurrentItem(m_bindings->topLevelItem(0));
|
||||
|
||||
@@ -286,7 +292,7 @@ void InputMappingDialog::ClearBinding() {
|
||||
void InputMappingDialog::RestoreDefaults() {
|
||||
for (int index = 0; index < m_bindings->topLevelItemCount(); index++) {
|
||||
SetBinding(m_bindings->topLevelItem(index),
|
||||
QString::fromLatin1(PAD_BUTTONS[index].default_binding));
|
||||
QString::fromLatin1(PAD_CONTROLS[index].default_binding));
|
||||
}
|
||||
m_customized = false;
|
||||
}
|
||||
|
||||
+91
-1
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
@@ -1180,6 +1181,95 @@ int KYTY_SYSV_ABI GraphicsUpdatePrimState(ShaderRegister* cx_regs, ShaderRegiste
|
||||
return OK;
|
||||
}
|
||||
|
||||
struct GsOccupancyLimits {
|
||||
uint32_t vertex;
|
||||
uint32_t export_count;
|
||||
};
|
||||
|
||||
static GsOccupancyLimits get_gs_occupancy_limits(const Shader* gs, uint32_t vertex_capacity,
|
||||
uint32_t export_capacity) {
|
||||
const auto* onchip =
|
||||
find_shader_register(gs->cx_registers, gs->num_cx_registers, Pm4::VGT_GS_ONCHIP_CNTL);
|
||||
const auto* subgroup =
|
||||
find_shader_register(gs->cx_registers, gs->num_cx_registers, Pm4::GE_NGG_SUBGRP_CNTL);
|
||||
const auto* vs_out =
|
||||
find_shader_register(gs->cx_registers, gs->num_cx_registers, Pm4::SPI_VS_OUT_CONFIG);
|
||||
const auto* cl_out =
|
||||
find_shader_register(gs->cx_registers, gs->num_cx_registers, Pm4::PA_CL_VS_OUT_CNTL);
|
||||
const auto* max_output = find_shader_register(gs->cx_registers, gs->num_cx_registers,
|
||||
Pm4::GE_MAX_OUTPUT_PER_SUBGROUP);
|
||||
|
||||
const uint32_t output_words = ((max_output->value & 0x3ffu) + 31u) >> 5u;
|
||||
const uint32_t subgroup_work =
|
||||
((((onchip->value >> 11u) & 0x7ffu) * (subgroup->value & 0x1ffu)) + 31u) >> 5u;
|
||||
uint32_t waves = std::max(subgroup_work, output_words);
|
||||
if ((gs->specials->vgt_shader_stages_en.value & 0x00400000u) == 0) {
|
||||
waves >>= 1u;
|
||||
}
|
||||
waves = std::max(waves, 1u);
|
||||
|
||||
const uint32_t exports = 1u + ((cl_out->value >> 21u) & 1u) + ((cl_out->value >> 22u) & 1u) +
|
||||
((cl_out->value >> 23u) & 1u);
|
||||
const uint32_t export_limit = (export_capacity / exports) * 4u;
|
||||
const uint32_t vertex_limit = (vs_out->value & 0x80u) != 0
|
||||
? 2048u
|
||||
: vertex_capacity / (((vs_out->value >> 2u) & 0xfu) + 1u);
|
||||
|
||||
return {waves * (vertex_limit / output_words), waves * (export_limit / output_words)};
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI GraphicsGetGsOversubscription(ShaderRegister* regs, const Shader* gs,
|
||||
uint32_t budget, float factor) {
|
||||
PRINT_NAME();
|
||||
|
||||
constexpr uint32_t FULL_PC_OVERSUBSCRIPTION = 0x7ffu;
|
||||
constexpr uint32_t FULL_SH_OVERSUBSCRIPTION = 0x007f0000u;
|
||||
|
||||
regs[0] = {Pm4::GE_PC_ALLOC, 0u};
|
||||
regs[1] = {Pm4::SPI_SHADER_PGM_RSRC4_GS, 0u};
|
||||
|
||||
if (budget == 0u) {
|
||||
return OK;
|
||||
}
|
||||
if (budget == UINT32_MAX) {
|
||||
regs[0].value |= FULL_PC_OVERSUBSCRIPTION;
|
||||
regs[1].value |= FULL_SH_OVERSUBSCRIPTION;
|
||||
return OK;
|
||||
}
|
||||
|
||||
const auto base = get_gs_occupancy_limits(gs, 1024u, 128u);
|
||||
const auto expanded = get_gs_occupancy_limits(gs, 2048u, 382u);
|
||||
const auto base_min = std::min(base.vertex, base.export_count);
|
||||
const auto limit_shift =
|
||||
(gs->specials->vgt_shader_stages_en.value & 0x00400000u) != 0 ? 5u : 6u;
|
||||
const auto expanded_limit =
|
||||
std::min({expanded.vertex, expanded.export_count, budget >> limit_shift, 1024u});
|
||||
const auto headroom = expanded_limit > base_min ? expanded_limit - base_min : 0u;
|
||||
const auto target = static_cast<uint32_t>(
|
||||
std::fma(factor, static_cast<float>(headroom), static_cast<float>(base_min)));
|
||||
|
||||
if (target <= base_min) {
|
||||
return OK;
|
||||
}
|
||||
if (target < base.export_count) {
|
||||
const auto range = std::max(expanded.vertex - base.vertex, 1u);
|
||||
auto value = static_cast<uint32_t>(
|
||||
std::min<uint64_t>((static_cast<uint64_t>(target - base.vertex) << 10u) / range, 1024));
|
||||
value = std::max(value, 1u);
|
||||
regs[0].value = (regs[0].value & ~FULL_PC_OVERSUBSCRIPTION) |
|
||||
(((value << 1u) - 1u) & FULL_PC_OVERSUBSCRIPTION);
|
||||
regs[1].value |= FULL_SH_OVERSUBSCRIPTION;
|
||||
} else {
|
||||
const auto range = std::max(expanded.export_count - base.export_count, 1u);
|
||||
const auto value = static_cast<uint32_t>(
|
||||
std::min<uint64_t>((static_cast<uint64_t>(target - base.export_count) * 127u) / range, 127));
|
||||
regs[0].value |= FULL_PC_OVERSUBSCRIPTION;
|
||||
regs[1].value = (regs[1].value & ~FULL_SH_OVERSUBSCRIPTION) | (value << 16u);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static uint32_t shader_semantic_word(const ShaderSemantic& semantic) {
|
||||
return ((semantic.semantic & 0xffu) << 0u) | ((semantic.hardware_mapping & 0xffu) << 8u) |
|
||||
((semantic.size_in_elements & 0xfu) << 16u) | ((semantic.is_f16 & 0x3u) << 20u) |
|
||||
@@ -1466,7 +1556,7 @@ uint32_t* KYTY_SYSV_ABI GraphicsDcbContextStateOp(CommandBuffer* buf, uint32_t o
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
uint32_t* first = nullptr;
|
||||
uint32_t* first = nullptr;
|
||||
const auto append = [&](uint32_t segment_size_dw) {
|
||||
auto* segment = buf->AllocateDW(segment_size_dw);
|
||||
if (segment == nullptr) {
|
||||
|
||||
@@ -56,6 +56,8 @@ int KYTY_SYSV_ABI GraphicsCreatePrimState(ShaderRegister* cx_regs, ShaderRegis
|
||||
const Shader* hs, const Shader* gs, uint32_t prim_type);
|
||||
int KYTY_SYSV_ABI GraphicsUpdatePrimState(ShaderRegister* cx_regs, ShaderRegister* uc_regs,
|
||||
uint32_t prim_type);
|
||||
int KYTY_SYSV_ABI GraphicsGetGsOversubscription(ShaderRegister* regs, const Shader* gs,
|
||||
uint32_t budget, float factor);
|
||||
int KYTY_SYSV_ABI GraphicsCreateInterpolantMapping(ShaderRegister* regs, const Shader* gs,
|
||||
const Shader* ps);
|
||||
int KYTY_SYSV_ABI GraphicsGetDataPacketPayloadAddress(uint32_t** addr, uint32_t* cmd, int type);
|
||||
|
||||
+659
-240
File diff suppressed because it is too large
Load Diff
+14
-2
@@ -356,6 +356,18 @@ int KYTY_SYSV_ABI PadInit() {
|
||||
return OK;
|
||||
}
|
||||
|
||||
static bool PadOpenArgsAreValid(int user_id, int type, int index) {
|
||||
constexpr int user_id_initial = 1000;
|
||||
constexpr int user_id_system = 0xff;
|
||||
constexpr int port_type_standard = 0;
|
||||
constexpr int port_type_special = 2;
|
||||
constexpr int port_type_remote = 16;
|
||||
const bool personal_port =
|
||||
user_id == user_id_initial && (type == port_type_standard || type == port_type_special);
|
||||
const bool system_remote_control = user_id == user_id_system && type == port_type_remote;
|
||||
return index == 0 && (personal_port || system_remote_control);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PadOpen(int user_id, int type, int index, const void* param) {
|
||||
PRINT_NAME();
|
||||
|
||||
@@ -367,7 +379,7 @@ int KYTY_SYSV_ABI PadOpen(int user_id, int type, int index, const void* param) {
|
||||
|
||||
constexpr int pad_error_invalid_arg = -2137915391; /* 0x80920001 */
|
||||
|
||||
if (user_id != 1000 || (type != 0 && type != 2 && type != 16) || index != 0) {
|
||||
if (!PadOpenArgsAreValid(user_id, type, index)) {
|
||||
return pad_error_invalid_arg;
|
||||
}
|
||||
|
||||
@@ -386,7 +398,7 @@ int KYTY_SYSV_ABI PadGetHandle(int user_id, int type, int index) {
|
||||
|
||||
constexpr int pad_error_device_no_handle = -2137915384; /* 0x80920008 */
|
||||
|
||||
if (user_id != 1000 || (type != 0 && type != 2 && type != 16) || index != 0) {
|
||||
if (!PadOpenArgsAreValid(user_id, type, index)) {
|
||||
return pad_error_device_no_handle;
|
||||
}
|
||||
|
||||
|
||||
+1020
File diff suppressed because it is too large
Load Diff
+245
@@ -0,0 +1,245 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_LIBS_IME_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_LIBS_IME_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace Libs::Ime {
|
||||
|
||||
constexpr uint32_t WORK_BUFFER_SIZE = 20 * 1024;
|
||||
constexpr uint32_t MAX_TEXT_LENGTH = 2048;
|
||||
|
||||
enum class Type : uint32_t { Default = 0, BasicLatin = 1, Url = 2, Mail = 3, Number = 4 };
|
||||
enum class EnterLabel : uint32_t { Default = 0, Send = 1, Search = 2, Go = 3 };
|
||||
enum class Alignment : uint32_t { Start = 0, Center = 1, End = 2 };
|
||||
|
||||
struct Event;
|
||||
struct Keycode;
|
||||
|
||||
using TextFilter = int32_t(KYTY_SYSV_ABI*)(char16_t* out_text, uint32_t* out_text_length,
|
||||
const char16_t* source_text,
|
||||
uint32_t source_text_length);
|
||||
using EventHandler = void(KYTY_SYSV_ABI*)(void* arg, const Event* event);
|
||||
using ExtKeyboardFilter = int(KYTY_SYSV_ABI*)(const Keycode* source_keycode, uint16_t* out_keycode,
|
||||
uint32_t* out_status, void* reserved);
|
||||
|
||||
struct Color {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t a;
|
||||
};
|
||||
|
||||
struct Caret {
|
||||
float x;
|
||||
float y;
|
||||
uint32_t height;
|
||||
uint32_t index;
|
||||
};
|
||||
|
||||
struct TextGeometry {
|
||||
float x;
|
||||
float y;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
};
|
||||
|
||||
struct Rect {
|
||||
float x;
|
||||
float y;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
};
|
||||
|
||||
enum class TextAreaMode : uint32_t { Disable = 0, Edit = 1, Preedit = 2, Select = 3 };
|
||||
|
||||
struct TextAreaProperty {
|
||||
TextAreaMode mode;
|
||||
uint32_t index;
|
||||
int32_t length;
|
||||
};
|
||||
|
||||
struct EditText {
|
||||
char16_t* str;
|
||||
uint32_t caret_index;
|
||||
uint32_t area_num;
|
||||
TextAreaProperty text_area[4];
|
||||
};
|
||||
|
||||
struct KeyboardResourceIdArray {
|
||||
int32_t user_id;
|
||||
uint32_t resource_id[5];
|
||||
};
|
||||
|
||||
struct Keycode {
|
||||
uint16_t keycode;
|
||||
char16_t character;
|
||||
uint32_t status;
|
||||
uint32_t type;
|
||||
int32_t user_id;
|
||||
uint32_t resource_id;
|
||||
uint64_t timestamp;
|
||||
};
|
||||
|
||||
union EventParam {
|
||||
Rect rect;
|
||||
EditText text;
|
||||
uint32_t caret_move;
|
||||
Keycode keycode;
|
||||
KeyboardResourceIdArray resource_id_array;
|
||||
uint64_t reserved[8];
|
||||
};
|
||||
|
||||
struct Event {
|
||||
uint32_t id;
|
||||
EventParam param;
|
||||
};
|
||||
|
||||
struct Param {
|
||||
int32_t user_id;
|
||||
Type type;
|
||||
uint64_t supported_languages;
|
||||
EnterLabel enter_label;
|
||||
uint32_t input_method;
|
||||
TextFilter filter;
|
||||
uint32_t option;
|
||||
uint32_t max_text_length;
|
||||
char16_t* input_text_buffer;
|
||||
float posx;
|
||||
float posy;
|
||||
Alignment horizontal_alignment;
|
||||
Alignment vertical_alignment;
|
||||
void* work;
|
||||
void* arg;
|
||||
EventHandler handler;
|
||||
int8_t reserved[8];
|
||||
};
|
||||
|
||||
struct ExtendedParam {
|
||||
uint32_t option;
|
||||
Color color_base;
|
||||
Color color_line;
|
||||
Color color_text_field;
|
||||
Color color_preedit;
|
||||
Color color_button_default;
|
||||
Color color_button_function;
|
||||
Color color_button_symbol;
|
||||
Color color_text;
|
||||
Color color_special;
|
||||
uint32_t priority;
|
||||
const char* additional_dictionary_path;
|
||||
ExtKeyboardFilter ext_keyboard_filter;
|
||||
uint32_t disable_device;
|
||||
uint32_t ext_keyboard_mode;
|
||||
int8_t reserved[60];
|
||||
};
|
||||
|
||||
struct KeyboardParam {
|
||||
uint32_t option;
|
||||
int8_t reserved1[4];
|
||||
void* arg;
|
||||
EventHandler handler;
|
||||
int8_t reserved2[8];
|
||||
};
|
||||
|
||||
struct KeyboardInfo {
|
||||
int32_t user_id;
|
||||
uint32_t device;
|
||||
uint32_t type;
|
||||
uint32_t repeat_delay;
|
||||
uint32_t repeat_rate;
|
||||
uint32_t status;
|
||||
int8_t reserved[12];
|
||||
};
|
||||
|
||||
enum class ExternalAction : uint8_t {
|
||||
None,
|
||||
Text,
|
||||
Backspace,
|
||||
MoveLeft,
|
||||
MoveRight,
|
||||
Cancel,
|
||||
Accept,
|
||||
Newline,
|
||||
};
|
||||
|
||||
struct ExternalInput {
|
||||
Keycode key;
|
||||
ExternalAction action;
|
||||
std::u16string text;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Caret) == 0x10);
|
||||
static_assert(sizeof(TextGeometry) == 0x10);
|
||||
static_assert(sizeof(TextAreaProperty) == 0x0c);
|
||||
static_assert(sizeof(EditText) == 0x40);
|
||||
static_assert(sizeof(EventParam) == 0x40);
|
||||
static_assert(alignof(EventParam) == 0x08);
|
||||
static_assert(sizeof(Event) == 0x48);
|
||||
static_assert(offsetof(Event, param) == 0x08);
|
||||
static_assert(sizeof(Param) == 0x60);
|
||||
static_assert(offsetof(Param, input_text_buffer) == 0x28);
|
||||
static_assert(offsetof(Param, work) == 0x40);
|
||||
static_assert(offsetof(Param, handler) == 0x50);
|
||||
static_assert(sizeof(ExtendedParam) == 0x88);
|
||||
static_assert(offsetof(ExtendedParam, additional_dictionary_path) == 0x30);
|
||||
static_assert(sizeof(KeyboardParam) == 0x20);
|
||||
static_assert(sizeof(KeyboardResourceIdArray) == 0x18);
|
||||
static_assert(sizeof(KeyboardInfo) == 0x24);
|
||||
static_assert(sizeof(Keycode) == 0x20);
|
||||
|
||||
struct VisualState {
|
||||
bool active;
|
||||
uint64_t revision;
|
||||
};
|
||||
|
||||
struct HostSnapshot {
|
||||
uint64_t generation;
|
||||
Type type;
|
||||
EnterLabel enter_label;
|
||||
uint32_t option;
|
||||
uint32_t max_text_length;
|
||||
uint32_t cursor;
|
||||
uint32_t disable_device;
|
||||
bool key_panel_visible;
|
||||
float posx;
|
||||
float posy;
|
||||
Alignment horizontal_alignment;
|
||||
Alignment vertical_alignment;
|
||||
uint32_t panel_width;
|
||||
uint32_t panel_height;
|
||||
std::u16string text;
|
||||
};
|
||||
|
||||
using VisibilityCallback = void (*)(bool visible, uint64_t generation);
|
||||
|
||||
void KYTY_SYSV_ABI ImeParamInit(Param* param);
|
||||
int KYTY_SYSV_ABI ImeGetPanelSize(const Param* param, uint32_t* width, uint32_t* height);
|
||||
int KYTY_SYSV_ABI ImeOpen(const Param* param, const ExtendedParam* extended);
|
||||
int KYTY_SYSV_ABI ImeUpdate(EventHandler handler);
|
||||
int KYTY_SYSV_ABI ImeClose();
|
||||
int KYTY_SYSV_ABI ImeSetText(const char16_t* text, uint32_t length);
|
||||
int KYTY_SYSV_ABI ImeSetCaret(const Caret* caret);
|
||||
int KYTY_SYSV_ABI ImeSetTextGeometry(TextAreaMode mode, const TextGeometry* geometry);
|
||||
int KYTY_SYSV_ABI ImeKeyboardOpen(int32_t user_id, const KeyboardParam* param);
|
||||
int KYTY_SYSV_ABI ImeKeyboardClose(int32_t user_id);
|
||||
int KYTY_SYSV_ABI ImeKeyboardGetResourceId(int32_t user_id, KeyboardResourceIdArray* resource_ids);
|
||||
int KYTY_SYSV_ABI ImeKeyboardGetInfo(uint32_t resource_id, KeyboardInfo* info);
|
||||
int KYTY_SYSV_ABI ImeKeyboardSetMode(int32_t user_id, uint32_t mode);
|
||||
|
||||
VisualState GetVisualState() noexcept;
|
||||
void SetVisibilityCallback(VisibilityCallback callback) noexcept;
|
||||
bool GetHostSnapshot(HostSnapshot* snapshot);
|
||||
bool HostInsertText(uint64_t generation, std::u16string_view text);
|
||||
bool HostBackspace(uint64_t generation);
|
||||
bool HostAccept(uint64_t generation);
|
||||
bool HostCancel(uint64_t generation);
|
||||
bool HostQueueExternalInput(uint64_t generation, ExternalInput input);
|
||||
|
||||
} // namespace Libs::Ime
|
||||
|
||||
#endif // EMULATOR_INCLUDE_EMULATOR_LIBS_IME_H_
|
||||
@@ -171,6 +171,7 @@ LIB_DEFINE(InitGraphicsDriver_1) {
|
||||
LIB_FUNC("vRoArM9zaIk", Gen5::GraphicsSetUcRegIndirectPatchAddRegisters);
|
||||
LIB_FUNC("D9sr1xGUriE", Gen5::GraphicsCreatePrimState);
|
||||
LIB_FUNC("Y3ymLfZ1384", Gen5::GraphicsUpdatePrimState);
|
||||
LIB_FUNC("NKIzURsgV7I", Gen5::GraphicsGetGsOversubscription);
|
||||
LIB_FUNC("HV4j+E0MBHE", Gen5::GraphicsCreateInterpolantMapping);
|
||||
LIB_FUNC("V++UgBtQhn0", Gen5::GraphicsGetDataPacketPayloadAddress);
|
||||
LIB_FUNC("s+VGAMDQ0AQ", Gen5::GraphicsGetDataPacketPayloadRange);
|
||||
|
||||
+23
-10
@@ -167,6 +167,19 @@ static JsonObject* JsonObjectNew() {
|
||||
return object;
|
||||
}
|
||||
|
||||
static void JsonValueSetEmptyType(JsonValue* self, uint32_t type) {
|
||||
if (self == nullptr) {
|
||||
return;
|
||||
}
|
||||
self->type = type;
|
||||
switch (type) {
|
||||
case JsonValueTypeString: self->string = JsonStringNew(""); break;
|
||||
case JsonValueTypeArray: self->array = JsonArrayNew(); break;
|
||||
case JsonValueTypeObject: self->object = JsonObjectNew(); break;
|
||||
default: self->uinteger = 0; break;
|
||||
}
|
||||
}
|
||||
|
||||
static void JsonValueDelete(JsonValue* self) {
|
||||
if (self != nullptr) {
|
||||
JsonValueClear(self);
|
||||
@@ -497,6 +510,14 @@ static void* KYTY_SYSV_ABI JsonValueCtor(void* self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
static JsonValue* KYTY_SYSV_ABI JsonValueTypeCtor(JsonValue* self, uint32_t type) {
|
||||
PRINT_NAME();
|
||||
|
||||
JsonValueInit(self);
|
||||
JsonValueSetEmptyType(self, type);
|
||||
return self;
|
||||
}
|
||||
|
||||
static JsonValue* KYTY_SYSV_ABI JsonValueBoolCtor(JsonValue* self, bool value) {
|
||||
PRINT_NAME();
|
||||
|
||||
@@ -637,16 +658,7 @@ static void KYTY_SYSV_ABI JsonValueSetType(JsonValue* self, uint32_t type) {
|
||||
PRINT_NAME();
|
||||
|
||||
JsonValueClear(self);
|
||||
if (self == nullptr) {
|
||||
return;
|
||||
}
|
||||
self->type = type;
|
||||
switch (type) {
|
||||
case JsonValueTypeString: self->string = JsonStringNew(""); break;
|
||||
case JsonValueTypeArray: self->array = JsonArrayNew(); break;
|
||||
case JsonValueTypeObject: self->object = JsonObjectNew(); break;
|
||||
default: self->uinteger = 0; break;
|
||||
}
|
||||
JsonValueSetEmptyType(self, type);
|
||||
}
|
||||
|
||||
static uint32_t KYTY_SYSV_ABI JsonValueGetType(const JsonValue* self) {
|
||||
@@ -939,6 +951,7 @@ LIB_DEFINE(InitNet_1_Json2) {
|
||||
LIB_FUNC("OcAgPxcq5Vk", LibJson2::JsonMemAllocatorDtor);
|
||||
LIB_FUNC("qBMjqyBn3OM", LibJson2::JsonValueCtor);
|
||||
LIB_FUNC("-wa17B7TGnw", LibJson2::JsonValueCtor);
|
||||
LIB_FUNC("CbrT3dwDILo", LibJson2::JsonValueTypeCtor);
|
||||
LIB_FUNC("WTtYf+cNnXI", LibJson2::JsonValueDtor);
|
||||
LIB_FUNC("0eUrW9JAxM0", LibJson2::JsonValueDtor);
|
||||
LIB_FUNC("S5JxQnoGF3E", LibJson2::JsonParserParse);
|
||||
|
||||
@@ -3804,77 +3804,6 @@ LIB_DEFINE(InitPlatform_1_Random) {
|
||||
|
||||
} // namespace LibRandom
|
||||
|
||||
namespace LibIme {
|
||||
|
||||
LIB_VERSION("Ime", 1, "Ime", 1, 1);
|
||||
|
||||
static int KYTY_SYSV_ABI ImeKeyboardOpen(int user_id, const void* param) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("\t user_id = %d\n"
|
||||
"\t param = 0x%016" PRIx64 "\n",
|
||||
user_id, reinterpret_cast<uint64_t>(param));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI ImeKeyboardClose(int user_id) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("\t user_id = %d\n", user_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI ImeKeyboardGetResourceId(int user_id, void* resource_id_array) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("\t user_id = %d\n"
|
||||
"\t resource_id_array = 0x%016" PRIx64 "\n",
|
||||
user_id, reinterpret_cast<uint64_t>(resource_id_array));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI ImeKeyboardGetInfo(uint32_t resource_id, void* info) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("\t resource_id = 0x%08" PRIx32 "\n"
|
||||
"\t info = 0x%016" PRIx64 "\n",
|
||||
resource_id, reinterpret_cast<uint64_t>(info));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI ImeKeyboardSetMode(int user_id, uint32_t mode) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("\t user_id = %d\n"
|
||||
"\t mode = 0x%08" PRIx32 "\n",
|
||||
user_id, mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI ImeUpdate(void* handler) {
|
||||
PRINT_NAME();
|
||||
|
||||
// LOGF("\t handler = 0x%016" PRIx64 "\n", reinterpret_cast<uint64_t>(handler));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIB_DEFINE(InitPlatform_1_Ime) {
|
||||
LIB_FUNC("eaFXjfJv3xs", LibIme::ImeKeyboardOpen);
|
||||
LIB_FUNC("PMVehSlfZ94", LibIme::ImeKeyboardClose);
|
||||
LIB_FUNC("dKadqZFgKKQ", LibIme::ImeKeyboardGetResourceId);
|
||||
LIB_FUNC("VkqLPArfFdc", LibIme::ImeKeyboardGetInfo);
|
||||
LIB_FUNC("ua+13Hk9kKs", LibIme::ImeKeyboardSetMode);
|
||||
LIB_FUNC("-4GCfYdNF1s", LibIme::ImeUpdate);
|
||||
}
|
||||
|
||||
} // namespace LibIme
|
||||
|
||||
namespace LibRemoteplay {
|
||||
|
||||
LIB_VERSION("Remoteplay", 1, "Remoteplay", 1, 1);
|
||||
@@ -3994,7 +3923,6 @@ LIB_DEFINE(InitPlatform_1_SharePlay) {
|
||||
|
||||
LIB_DEFINE(InitPlatform_1) {
|
||||
LibRandom::InitPlatform_1_Random(s);
|
||||
LibIme::InitPlatform_1_Ime(s);
|
||||
LibRemoteplay::InitPlatform_1_Remoteplay(s);
|
||||
LibWebBrowserDialog::InitPlatform_1_WebBrowserDialog(s);
|
||||
LibGameLiveStreaming::InitPlatform_1_GameLiveStreaming(s);
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace SystemService {
|
||||
[[maybe_unused]] constexpr int PARAM_ID_SUMMERTIME = 5;
|
||||
[[maybe_unused]] constexpr int PARAM_ID_SYSTEM_NAME = 6;
|
||||
[[maybe_unused]] constexpr int PARAM_ID_GAME_PARENTAL_LEVEL = 7;
|
||||
[[maybe_unused]] constexpr int PARAM_ID_CC_ENABLE = 100;
|
||||
[[maybe_unused]] constexpr int PARAM_ID_SCREEN_READER = 208;
|
||||
[[maybe_unused]] constexpr int PARAM_ID_ENTER_BUTTON_ASSIGN = 1000;
|
||||
|
||||
@@ -33,6 +34,9 @@ namespace SystemService {
|
||||
[[maybe_unused]] constexpr int PARAM_TIME_FORMAT_12HOUR = 0;
|
||||
[[maybe_unused]] constexpr int PARAM_TIME_FORMAT_24HOUR = 1;
|
||||
|
||||
[[maybe_unused]] constexpr int PARAM_CC_DISABLED = 0;
|
||||
[[maybe_unused]] constexpr int PARAM_CC_ENABLED = 1;
|
||||
|
||||
[[maybe_unused]] constexpr int MAX_SYSTEM_NAME_LENGTH = 65;
|
||||
|
||||
[[maybe_unused]] constexpr int PARAM_GAME_PARENTAL_OFF = 0;
|
||||
@@ -97,9 +101,12 @@ static int KYTY_SYSV_ABI SystemServiceParamGetInt(int param_id, int* value) {
|
||||
case PARAM_ID_TIME_ZONE: v = +180; break;
|
||||
case PARAM_ID_SUMMERTIME: v = 0; break;
|
||||
case PARAM_ID_GAME_PARENTAL_LEVEL: v = PARAM_GAME_PARENTAL_OFF; break;
|
||||
case PARAM_ID_CC_ENABLE: v = PARAM_CC_DISABLED; break;
|
||||
case PARAM_ID_SCREEN_READER: v = 0; break;
|
||||
case PARAM_ID_ENTER_BUTTON_ASSIGN: v = PARAM_ENTER_BUTTON_ASSIGN_CROSS; break;
|
||||
default: EXIT("unknown param_id: %d\n", param_id);
|
||||
default:
|
||||
LOGF_COLOR(Log::Color::Yellow, " unsupported param_id %d, using 0\n", param_id);
|
||||
break;
|
||||
}
|
||||
|
||||
LOGF(" %d = %d\n", param_id, v);
|
||||
|
||||
@@ -34,6 +34,10 @@ namespace LibKeyboard {
|
||||
LIB_DEFINE(InitKeyboard_1);
|
||||
} // namespace LibKeyboard
|
||||
|
||||
namespace Ime {
|
||||
LIB_DEFINE(InitPlatform_1_Ime);
|
||||
} // namespace Ime
|
||||
|
||||
namespace LibUlt {
|
||||
LIB_DEFINE(InitUlt_1);
|
||||
} // namespace LibUlt
|
||||
@@ -89,6 +93,7 @@ void InitAll(Loader::SymbolDatabase* s) {
|
||||
LIB_LOAD(InitLibKernel_1);
|
||||
LIB_LOAD(LibMouse::InitMouse_1);
|
||||
LIB_LOAD(LibKeyboard::InitKeyboard_1);
|
||||
LIB_LOAD(Ime::InitPlatform_1_Ime);
|
||||
LIB_LOAD(InitNet_1);
|
||||
LIB_LOAD(InitPad_1);
|
||||
LIB_LOAD(InitPlayGo_1);
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ static void PrintUsage() {
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
::printf(" --redzone Protect the guest SysV red zone.\n");
|
||||
#endif
|
||||
::printf(" --keymap <Pad=Key> DualSense mapping; may be repeated.\n");
|
||||
::printf(" --keymap <Control=Input> DualSense mapping; may be repeated.\n");
|
||||
::printf(" --rd Enable RenderDoc capture.\n");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user