mirror of https://github.com/RPCS3/rpcs3.git
overlays/osk: Implement native osk interface
This commit is contained in:
parent
9d4b19b97a
commit
9ed9d7e947
|
@ -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<OskDialogBase> _get_osk_dialog(bool always = false)
|
||||
{
|
||||
if (auto manager = fxm::get<rsx::overlays::display_manager>())
|
||||
{
|
||||
// NOTE: Osk lifetime is managed by fxm, not display manager
|
||||
// Visibility is still managed by display manager
|
||||
auto osk = fxm::get<rsx::overlays::osk_enUS>();
|
||||
if (always)
|
||||
{
|
||||
if (!osk)
|
||||
{
|
||||
// Create if does not exist
|
||||
osk = fxm::make<rsx::overlays::osk_enUS>();
|
||||
}
|
||||
|
||||
// Attach to display manager forcefully
|
||||
osk = manager->add(osk);
|
||||
}
|
||||
|
||||
return osk;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto osk = fxm::get<OskDialogBase>();
|
||||
if (always && !osk)
|
||||
{
|
||||
return fxm::import<OskDialogBase>(Emu.GetCallbacks().get_osk_dialog);
|
||||
}
|
||||
|
||||
return osk;
|
||||
}
|
||||
}
|
||||
|
||||
error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dialogParam, vm::ptr<CellOskDialogInputFieldInfo> 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<CellOskDialogParam> dia
|
|||
return CELL_OSKDIALOG_ERROR_PARAM;
|
||||
}
|
||||
|
||||
auto osk = fxm::get<OskDialogBase>();
|
||||
if (!osk)
|
||||
{
|
||||
osk = fxm::import<OskDialogBase>(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<CellOskDialogParam> dia
|
|||
|
||||
bool result = false;
|
||||
|
||||
osk->on_close = [maxLength, wptr = std::weak_ptr<OskDialogBase>(osk)](s32 status)
|
||||
osk->on_osk_close = [maxLength, wptr = std::weak_ptr<OskDialogBase>(osk)](s32 status)
|
||||
{
|
||||
const auto osk = wptr.lock();
|
||||
|
||||
|
@ -216,7 +246,7 @@ error_code getText(vm::ptr<CellOskDialogCallbackReturnParam> OutputInfo, bool is
|
|||
return CELL_OSKDIALOG_ERROR_PARAM;
|
||||
}
|
||||
|
||||
const auto osk = fxm::get<OskDialogBase>();
|
||||
const auto osk = _get_osk_dialog();
|
||||
|
||||
if (!osk)
|
||||
{
|
||||
|
@ -306,7 +336,7 @@ error_code cellOskDialogAbort()
|
|||
{
|
||||
cellOskDialog.warning("cellOskDialogAbort()");
|
||||
|
||||
const auto osk = fxm::get<OskDialogBase>();
|
||||
const auto osk = _get_osk_dialog();
|
||||
|
||||
if (!osk)
|
||||
{
|
||||
|
@ -358,12 +388,7 @@ error_code cellOskDialogSetSeparateWindowOption(vm::ptr<CellOskDialogSeparateWin
|
|||
return CELL_OSKDIALOG_ERROR_PARAM;
|
||||
}
|
||||
|
||||
auto osk = fxm::get<OskDialogBase>();
|
||||
if (!osk)
|
||||
{
|
||||
osk = fxm::import<OskDialogBase>(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<OskDialogBase>();
|
||||
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<cellOskDial
|
|||
return CELL_OSKDIALOG_ERROR_PARAM;
|
||||
}
|
||||
|
||||
auto osk = fxm::get<OskDialogBase>();
|
||||
if (!osk)
|
||||
{
|
||||
osk = fxm::import<OskDialogBase>(Emu.GetCallbacks().get_osk_dialog);
|
||||
}
|
||||
auto osk = _get_osk_dialog(true);
|
||||
osk->osk_confirm_callback = pCallback;
|
||||
|
||||
return CELL_OK;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#pragma once
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
|
@ -247,7 +247,7 @@ public:
|
|||
virtual void Close(bool accepted) = 0;
|
||||
virtual ~OskDialogBase();
|
||||
|
||||
std::function<void(s32 status)> on_close;
|
||||
std::function<void(s32 status)> on_osk_close;
|
||||
std::function<void()> on_osk_input_entered;
|
||||
|
||||
atomic_t<OskDialogState> state{ OskDialogState::Close };
|
||||
|
|
|
@ -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<rsx::overlays::message_dialog> dlg;
|
||||
|
||||
native_helper(GLGSRender *ptr) :
|
||||
owner(ptr) {}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<grid_entry_ctor>& 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<u32>(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<u32>(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<osk_dialog::grid_entry_ctor> 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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,41 @@
|
|||
#include "overlays.h"
|
||||
#include "../GSRender.h"
|
||||
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
|
||||
#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<std::codecvt_utf8_utf16<int16_t>, int16_t> convert;
|
||||
auto p = reinterpret_cast<const int16_t *>(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<std::codecvt_utf8_utf16<int16_t>, int16_t> convert;
|
||||
auto ws = convert.from_bytes(utf8_string);
|
||||
return reinterpret_cast<const char16_t*>(ws.c_str());
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
std::string utf16_to_utf8(const std::u16string& utf16_string)
|
||||
{
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||
return convert.to_bytes(utf16_string);
|
||||
}
|
||||
|
||||
std::u16string utf8_to_utf16(const std::string& utf8_string)
|
||||
{
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||
return convert.from_bytes(utf8_string);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
namespace overlays
|
||||
|
|
|
@ -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<u32> m_uid_ctr { 0u };
|
||||
std::vector<std::unique_ptr<overlay>> m_iface_list;
|
||||
std::vector<std::unique_ptr<overlay>> m_dirty_list;
|
||||
std::vector<std::shared_ptr<overlay>> m_iface_list;
|
||||
std::vector<std::shared_ptr<overlay>> m_dirty_list;
|
||||
|
||||
shared_mutex m_list_mutex;
|
||||
std::vector<u32> 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 <typename T>
|
||||
T* add(std::unique_ptr<T>& entry, bool remove_existing = true)
|
||||
std::shared_ptr<T> add(std::shared_ptr<T>& 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<T>();
|
||||
entry->uid = m_uid_ctr.fetch_add(1);
|
||||
entry->type_index = id_manager::typeinfo::get_index<T>();
|
||||
|
||||
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<T>(*It);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_iface_list.push_back(std::move(entry));
|
||||
return e;
|
||||
return std::static_pointer_cast<T>(m_iface_list.back());
|
||||
}
|
||||
|
||||
// Allocates object and adds to internal list. Returns pointer to created object
|
||||
template <typename T, typename ...Args>
|
||||
T* create(Args&&... args)
|
||||
std::shared_ptr<T> create(Args&&... args)
|
||||
{
|
||||
std::unique_ptr<T> object = std::make_unique<T>(std::forward<Args>(args)...);
|
||||
auto object = std::make_shared<T>(std::forward<Args>(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<std::unique_ptr<overlay>>& get_views() const
|
||||
const std::vector<std::shared_ptr<overlay>>& 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<std::unique_ptr<overlay>>& get_dirty() const
|
||||
const std::vector<std::shared_ptr<overlay>>& 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<overlay>& e)
|
||||
std::remove_if(m_dirty_list.begin(), m_dirty_list.end(), [&uids](std::shared_ptr<overlay>& 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<overlay> 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 <typename T>
|
||||
T* get()
|
||||
std::shared_ptr<T> get()
|
||||
{
|
||||
reader_lock lock(m_list_mutex);
|
||||
|
||||
|
@ -288,11 +290,11 @@ namespace rsx
|
|||
{
|
||||
if (iface->type_index == type_id)
|
||||
{
|
||||
return static_cast<T*>(iface.get());
|
||||
return std::static_pointer_cast<T>(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<void(const std::string&)>;
|
||||
|
||||
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<std::string> outputs;
|
||||
callback_t callback;
|
||||
};
|
||||
|
||||
struct grid_entry_ctor
|
||||
{
|
||||
std::vector<std::string> 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<cell> 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<grid_entry_ctor>& 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:
|
||||
|
|
|
@ -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<rsx::overlays::message_dialog> dlg;
|
||||
|
||||
native_helper(VKGSRender *ptr) :
|
||||
owner(ptr) {}
|
||||
|
|
|
@ -295,6 +295,7 @@
|
|||
</ClCompile>
|
||||
<ClCompile Include="Emu\RSX\Null\NullGSRender.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Overlays\overlays.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Overlays\overlay_osk.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Overlays\overlay_perf_metrics.cpp" />
|
||||
<ClCompile Include="Emu\RSX\RSXFIFO.cpp" />
|
||||
<ClCompile Include="Emu\RSX\rsx_methods.cpp" />
|
||||
|
|
|
@ -752,6 +752,9 @@
|
|||
<ClCompile Include="Emu\RSX\RSXFIFO.cpp">
|
||||
<Filter>Emu\GPU\RSX</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\RSX\Overlays\overlay_osk.cpp">
|
||||
<Filter>Emu\GPU\RSX\Overlays</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Crypto\aes.h">
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue