diff --git a/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp b/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp index 0c7416885a..63abe5298c 100644 --- a/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp +++ b/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp @@ -1,7 +1,8 @@ -#include "stdafx.h" +#include "stdafx.h" #include "Emu/System.h" #include "Emu/IdManager.h" #include "Emu/Cell/PPUModule.h" +#include "Emu/RSX/Overlays/overlays.h" #include "pad_thread.h" #include "cellSysutil.h" @@ -31,6 +32,39 @@ OskDialogBase::~OskDialogBase() { } +std::shared_ptr _get_osk_dialog(bool always = false) +{ + if (auto manager = fxm::get()) + { + // NOTE: Osk lifetime is managed by fxm, not display manager + // Visibility is still managed by display manager + auto osk = fxm::get(); + if (always) + { + if (!osk) + { + // Create if does not exist + osk = fxm::make(); + } + + // Attach to display manager forcefully + osk = manager->add(osk); + } + + return osk; + } + else + { + auto osk = fxm::get(); + if (always && !osk) + { + return fxm::import(Emu.GetCallbacks().get_osk_dialog); + } + + return osk; + } +} + error_code cellOskDialogLoadAsync(u32 container, vm::ptr dialogParam, vm::ptr inputFieldInfo) { cellOskDialog.warning("cellOskDialogLoadAsync(container=0x%x, dialogParam=*0x%x, inputFieldInfo=*0x%x)", container, dialogParam, inputFieldInfo); @@ -40,11 +74,7 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr dia return CELL_OSKDIALOG_ERROR_PARAM; } - auto osk = fxm::get(); - if (!osk) - { - osk = fxm::import(Emu.GetCallbacks().get_osk_dialog); - } + auto osk = _get_osk_dialog(true); // Can't open another dialog if this one is already open. if (!osk || osk->state.load() == OskDialogState::Open) @@ -85,7 +115,7 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr dia bool result = false; - osk->on_close = [maxLength, wptr = std::weak_ptr(osk)](s32 status) + osk->on_osk_close = [maxLength, wptr = std::weak_ptr(osk)](s32 status) { const auto osk = wptr.lock(); @@ -216,7 +246,7 @@ error_code getText(vm::ptr OutputInfo, bool is return CELL_OSKDIALOG_ERROR_PARAM; } - const auto osk = fxm::get(); + const auto osk = _get_osk_dialog(); if (!osk) { @@ -306,7 +336,7 @@ error_code cellOskDialogAbort() { cellOskDialog.warning("cellOskDialogAbort()"); - const auto osk = fxm::get(); + const auto osk = _get_osk_dialog(); if (!osk) { @@ -358,12 +388,7 @@ error_code cellOskDialogSetSeparateWindowOption(vm::ptr(); - if (!osk) - { - osk = fxm::import(Emu.GetCallbacks().get_osk_dialog); - } - + auto osk = _get_osk_dialog(true); osk->use_seperate_windows = true; osk->osk_continuous_mode = (CellOskDialogContinuousMode)(u32)windowOption->continuousMode; @@ -446,7 +471,7 @@ error_code cellOskDialogExtSendFinishMessage(u32 /*CellOskDialogFinishReason*/ f { cellOskDialog.warning("cellOskDialogExtSendFinishMessage(finishReason=%d)", finishReason); - const auto osk = fxm::get(); + const auto osk = _get_osk_dialog(); // Check for open dialog. if (!osk || osk->state.load() != OskDialogState::Open) @@ -492,11 +517,7 @@ error_code cellOskDialogExtRegisterConfirmWordFilterCallback(vm::ptr(); - if (!osk) - { - osk = fxm::import(Emu.GetCallbacks().get_osk_dialog); - } + auto osk = _get_osk_dialog(true); osk->osk_confirm_callback = pCallback; return CELL_OK; diff --git a/rpcs3/Emu/Cell/Modules/cellOskDialog.h b/rpcs3/Emu/Cell/Modules/cellOskDialog.h index 4f1fcb9076..51458f376e 100644 --- a/rpcs3/Emu/Cell/Modules/cellOskDialog.h +++ b/rpcs3/Emu/Cell/Modules/cellOskDialog.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once @@ -247,7 +247,7 @@ public: virtual void Close(bool accepted) = 0; virtual ~OskDialogBase(); - std::function on_close; + std::function on_osk_close; std::function on_osk_input_entered; atomic_t state{ OskDialogState::Close }; diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 57d05f77dc..c767629411 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -946,7 +946,7 @@ void GLGSRender::on_init_thread() struct native_helper : gl::shader_cache::progress_dialog_helper { rsx::thread *owner = nullptr; - rsx::overlays::message_dialog *dlg = nullptr; + std::shared_ptr dlg; native_helper(GLGSRender *ptr) : owner(ptr) {} diff --git a/rpcs3/Emu/RSX/Overlays/overlay_controls.h b/rpcs3/Emu/RSX/Overlays/overlay_controls.h index 9d457a8aa4..8955a86af9 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_controls.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_controls.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "Utilities/types.h" #include "Utilities/geometry.h" #include "Emu/System.h" @@ -1292,7 +1292,8 @@ namespace rsx struct image_button : public image_view { const u16 text_horizontal_offset = 25; - u16 m_text_offset = 0; + u16 m_text_offset_x = 0; + s16 m_text_offset_y = 0; image_button() { @@ -1307,10 +1308,15 @@ namespace rsx set_size(_w, _h); } + void set_text_vertical_adjust(s16 offset) + { + m_text_offset_y = offset; + } + void set_size(u16 /*w*/, u16 h) override { image_view::set_size(h, h); - m_text_offset = (h / 2) + text_horizontal_offset; // By default text is at the horizontal center + m_text_offset_x = (h / 2) + text_horizontal_offset; // By default text is at the horizontal center } compiled_resource& get_compiled() override @@ -1325,7 +1331,8 @@ namespace rsx // Text, translate geometry to the right for (auto &v : cmd.verts) { - v.values[0] += m_text_offset; + v.values[0] += m_text_offset_x; + v.values[1] += m_text_offset_y; } } } diff --git a/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp new file mode 100644 index 0000000000..706573cc5f --- /dev/null +++ b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp @@ -0,0 +1,563 @@ +#include "stdafx.h" +#include "overlays.h" + +namespace rsx +{ + namespace overlays + { + void osk_dialog::Close(bool ok) + { + if (on_osk_close) + { + if (ok) + { + on_osk_close(CELL_MSGDIALOG_BUTTON_OK); + } + else + { + on_osk_close(CELL_MSGDIALOG_BUTTON_ESCAPE); + } + } + + m_visible = false; + close(); + } + + void osk_dialog::initialize_layout(const std::vector& layout, const std::string& title, const std::string& initial_text) + { + const u32 cell_count = num_rows * num_columns; + + m_grid.resize(cell_count); + u32 index = 0; + + for (const auto& props : layout) + { + for (u32 c = 0; c < props.num_cell_hz; ++c) + { + const auto row = (index / num_columns); + const auto col = (index % num_columns); + verify(HERE), row < num_rows && col < num_columns; + + auto &_cell = m_grid[index++]; + _cell.pos = { col * cell_size_x, row * cell_size_y }; + _cell.backcolor = props.color; + _cell.callback = props.callback; + _cell.outputs = props.outputs; + _cell.selected = 0; + + num_layers = std::max(num_layers, _cell.outputs.size()); + + if (LIKELY(props.num_cell_hz == 1)) + { + _cell.flags = border_flags::default_cell; + } + else if (c == 0) + { + // Leading cell + _cell.flags = border_flags::start_cell; + } + else if (c == (props.num_cell_hz - 1)) + { + // Last cell + _cell.flags = border_flags::end_cell; + } + else + { + // Middle cell + _cell.flags = border_flags::middle_cell; + } + } + } + + verify(HERE), num_layers; + + selected_x = selected_y = selected_z = 0; + m_grid[0].selected = true; + m_update = true; + + m_background.set_size(1280, 720); + m_background.back_color.a = 0.8f; + + // Place elements with absolute positioning + u16 frame_w = u16(num_columns * cell_size_x); + u16 frame_h = u16(num_rows * cell_size_y) + 80; + u16 frame_x = (1280 - frame_w) / 2; + u16 frame_y = (720 - frame_h) / 2; + + m_frame.set_pos(frame_x, frame_y); + m_frame.set_size(frame_w, frame_h); + m_frame.back_color = { 0.2f, 0.2f, 0.2f, 1.f }; + + m_title.set_pos(frame_x, frame_y); + m_title.set_size(frame_w, 30); + m_title.set_text(title); + m_title.set_padding(15, 0, 5, 0); + m_title.back_color.a = 0.f; + + m_preview.set_pos(frame_x, frame_y + 30); + m_preview.set_size(frame_w, 40); + m_preview.set_text(initial_text.empty()? "[Enter Text]" : initial_text); + m_preview.set_padding(15, 0, 10, 0); + + position2u grid_origin = { frame_x, frame_y + 70u }; + for (auto &_cell : m_grid) + { + _cell.pos += grid_origin; + } + + m_btn_shift.set_pos(frame_x + 50, frame_y + frame_h + 10); + m_btn_shift.set_size(140, 30); + m_btn_shift.set_text("Shift"); + m_btn_shift.set_text_vertical_adjust(5); + + m_btn_accept.set_pos(frame_x + 200, frame_y + frame_h + 10); + m_btn_accept.set_size(140, 30); + m_btn_accept.set_text("Accept"); + m_btn_accept.set_text_vertical_adjust(5); + + m_btn_cancel.set_pos(frame_x + 350, frame_y + frame_h + 10); + m_btn_cancel.set_size(140, 30); + m_btn_cancel.set_text("Cancel"); + m_btn_cancel.set_text_vertical_adjust(5); + + m_btn_shift.set_image_resource(resource_config::standard_image_resource::triangle); + m_btn_accept.set_image_resource(resource_config::standard_image_resource::square); + + if (g_cfg.sys.enter_button_assignment == enter_button_assign::circle) + { + m_btn_cancel.set_image_resource(resource_config::standard_image_resource::cross); + } + else + { + m_btn_cancel.set_image_resource(resource_config::standard_image_resource::circle); + } + + m_visible = true; + exit = false; + + thread_ctrl::spawn("osk input thread", [this] + { + if (auto error = run_input_loop()) + { + LOG_ERROR(RSX, "Osk input loop exited with error code=%d", error); + } + }); + } + + void osk_dialog::on_button_pressed(pad_button button_press) + { + const auto index_limit = (num_columns * num_rows) - 1; + + auto get_cell_geometry = [&](u32 index) + { + u32 start_index = index, count = 0; + + // Find first cell + while (!(m_grid[start_index].flags & border_flags::left) && start_index) + { + start_index--; + } + + // Find last cell + while (true) + { + const auto current_index = (start_index + count); + verify(HERE), current_index <= index_limit; + + if (m_grid[current_index].flags & border_flags::right) + { + count++; + break; + } + + count++; + } + + return std::make_pair(start_index, count); + }; + + auto select_cell = [&](u32 index, bool state) + { + const auto info = get_cell_geometry(index); + + // Tag all in range + for (u32 _index = info.first, _ctr = 0; _ctr < info.second; ++_index, ++_ctr) + { + m_grid[_index].selected = state; + } + }; + + auto decode_index = [&](u32 index) + { + // 1. Deselect current + auto current_index = (selected_y * num_columns) + selected_x; + select_cell(current_index, false); + + // 2. Select new + selected_y = index / num_columns; + selected_x = index % num_columns; + select_cell(index, true); + }; + + auto on_accept = [&]() + { + const u32 current_index = (selected_y * num_columns) + selected_x; + const u32 output_count = (u32)m_grid[current_index].outputs.size(); + + if (output_count) + { + const auto _z = std::clamp(selected_z, 0u, output_count - 1u); + const auto& str = m_grid[current_index].outputs[_z]; + + if (m_grid[current_index].callback) + { + m_grid[current_index].callback(str); + } + else + { + on_default_callback(str); + } + } + }; + + switch (button_press) + { + case pad_button::dpad_right: + { + u32 current_index = (selected_y * num_columns) + selected_x; + const auto current = get_cell_geometry(current_index); + + current_index = current.first + current.second; + + if (current_index < index_limit) + { + decode_index(current_index); + m_update = true; + } + break; + } + case pad_button::dpad_left: + { + u32 current_index = (selected_y * num_columns) + selected_x; + if (current_index > 0) + { + const auto current = get_cell_geometry(current_index); + if (current.first) + { + current_index = current.first - 1; + decode_index(current_index); + m_update = true; + } + } + break; + } + case pad_button::dpad_down: + { + u32 current_index = (selected_y * num_columns) + selected_x; + current_index += num_columns; + + if (current_index <= index_limit) + { + decode_index(current_index); + m_update = true; + } + break; + } + case pad_button::dpad_up: + { + u32 current_index = (selected_y * num_columns) + selected_x; + if (current_index >= num_columns) + { + current_index -= num_columns; + decode_index(current_index); + m_update = true; + } + break; + } + case pad_button::triangle: + { + on_shift("Shift"); + break; + } + case pad_button::square: + { + Close(true); + break; + } + case pad_button::cross: + { + if (g_cfg.sys.enter_button_assignment != enter_button_assign::circle) + { + on_accept(); + } + else + { + Close(false); + } + break; + } + case pad_button::circle: + { + if (g_cfg.sys.enter_button_assignment == enter_button_assign::circle) + { + on_accept(); + } + else + { + Close(false); + } + break; + } + } + } + + void osk_dialog::on_text_changed() + { + const auto ws = utf8_to_utf16(m_preview.text); + const auto length = (ws.length() + 1) * sizeof(char16_t); + memcpy(osk_text, ws.c_str(), length); + + on_osk_input_entered(); + m_update = true; + } + + void osk_dialog::on_default_callback(const std::string& str) + { + // Append to output text + if (m_preview.text == "[Enter Text]") + { + m_preview.set_text(str); + } + else + { + if (m_preview.text.length() == char_limit) + { + return; + } + + auto new_str = m_preview.text + str; + if (new_str.length() > char_limit) + { + new_str = new_str.substr(0, char_limit); + } + + m_preview.set_text(new_str); + } + + on_text_changed(); + } + + void osk_dialog::on_shift(const std::string&) + { + selected_z = (selected_z + 1) % num_layers; + m_update = true; + } + + void osk_dialog::on_space(const std::string&) + { + if (!(flags & CELL_OSKDIALOG_NO_SPACE)) + { + on_default_callback(" "); + } + else + { + // Beep or give some other kind of visual feedback + } + } + + void osk_dialog::on_backspace(const std::string&) + { + if (m_preview.text.empty()) + { + return; + } + + auto new_length = m_preview.text.length() - 1; + m_preview.set_text(m_preview.text.substr(0, new_length)); + on_text_changed(); + } + + void osk_dialog::on_enter(const std::string&) + { + // Accept dialog + Close(true); + } + + compiled_resource osk_dialog::get_compiled() + { + if (!m_visible) + { + return {}; + } + + if (m_update) + { + m_cached_resource.clear(); + m_cached_resource.add(m_background.get_compiled()); + m_cached_resource.add(m_frame.get_compiled()); + m_cached_resource.add(m_title.get_compiled()); + m_cached_resource.add(m_preview.get_compiled()); + m_cached_resource.add(m_btn_accept.get_compiled()); + m_cached_resource.add(m_btn_cancel.get_compiled()); + m_cached_resource.add(m_btn_shift.get_compiled()); + + overlay_element tmp; + label m_label; + u16 buffered_cell_count; + bool render_label = false; + + m_label.back_color = { 0.f, 0.f, 0.f, 0.f }; + m_label.fore_color = { 0.f, 0.f, 0.f, 1.f }; + m_label.set_padding(0, 0, 10, 0); + + for (const auto& c : m_grid) + { + u16 x = u16(c.pos.x); + u16 y = u16(c.pos.y); + u16 w = cell_size_x; + u16 h = cell_size_y; + + if (c.flags & border_flags::left) + { + x++; + w--; + buffered_cell_count = 0; + } + + if (c.flags & border_flags::right) + { + w--; + + if (!c.outputs.empty()) + { + u16 offset_x = u16(buffered_cell_count * cell_size_x); + u16 full_width = u16(offset_x + cell_size_x); + + m_label.set_pos(x - offset_x, y); + m_label.set_size(full_width, cell_size_y); + + auto _z = (selected_z < c.outputs.size()) ? selected_z : u32(c.outputs.size()) - 1u; + m_label.set_text(c.outputs[_z]); + m_label.align_text(rsx::overlays::overlay_element::text_align::center); + render_label = true; + } + } + + if (c.flags & border_flags::top) + { + y++; + h--; + } + + if (c.flags & border_flags::bottom) + { + h--; + } + + buffered_cell_count++; + + tmp.back_color = c.backcolor; + tmp.set_pos(x, y); + tmp.set_size(w, h); + tmp.pulse_effect_enabled = c.selected; + + m_cached_resource.add(tmp.get_compiled()); + + if (render_label) + { + m_label.pulse_effect_enabled = c.selected; + m_cached_resource.add(m_label.get_compiled()); + } + } + + m_update = false; + } + + return m_cached_resource; + } + + // Language specific implementations + void osk_enUS::Create(const std::string& title, const std::u16string& message, char16_t* init_text, u32 charlimit, u32 options) + { + state = OskDialogState::Open; + flags = options; + char_limit = charlimit; + + if (!(flags & CELL_OSKDIALOG_NO_RETURN)) + { + LOG_WARNING(RSX, "Native OSK dialog does not support multiline text!"); + } + + color4f default_bg = { 0.5f, 0.5f, 0.5f, 1.f }; + color4f special_bg = { 0.5f, 0.5f, 0.8f, 1.f }; + color4f special2_bg = { 0.8f, 0.8f, 0.5f, 1.f }; + + num_rows = 5; + num_columns = 10; + cell_size_x = 50; + cell_size_y = 40; + + callback_t shift_callback = std::bind(&osk_dialog::on_shift, this, std::placeholders::_1); + callback_t space_callback = std::bind(&osk_dialog::on_space, this, std::placeholders::_1); + callback_t delete_callback = std::bind(&osk_dialog::on_backspace, this, std::placeholders::_1); + callback_t enter_callback = std::bind(&osk_dialog::on_enter, this, std::placeholders::_1); + + std::vector layout = + { + // Alphanumeric + {{"1", "!"}, default_bg, 1}, + {{"2", "@"}, default_bg, 1}, + {{"3", "#"}, default_bg, 1}, + {{"4", "$"}, default_bg, 1}, + {{"5", "%"}, default_bg, 1}, + {{"6", "^"}, default_bg, 1}, + {{"7", "&"}, default_bg, 1}, + {{"8", "*"}, default_bg, 1}, + {{"9", "("}, default_bg, 1}, + {{"0", ")"}, default_bg, 1}, + + // Alpha + {{"q", "Q"}, default_bg, 1}, + {{"w", "W"}, default_bg, 1}, + {{"e", "E"}, default_bg, 1}, + {{"r", "R"}, default_bg, 1}, + {{"t", "T"}, default_bg, 1}, + {{"y", "Y"}, default_bg, 1}, + {{"u", "U"}, default_bg, 1}, + {{"i", "I"}, default_bg, 1}, + {{"o", "O"}, default_bg, 1}, + {{"p", "P"}, default_bg, 1}, + {{"a", "A"}, default_bg, 1}, + {{"s", "S"}, default_bg, 1}, + {{"d", "D"}, default_bg, 1}, + {{"f", "F"}, default_bg, 1}, + {{"g", "G"}, default_bg, 1}, + {{"h", "H"}, default_bg, 1}, + {{"j", "J"}, default_bg, 1}, + {{"k", "K"}, default_bg, 1}, + {{"l", "L"}, default_bg, 1}, + {{"'", "\""}, default_bg, 1}, + {{"z", "Z"}, default_bg, 1}, + {{"x", "X"}, default_bg, 1}, + {{"c", "C"}, default_bg, 1}, + {{"v", "V"}, default_bg, 1}, + {{"b", "B"}, default_bg, 1}, + {{"n", "N"}, default_bg, 1}, + {{"m", "M"}, default_bg, 1}, + {{"-", "_"}, default_bg, 1}, + {{"+", "="}, default_bg, 1}, + {{",", "?"}, default_bg, 1}, + + // Special + {{"Shift"}, special2_bg, 2, shift_callback }, + {{"Space"}, special_bg, 4, space_callback }, + {{"Backspace"}, special_bg, 2, delete_callback }, + {{"Enter"}, special2_bg, 2, enter_callback }, + }; + + // Narrow to utf-8 as native does not have support for non-ascii glyphs + // TODO: Full multibyte string support in all of rsx::overlays (kd-11) + initialize_layout(layout, utf16_to_utf8(message), utf16_to_utf8(init_text)); + } + } +} diff --git a/rpcs3/Emu/RSX/Overlays/overlays.cpp b/rpcs3/Emu/RSX/Overlays/overlays.cpp index 0793e850d2..7e49367958 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlays.cpp @@ -2,6 +2,41 @@ #include "overlays.h" #include "../GSRender.h" +#include +#include + +#if _MSC_VER >= 1900 +// Stupid MSVC bug when T is set to char16_t +std::string utf16_to_utf8(const std::u16string& utf16_string) +{ + std::wstring_convert, int16_t> convert; + auto p = reinterpret_cast(utf16_string.data()); + return convert.to_bytes(p, p + utf16_string.size()); +} + +std::u16string utf8_to_utf16(const std::string& utf8_string) +{ + std::wstring_convert, int16_t> convert; + auto ws = convert.from_bytes(utf8_string); + return reinterpret_cast(ws.c_str()); +} + +#else + +std::string utf16_to_utf8(const std::u16string& utf16_string) +{ + std::wstring_convert, char16_t> convert; + return convert.to_bytes(utf16_string); +} + +std::u16string utf8_to_utf16(const std::string& utf8_string) +{ + std::wstring_convert, char16_t> convert; + return convert.from_bytes(utf8_string); +} + +#endif + namespace rsx { namespace overlays diff --git a/rpcs3/Emu/RSX/Overlays/overlays.h b/rpcs3/Emu/RSX/Overlays/overlays.h index cbfd1d390e..35db31860a 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.h +++ b/rpcs3/Emu/RSX/Overlays/overlays.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "overlay_controls.h" #include "../../../Utilities/date_time.h" @@ -11,10 +11,14 @@ #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/Modules/cellSaveData.h" #include "Emu/Cell/Modules/cellMsgDialog.h" +#include "Emu/Cell/Modules/cellOskDialog.h" #include "Emu/Cell/Modules/sceNpTrophy.h" #include "Utilities/CPUStats.h" #include "Utilities/Timer.h" +// Utils +std::string utf16_to_utf8(const std::u16string& utf16_string); +std::u16string utf8_to_utf16(const std::string& utf8_string); extern u64 get_system_time(); // Definition of user interface implementations @@ -87,8 +91,8 @@ namespace rsx { private: atomic_t m_uid_ctr { 0u }; - std::vector> m_iface_list; - std::vector> m_dirty_list; + std::vector> m_iface_list; + std::vector> m_dirty_list; shared_mutex m_list_mutex; std::vector m_uids_to_remove; @@ -153,38 +157,36 @@ namespace rsx // Adds an object to the internal list. Optionally removes other objects of the same type. // Original handle loses ownership but a usable pointer is returned template - T* add(std::unique_ptr& entry, bool remove_existing = true) + std::shared_ptr add(std::shared_ptr& entry, bool remove_existing = true) { std::lock_guard lock(m_list_mutex); - T* e = entry.get(); - e->uid = m_uid_ctr.fetch_add(1); - e->type_index = id_manager::typeinfo::get_index(); + entry->uid = m_uid_ctr.fetch_add(1); + entry->type_index = id_manager::typeinfo::get_index(); if (remove_existing) { for (auto It = m_iface_list.begin(); It != m_iface_list.end(); It++) { - if (It->get()->type_index == e->type_index) + if (It->get()->type_index == entry->type_index) { // Replace m_dirty_list.push_back(std::move(*It)); - It->reset(e); - entry.reset(); - return e; + *It = std::move(entry); + return std::static_pointer_cast(*It); } } } m_iface_list.push_back(std::move(entry)); - return e; + return std::static_pointer_cast(m_iface_list.back()); } // Allocates object and adds to internal list. Returns pointer to created object template - T* create(Args&&... args) + std::shared_ptr create(Args&&... args) { - std::unique_ptr object = std::make_unique(std::forward(args)...); + auto object = std::make_shared(std::forward(args)...); return add(object); } @@ -231,14 +233,14 @@ namespace rsx } // Returns current list for reading. Caller must ensure synchronization by first locking the list - const std::vector>& get_views() const + const std::vector>& get_views() const { return m_iface_list; } // Returns current list of removed objects not yet deallocated for reading. // Caller must ensure synchronization by first locking the list - const std::vector>& get_dirty() const + const std::vector>& get_dirty() const { return m_dirty_list; } @@ -255,7 +257,7 @@ namespace rsx m_dirty_list.erase ( - std::remove_if(m_dirty_list.begin(), m_dirty_list.end(), [&uids](std::unique_ptr& e) + std::remove_if(m_dirty_list.begin(), m_dirty_list.end(), [&uids](std::shared_ptr& e) { return std::find(uids.begin(), uids.end(), e->uid) != uids.end(); }), @@ -264,22 +266,22 @@ namespace rsx } // Returns pointer to the object matching the given uid - overlay* get(u32 uid) + std::shared_ptr get(u32 uid) { reader_lock lock(m_list_mutex); for (const auto& iface : m_iface_list) { if (iface->uid == uid) - return iface.get(); + return iface; } - return nullptr; + return {}; } // Returns pointer to the first object matching the given type template - T* get() + std::shared_ptr get() { reader_lock lock(m_list_mutex); @@ -288,11 +290,11 @@ namespace rsx { if (iface->type_index == type_id) { - return static_cast(iface.get()); + return std::static_pointer_cast(iface); } } - return nullptr; + return {}; } // Lock for read-only access (BasicLockable) @@ -376,6 +378,97 @@ namespace rsx } }; + struct osk_dialog : public user_interface, public OskDialogBase + { + using callback_t = std::function; + + enum border_flags + { + top = 1, + bottom = 2, + left = 4, + right = 8, + + start_cell = top | bottom | left, + end_cell = top | bottom | right, + middle_cell = top | bottom, + default_cell = top | bottom | left | right + }; + + struct cell + { + position2u pos; + color4f backcolor{}; + border_flags flags = default_cell; + bool selected = false; + + std::vector outputs; + callback_t callback; + }; + + struct grid_entry_ctor + { + std::vector outputs; + color4f color; + u32 num_cell_hz; + callback_t callback; + }; + + // Base UI + overlay_element m_frame; + overlay_element m_background; + label m_title; + label m_preview; + image_button m_btn_accept; + image_button m_btn_cancel; + image_button m_btn_shift; + + // Grid + u32 cell_size_x = 0; + u32 cell_size_y = 0; + u32 num_columns = 0; + u32 num_rows = 0; + u32 num_layers = 0; + u32 selected_x = 0; + u32 selected_y = 0; + u32 selected_z = 0; + + std::vector m_grid; + + bool m_visible = false; + bool m_update = true; + compiled_resource m_cached_resource; + + u32 flags = 0; + u32 char_limit = UINT32_MAX; + + osk_dialog() {} + virtual ~osk_dialog() {} + + void Create(const std::string& title, const std::u16string& message, char16_t* init_text, u32 charlimit, u32 options) override = 0; + void Close(bool accepted) override; + + void initialize_layout(const std::vector& layout, const std::string& title, const std::string& initial_text); + + void on_button_pressed(pad_button button_press) override; + void on_text_changed(); + + void on_default_callback(const std::string&); + void on_shift(const std::string&); + void on_space(const std::string&); + void on_backspace(const std::string&); + void on_enter(const std::string&); + + compiled_resource get_compiled() override; + }; + + struct osk_enUS : osk_dialog + { + using osk_dialog::osk_dialog; + + void Create(const std::string& title, const std::u16string& message, char16_t* init_text, u32 charlimit, u32 options); + }; + struct save_dialog : public user_interface { private: diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index d27fe3814f..a7e1a8c34f 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -1912,7 +1912,7 @@ void VKGSRender::on_init_thread() struct native_helper : vk::shader_cache::progress_dialog_helper { rsx::thread *owner = nullptr; - rsx::overlays::message_dialog *dlg = nullptr; + std::shared_ptr dlg; native_helper(VKGSRender *ptr) : owner(ptr) {} diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 5e58a1a0bd..1a8b77a325 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -295,6 +295,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index d64cefec91..0f35977044 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -752,6 +752,9 @@ Emu\GPU\RSX + + Emu\GPU\RSX\Overlays + diff --git a/rpcs3/rpcs3qt/osk_dialog_frame.cpp b/rpcs3/rpcs3qt/osk_dialog_frame.cpp index 70a81d420b..6918d71701 100644 --- a/rpcs3/rpcs3qt/osk_dialog_frame.cpp +++ b/rpcs3/rpcs3qt/osk_dialog_frame.cpp @@ -1,4 +1,4 @@ - + #include "osk_dialog_frame.h" #include "Emu/Cell/Modules/cellMsgDialog.h" @@ -165,12 +165,12 @@ void osk_dialog_frame::Create(const std::string& title, const std::u16string& me connect(m_dialog, &QDialog::accepted, [=] { - on_close(CELL_MSGDIALOG_BUTTON_OK); + on_osk_close(CELL_MSGDIALOG_BUTTON_OK); }); connect(m_dialog, &QDialog::rejected, [=] { - on_close(CELL_MSGDIALOG_BUTTON_ESCAPE); + on_osk_close(CELL_MSGDIALOG_BUTTON_ESCAPE); }); // Fix size