rpcs3/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp
Megamouse 3183d73e4d OSK/overlays: fix initial input interception
Don't use default interception if we already intercept with custom params.
2022-04-26 00:51:38 +02:00

1221 lines
35 KiB
C++

#include "stdafx.h"
#include "overlay_osk.h"
#include "Emu/RSX/RSXThread.h"
#include "Emu/Cell/Modules/cellSysutil.h"
#include "Emu/Cell/Modules/cellMsgDialog.h"
#include "Emu/Cell/Modules/cellKb.h"
LOG_CHANNEL(osk, "OSK");
namespace rsx
{
namespace overlays
{
osk_dialog::osk_dialog()
{
m_auto_repeat_buttons.insert(pad_button::L1);
m_auto_repeat_buttons.insert(pad_button::R1);
m_auto_repeat_buttons.insert(pad_button::cross);
m_auto_repeat_buttons.insert(pad_button::triangle);
m_auto_repeat_buttons.insert(pad_button::square);
m_keyboard_input_enabled = true;
}
void osk_dialog::Close(s32 status)
{
fade_animation.current = color4f(1.f);
fade_animation.end = color4f(0.f);
fade_animation.duration = 0.5f;
fade_animation.on_finish = [this, status]
{
if (on_osk_close)
{
Emu.CallFromMainThread([this, status]()
{
on_osk_close(status);
});
}
visible = false;
close(true, true);
};
fade_animation.active = true;
}
void osk_dialog::add_panel(const osk_panel& panel)
{
// On PS3 apparently only 7 panels are added, the rest is ignored
if (m_panels.size() < 7)
{
// Don't add this panel if there already exists one with the same panel mode
if (std::none_of(m_panels.begin(), m_panels.end(), [&panel](const osk_panel& existing) { return existing.osk_panel_mode == panel.osk_panel_mode; }))
{
m_panels.push_back(panel);
}
}
}
void osk_dialog::step_panel(bool next_panel)
{
const usz num_panels = m_panels.size();
if (num_panels > 0)
{
if (next_panel)
{
m_panel_index = (m_panel_index + 1) % num_panels;
}
else if (m_panel_index > 0)
{
m_panel_index = (m_panel_index - 1) % num_panels;
}
else
{
m_panel_index = num_panels - 1;
}
}
update_panel();
}
void osk_dialog::update_panel()
{
ensure(m_panel_index < m_panels.size());
const auto& panel = m_panels[m_panel_index];
num_rows = panel.num_rows;
num_columns = panel.num_columns;
cell_size_x = panel.cell_size_x;
cell_size_y = panel.cell_size_y;
update_layout();
const u32 cell_count = num_rows * num_columns;
m_grid.resize(cell_count);
num_shift_layers_by_charset.clear();
const position2u grid_origin = { m_frame.x, m_frame.y + 30u + m_preview.h };
const u32 old_index = (selected_y * num_columns) + selected_x;
u32 index = 0;
for (const auto& props : panel.layout)
{
for (u32 c = 0; c < props.num_cell_hz; ++c)
{
const auto row = (index / num_columns);
const auto col = (index % num_columns);
ensure(row < num_rows && col < num_columns);
auto& _cell = m_grid[index++];
_cell.button_flag = props.type_flags;
_cell.pos = { grid_origin.x + col * cell_size_x, grid_origin.y + row * cell_size_y };
_cell.backcolor = props.color;
_cell.callback = props.callback;
_cell.outputs = props.outputs;
_cell.selected = false;
// Add shift layers
for (u32 layer = 0; layer < _cell.outputs.size(); ++layer)
{
// Only add a shift layer if at least one default button has content in a layer
if (props.type_flags != button_flags::_default)
{
continue;
}
usz cell_shift_layers = 0;
for (usz i = 0; i < _cell.outputs[layer].size(); ++i)
{
if (_cell.outputs[layer][i].empty() == false)
{
cell_shift_layers = i + 1;
}
}
if (layer >= num_shift_layers_by_charset.size())
{
num_shift_layers_by_charset.push_back(static_cast<u32>(cell_shift_layers));
}
else
{
num_shift_layers_by_charset[layer] = std::max(num_shift_layers_by_charset[layer], static_cast<u32>(cell_shift_layers));
}
}
switch (props.type_flags)
{
default:
case button_flags::_default:
_cell.enabled = true;
break;
case button_flags::_space:
_cell.enabled = !(flags & CELL_OSKDIALOG_NO_SPACE);
break;
case button_flags::_return:
_cell.enabled = !(flags & CELL_OSKDIALOG_NO_RETURN);
break;
case button_flags::_shift:
_cell.enabled |= !_cell.outputs.empty();
break;
case button_flags::_layer:
_cell.enabled |= !num_shift_layers_by_charset.empty();
break;
}
if (props.num_cell_hz == 1) [[likely]]
{
_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;
}
}
}
ensure(num_shift_layers_by_charset.size());
for (u32 layer = 0; layer < num_shift_layers_by_charset.size(); ++layer)
{
ensure(num_shift_layers_by_charset[layer]);
}
// Reset to first shift layer in the first charset, because the panel changed and we don't know if the layers are similar between panels.
m_selected_charset = 0;
selected_z = 0;
// Enable/Disable the control buttons based on the current layout.
update_controls();
// Roughly keep x and y selection in grid if possible. Jumping to (0,0) would be annoying. Needs to be done after updating the control buttons.
update_selection_by_index(old_index);
m_update = true;
}
void osk_dialog::update_layout()
{
const u16 preview_height = (flags & CELL_OSKDIALOG_NO_RETURN) ? 40 : 90;
// Place elements with absolute positioning
const u16 frame_w = static_cast<u16>(num_columns * cell_size_x);
const u16 frame_h = static_cast<u16>(num_rows * cell_size_y) + 30 + preview_height;
const u16 frame_x = (1280 - frame_w) / 2;
const u16 frame_y = (720 - frame_h) / 2;
m_frame.set_pos(frame_x, frame_y);
m_frame.set_size(frame_w, frame_h);
m_title.set_pos(frame_x, frame_y);
m_title.set_size(frame_w, 30);
m_title.set_padding(15, 0, 5, 0);
m_preview.set_pos(frame_x, frame_y + 30);
m_preview.set_size(frame_w, preview_height);
m_preview.set_padding(15, 0, 10, 0);
m_btn_cancel.set_pos(frame_x, frame_y + frame_h + 10);
m_btn_cancel.set_size(140, 30);
m_btn_cancel.set_text(localized_string_id::RSX_OVERLAYS_OSK_DIALOG_CANCEL);
m_btn_cancel.set_text_vertical_adjust(5);
m_btn_space.set_pos(frame_x + 100, frame_y + frame_h + 10);
m_btn_space.set_size(100, 30);
m_btn_space.set_text(localized_string_id::RSX_OVERLAYS_OSK_DIALOG_SPACE);
m_btn_space.set_text_vertical_adjust(5);
m_btn_delete.set_pos(frame_x + 200, frame_y + frame_h + 10);
m_btn_delete.set_size(100, 30);
m_btn_delete.set_text(localized_string_id::RSX_OVERLAYS_OSK_DIALOG_BACKSPACE);
m_btn_delete.set_text_vertical_adjust(5);
m_btn_shift.set_pos(frame_x + 320, frame_y + frame_h + 10);
m_btn_shift.set_size(80, 30);
m_btn_shift.set_text(localized_string_id::RSX_OVERLAYS_OSK_DIALOG_SHIFT);
m_btn_shift.set_text_vertical_adjust(5);
m_btn_accept.set_pos(frame_x + 400, frame_y + frame_h + 10);
m_btn_accept.set_size(100, 30);
m_btn_accept.set_text(localized_string_id::RSX_OVERLAYS_OSK_DIALOG_ACCEPT);
m_btn_accept.set_text_vertical_adjust(5);
m_update = true;
}
void osk_dialog::initialize_layout(const std::u32string& title, const std::u32string& initial_text)
{
m_background.set_size(1280, 720);
m_title.set_unicode_text(title);
m_title.back_color.a = 0.7f; // Uses the dimmed color of the frame background
m_preview.password_mode = m_password_mode;
m_preview.set_placeholder(get_placeholder());
m_preview.set_unicode_text(initial_text);
if (m_preview.value.empty())
{
m_preview.caret_position = 0;
m_preview.fore_color.a = 0.5f; // Muted contrast for hint text
}
else
{
m_preview.caret_position = m_preview.value.length();
m_preview.fore_color.a = 1.f;
}
m_btn_shift.set_image_resource(resource_config::standard_image_resource::select);
m_btn_accept.set_image_resource(resource_config::standard_image_resource::start);
m_btn_space.set_image_resource(resource_config::standard_image_resource::triangle);
m_btn_delete.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_update = true;
visible = true;
exit = false;
fade_animation.current = color4f(0.f);
fade_animation.end = color4f(1.f);
fade_animation.duration = 0.5f;
fade_animation.active = true;
}
void osk_dialog::update_controls()
{
const bool shift_enabled = num_shift_layers_by_charset[m_selected_charset] > 1;
const bool layer_enabled = num_shift_layers_by_charset.size() > 1;
for (auto& cell : m_grid)
{
switch (cell.button_flag)
{
case button_flags::_shift:
cell.enabled = shift_enabled;
break;
case button_flags::_layer:
cell.enabled = layer_enabled;
break;
default:
break;
}
}
m_update = true;
}
std::pair<u32, u32> osk_dialog::get_cell_geometry(u32 index)
{
const auto index_limit = (num_columns * num_rows) - 1;
u32 start_index = index;
u32 count = 0;
while (start_index > index_limit && start_index >= num_columns)
{
// Try one row above
start_index -= num_columns;
}
// 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);
ensure(current_index <= index_limit);
++count;
if (m_grid[current_index].flags & border_flags::right)
{
break;
}
}
return std::make_pair(start_index, count);
}
void osk_dialog::update_selection_by_index(u32 index)
{
auto select_cell = [&](u32 i, bool state)
{
const auto info = get_cell_geometry(i);
// Tag all in range
for (u32 _index = info.first, _ctr = 0; _ctr < info.second; ++_index, ++_ctr)
{
m_grid[_index].selected = state;
}
};
// 1. Deselect current
const 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);
}
void osk_dialog::on_button_pressed(pad_button button_press)
{
if (!pad_input_enabled || ignore_input_events)
return;
const auto index_limit = (num_columns * num_rows) - 1;
const auto on_accept = [this]()
{
const u32 current_index = (selected_y * num_columns) + selected_x;
const auto& current_cell = m_grid[current_index];
u32 output_count = 0;
if (m_selected_charset < current_cell.outputs.size())
{
output_count = ::size32(current_cell.outputs[m_selected_charset]);
}
if (output_count)
{
const auto _z = std::clamp<u32>(selected_z, 0u, output_count - 1u);
const auto& str = current_cell.outputs[m_selected_charset][_z];
if (current_cell.callback)
{
current_cell.callback(str);
}
else
{
on_default_callback(str);
}
}
};
bool play_cursor_sound = true;
switch (button_press)
{
case pad_button::L1:
{
m_preview.move_caret(edit_text::direction::left);
m_update = true;
break;
}
case pad_button::R1:
{
m_preview.move_caret(edit_text::direction::right);
m_update = true;
break;
}
case pad_button::dpad_right:
case pad_button::ls_right:
{
u32 current_index = (selected_y * num_columns) + selected_x;
while (true)
{
const auto current = get_cell_geometry(current_index);
current_index = current.first + current.second;
if (current_index > index_limit)
{
break;
}
if (m_grid[get_cell_geometry(current_index).first].enabled)
{
update_selection_by_index(current_index);
break;
}
}
m_reset_pulse = true;
break;
}
case pad_button::dpad_left:
case pad_button::ls_left:
{
u32 current_index = (selected_y * num_columns) + selected_x;
while (current_index > 0)
{
const auto current = get_cell_geometry(current_index);
if (current.first)
{
current_index = current.first - 1;
if (m_grid[get_cell_geometry(current_index).first].enabled)
{
update_selection_by_index(current_index);
break;
}
}
else
{
break;
}
}
m_reset_pulse = true;
break;
}
case pad_button::dpad_down:
case pad_button::ls_down:
{
u32 current_index = (selected_y * num_columns) + selected_x;
while (true)
{
current_index += num_columns;
if (current_index > index_limit)
{
break;
}
if (m_grid[get_cell_geometry(current_index).first].enabled)
{
update_selection_by_index(current_index);
break;
}
}
m_reset_pulse = true;
break;
}
case pad_button::dpad_up:
case pad_button::ls_up:
{
u32 current_index = (selected_y * num_columns) + selected_x;
while (current_index >= num_columns)
{
current_index -= num_columns;
if (m_grid[get_cell_geometry(current_index).first].enabled)
{
update_selection_by_index(current_index);
break;
}
}
m_reset_pulse = true;
break;
}
case pad_button::select:
{
on_shift(U"");
break;
}
case pad_button::start:
{
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_oskenter.wav");
Close(CELL_OSKDIALOG_CLOSE_CONFIRM);
play_cursor_sound = false;
break;
}
case pad_button::triangle:
{
on_space(U"");
break;
}
case pad_button::square:
{
on_backspace(U"");
break;
}
case pad_button::cross:
{
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_oskenter.wav");
on_accept();
m_reset_pulse = true;
play_cursor_sound = false;
break;
}
case pad_button::circle:
{
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_oskcancel.wav");
Close(CELL_OSKDIALOG_CLOSE_CANCEL);
play_cursor_sound = false;
break;
}
case pad_button::L2:
{
step_panel(false);
break;
}
case pad_button::R2:
{
step_panel(true);
break;
}
default:
break;
}
if (play_cursor_sound)
{
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cursor.wav");
}
if (m_reset_pulse)
{
m_update = true;
}
}
void osk_dialog::on_key_pressed(u32 led, u32 mkey, u32 key_code, u32 out_key_code, bool pressed)
{
if (!pressed || !keyboard_input_enabled || ignore_input_events)
return;
osk.notice("osk_dialog::on_key_pressed(led=%d, mkey=%d, key_code=%d, out_key_code=%d, pressed=%d)", led, mkey, key_code, out_key_code, pressed);
// Get keyboard layout
u32 kb_mapping = CELL_KB_MAPPING_101;
u32 osk_panel_mode = CELL_OSKDIALOG_PANELMODE_DEFAULT;
for (usz i = 0; i < m_panels.size(); ++i)
{
if (m_panel_index == i)
{
osk_panel_mode = m_panels[i].osk_panel_mode;
break;
}
}
switch (osk_panel_mode)
{
case CELL_OSKDIALOG_PANELMODE_DEFAULT : kb_mapping = CELL_KB_MAPPING_101; break;
case CELL_OSKDIALOG_PANELMODE_GERMAN : kb_mapping = CELL_KB_MAPPING_GERMAN_GERMANY; break;
case CELL_OSKDIALOG_PANELMODE_ENGLISH : kb_mapping = CELL_KB_MAPPING_ENGLISH_UK; break;
case CELL_OSKDIALOG_PANELMODE_SPANISH : kb_mapping = CELL_KB_MAPPING_SPANISH_SPAIN; break;
case CELL_OSKDIALOG_PANELMODE_FRENCH : kb_mapping = CELL_KB_MAPPING_FRENCH_FRANCE; break;
case CELL_OSKDIALOG_PANELMODE_ITALIAN : kb_mapping = CELL_KB_MAPPING_ITALIAN_ITALY; break;
case CELL_OSKDIALOG_PANELMODE_DUTCH : kb_mapping = CELL_KB_MAPPING_DUTCH_NETHERLANDS; break;
case CELL_OSKDIALOG_PANELMODE_PORTUGUESE : kb_mapping = CELL_KB_MAPPING_PORTUGUESE_PORTUGAL; break;
case CELL_OSKDIALOG_PANELMODE_RUSSIAN : kb_mapping = CELL_KB_MAPPING_RUSSIAN_RUSSIA; break;
case CELL_OSKDIALOG_PANELMODE_JAPANESE : kb_mapping = CELL_KB_MAPPING_106; break;
case CELL_OSKDIALOG_PANELMODE_DEFAULT_NO_JAPANESE: kb_mapping = CELL_KB_MAPPING_106; break;
case CELL_OSKDIALOG_PANELMODE_POLISH : kb_mapping = CELL_KB_MAPPING_POLISH_POLAND; break;
case CELL_OSKDIALOG_PANELMODE_KOREAN : kb_mapping = CELL_KB_MAPPING_KOREAN_KOREA; break;
case CELL_OSKDIALOG_PANELMODE_TURKEY : kb_mapping = CELL_KB_MAPPING_TURKISH_TURKEY; break;
case CELL_OSKDIALOG_PANELMODE_TRADITIONAL_CHINESE: kb_mapping = CELL_KB_MAPPING_CHINESE_TRADITIONAL; break;
case CELL_OSKDIALOG_PANELMODE_SIMPLIFIED_CHINESE : kb_mapping = CELL_KB_MAPPING_CHINESE_SIMPLIFIED; break;
case CELL_OSKDIALOG_PANELMODE_PORTUGUESE_BRAZIL : kb_mapping = CELL_KB_MAPPING_PORTUGUESE_BRAZIL; break;
case CELL_OSKDIALOG_PANELMODE_DANISH : kb_mapping = CELL_KB_MAPPING_DANISH_DENMARK; break;
case CELL_OSKDIALOG_PANELMODE_SWEDISH : kb_mapping = CELL_KB_MAPPING_SWEDISH_SWEDEN; break;
case CELL_OSKDIALOG_PANELMODE_NORWEGIAN : kb_mapping = CELL_KB_MAPPING_NORWEGIAN_NORWAY; break;
case CELL_OSKDIALOG_PANELMODE_FINNISH : kb_mapping = CELL_KB_MAPPING_FINNISH_FINLAND; break;
case CELL_OSKDIALOG_PANELMODE_JAPANESE_HIRAGANA : kb_mapping = CELL_KB_MAPPING_106; break;
case CELL_OSKDIALOG_PANELMODE_JAPANESE_KATAKANA : kb_mapping = CELL_KB_MAPPING_106_KANA; break;
case CELL_OSKDIALOG_PANELMODE_ALPHABET_FULL_WIDTH: kb_mapping = CELL_KB_MAPPING_106; break;
case CELL_OSKDIALOG_PANELMODE_ALPHABET : kb_mapping = CELL_KB_MAPPING_101; break;
case CELL_OSKDIALOG_PANELMODE_LATIN : kb_mapping = CELL_KB_MAPPING_101; break;
case CELL_OSKDIALOG_PANELMODE_NUMERAL_FULL_WIDTH : kb_mapping = CELL_KB_MAPPING_101; break;
case CELL_OSKDIALOG_PANELMODE_NUMERAL : kb_mapping = CELL_KB_MAPPING_101; break;
case CELL_OSKDIALOG_PANELMODE_URL : kb_mapping = CELL_KB_MAPPING_101; break;
case CELL_OSKDIALOG_PANELMODE_PASSWORD : kb_mapping = CELL_KB_MAPPING_101; break;
default : kb_mapping = CELL_KB_MAPPING_101; break;
}
// Convert key to its u32string presentation
const u16 converted_out_key = cellKbCnvRawCode(kb_mapping, mkey, led, out_key_code);
std::u16string utf16_string;
utf16_string.push_back(converted_out_key);
const std::u32string u32_string = utf16_to_u32string(utf16_string);
const std::string out_key_string = utf16_to_ascii8(utf16_string);
// Find matching key in the OSK
bool found_key = false;
for (const cell& current_cell : m_grid)
{
// TODO: maybe just ignore the current charset and check all outputs
if (m_selected_charset < current_cell.outputs.size())
{
for (const auto& str : current_cell.outputs[m_selected_charset])
{
if (str == u32_string)
{
// Apply key press
if (current_cell.callback)
{
current_cell.callback(str);
}
else
{
on_default_callback(str);
}
found_key = true;
break;
}
}
if (found_key)
{
break;
}
}
}
// Handle special input
if (!found_key)
{
switch (out_key_code)
{
case CELL_KEYC_SPACE:
on_space(u32_string);
break;
case CELL_KEYC_BS:
on_backspace(u32_string);
break;
case CELL_KEYC_ESCAPE:
Close(CELL_OSKDIALOG_CLOSE_CANCEL);
break;
case CELL_KEYC_ENTER:
if ((flags & CELL_OSKDIALOG_NO_RETURN))
{
Close(CELL_OSKDIALOG_CLOSE_CONFIRM);
}
else
{
on_enter(u32_string);
}
break;
default:
break;
}
}
if (on_osk_key_input_entered)
{
CellOskDialogKeyMessage key_message{};
key_message.led = led;
key_message.mkey = mkey;
key_message.keycode = key_code;
on_osk_key_input_entered(key_message);
}
}
void osk_dialog::on_text_changed()
{
const auto ws = u32string_to_utf16(m_preview.value);
const auto length = (ws.length() + 1) * sizeof(char16_t);
memcpy(osk_text, ws.c_str(), length);
// Muted contrast for placeholder text
m_preview.fore_color.a = m_preview.value.empty() ? 0.5f : 1.f;
m_update = true;
}
void osk_dialog::on_default_callback(const std::u32string& str)
{
if (str.empty())
{
return;
}
// Append to output text
if (m_preview.value.empty())
{
m_preview.caret_position = str.length();
m_preview.set_unicode_text(str);
}
else
{
if (m_preview.value.length() == char_limit)
{
return;
}
const auto new_str = m_preview.value + str;
if (new_str.length() <= char_limit)
{
m_preview.insert_text(str);
}
}
on_text_changed();
}
void osk_dialog::on_shift(const std::u32string&)
{
const u32 max = num_shift_layers_by_charset[m_selected_charset];
selected_z = (selected_z + 1) % max;
m_update = true;
}
void osk_dialog::on_layer(const std::u32string&)
{
const u32 num_charsets = std::max<u32>(::size32(num_shift_layers_by_charset), 1);
m_selected_charset = (m_selected_charset + 1) % num_charsets;
const u32 max_z_layer = num_shift_layers_by_charset[m_selected_charset] - 1;
if (selected_z > max_z_layer)
{
selected_z = max_z_layer;
}
update_controls();
m_update = true;
}
void osk_dialog::on_space(const std::u32string&)
{
if (!(flags & CELL_OSKDIALOG_NO_SPACE))
{
on_default_callback(U" ");
}
else
{
// Beep or give some other kind of visual feedback
}
}
void osk_dialog::on_backspace(const std::u32string&)
{
m_preview.erase();
on_text_changed();
}
void osk_dialog::on_enter(const std::u32string&)
{
if (!(flags & CELL_OSKDIALOG_NO_RETURN))
{
on_default_callback(U"\n");
}
else
{
// Beep or give some other kind of visual feedback
}
}
std::u32string osk_dialog::get_placeholder() const
{
const localized_string_id id = m_password_mode
? localized_string_id::RSX_OVERLAYS_OSK_DIALOG_ENTER_PASSWORD
: localized_string_id::RSX_OVERLAYS_OSK_DIALOG_ENTER_TEXT;
return get_localized_u32string(id);
}
void osk_dialog::update()
{
if (fade_animation.active)
{
fade_animation.update(rsx::get_current_renderer()->vblank_count);
m_update = true;
}
}
compiled_resource osk_dialog::get_compiled()
{
if (!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());
m_cached_resource.add(m_btn_space.get_compiled());
m_cached_resource.add(m_btn_delete.get_compiled());
overlay_element tmp;
label m_label;
u16 buffered_cell_count = 0;
bool render_label = false;
const color4f disabled_back_color = { 0.3f, 0.3f, 0.3f, 1.f };
const color4f disabled_fore_color = { 0.8f, 0.8f, 0.8f, 1.f };
const color4f normal_fore_color = { 0.f, 0.f, 0.f, 1.f };
m_label.back_color = { 0.f, 0.f, 0.f, 0.f };
m_label.set_padding(0, 0, 10, 0);
if (m_reset_pulse)
{
// Reset the pulse slightly above 0 falling on each user interaction
m_key_pulse_cache.set_sinus_offset(0.6f);
}
for (const auto& c : m_grid)
{
u16 x = static_cast<u16>(c.pos.x);
u16 y = static_cast<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--;
u32 output_count = 0;
if (m_selected_charset < c.outputs.size())
{
output_count = ::size32(c.outputs[m_selected_charset]);
}
if (output_count)
{
const u16 offset_x = static_cast<u16>(buffered_cell_count * cell_size_x);
const u16 full_width = static_cast<u16>(offset_x + cell_size_x);
m_label.set_pos(x - offset_x, y);
m_label.set_size(full_width, cell_size_y);
m_label.fore_color = c.enabled ? normal_fore_color : disabled_fore_color;
const auto _z = (selected_z < output_count) ? selected_z : output_count - 1u;
m_label.set_unicode_text(c.outputs[m_selected_charset][_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.enabled? c.backcolor : disabled_back_color;
tmp.set_pos(x, y);
tmp.set_size(w, h);
tmp.pulse_effect_enabled = c.selected;
tmp.pulse_sinus_offset = m_key_pulse_cache.pulse_sinus_offset;
m_cached_resource.add(tmp.get_compiled());
if (render_label)
{
m_label.pulse_effect_enabled = c.selected;
m_label.pulse_sinus_offset = m_key_pulse_cache.pulse_sinus_offset;
m_cached_resource.add(m_label.get_compiled());
}
}
m_reset_pulse = false;
m_update = false;
}
fade_animation.apply(m_cached_resource);
return m_cached_resource;
}
struct osk_dialog_thread
{
static constexpr auto thread_name = "OSK Thread"sv;
};
void osk_dialog::Create(const std::string& /*title*/, const std::u16string& message, char16_t* init_text, u32 charlimit, u32 prohibit_flags, u32 panel_flag, u32 first_view_panel, color base_color, bool dimmer_enabled, bool intercept_input)
{
state = OskDialogState::Open;
flags = prohibit_flags;
char_limit = charlimit;
m_frame.back_color.r = base_color.r;
m_frame.back_color.g = base_color.g;
m_frame.back_color.b = base_color.b;
m_frame.back_color.a = base_color.a;
m_background.back_color.a = dimmer_enabled ? 0.8f : 0.0f;
m_start_pad_interception = intercept_input;
const callback_t shift_cb = [this](const std::u32string& text){ on_shift(text); };
const callback_t layer_cb = [this](const std::u32string& text){ on_layer(text); };
const callback_t space_cb = [this](const std::u32string& text){ on_space(text); };
const callback_t delete_cb = [this](const std::u32string& text){ on_backspace(text); };
const callback_t enter_cb = [this](const std::u32string& text){ on_enter(text); };
if (panel_flag & CELL_OSKDIALOG_PANELMODE_PASSWORD)
{
// If password was requested, then password has to be the only osk panel mode available to the user
// first_view_panel can be ignored
add_panel(osk_panel_password(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
m_password_mode = true;
}
else if (panel_flag == CELL_OSKDIALOG_PANELMODE_DEFAULT || panel_flag == CELL_OSKDIALOG_PANELMODE_DEFAULT_NO_JAPANESE)
{
// Prefer the systems settings
// first_view_panel is ignored
switch (g_cfg.sys.language)
{
case CELL_SYSUTIL_LANG_JAPANESE:
if (panel_flag == CELL_OSKDIALOG_PANELMODE_DEFAULT_NO_JAPANESE)
add_panel(osk_panel_english(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
else
add_panel(osk_panel_japanese(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_FRENCH:
add_panel(osk_panel_french(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_SPANISH:
add_panel(osk_panel_spanish(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_GERMAN:
add_panel(osk_panel_german(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_ITALIAN:
add_panel(osk_panel_italian(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_DANISH:
add_panel(osk_panel_danish(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_NORWEGIAN:
add_panel(osk_panel_norwegian(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_DUTCH:
add_panel(osk_panel_dutch(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_FINNISH:
add_panel(osk_panel_finnish(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_SWEDISH:
add_panel(osk_panel_swedish(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_PORTUGUESE_PT:
add_panel(osk_panel_portuguese_pt(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_PORTUGUESE_BR:
add_panel(osk_panel_portuguese_br(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_TURKISH:
add_panel(osk_panel_turkey(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_POLISH:
add_panel(osk_panel_polish(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_RUSSIAN:
add_panel(osk_panel_russian(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_KOREAN:
add_panel(osk_panel_korean(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_CHINESE_T:
add_panel(osk_panel_traditional_chinese(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_CHINESE_S:
add_panel(osk_panel_simplified_chinese(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
case CELL_SYSUTIL_LANG_ENGLISH_US:
case CELL_SYSUTIL_LANG_ENGLISH_GB:
default:
add_panel(osk_panel_english(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
break;
}
}
else
{
// Append osk modes.
// TODO: find out the exact order
if (panel_flag & CELL_OSKDIALOG_PANELMODE_LATIN)
{
add_panel(osk_panel_latin(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_ENGLISH)
{
add_panel(osk_panel_english(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_FRENCH)
{
add_panel(osk_panel_french(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_SPANISH)
{
add_panel(osk_panel_spanish(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_ITALIAN)
{
add_panel(osk_panel_italian(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_GERMAN)
{
add_panel(osk_panel_german(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_TURKEY)
{
add_panel(osk_panel_turkey(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_POLISH)
{
add_panel(osk_panel_polish(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_RUSSIAN)
{
add_panel(osk_panel_russian(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_DANISH)
{
add_panel(osk_panel_danish(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_NORWEGIAN)
{
add_panel(osk_panel_norwegian(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_DUTCH)
{
add_panel(osk_panel_dutch(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_SWEDISH)
{
add_panel(osk_panel_swedish(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_FINNISH)
{
add_panel(osk_panel_finnish(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_PORTUGUESE)
{
add_panel(osk_panel_portuguese_pt(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_PORTUGUESE_BRAZIL)
{
add_panel(osk_panel_portuguese_br(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_KOREAN)
{
add_panel(osk_panel_korean(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_TRADITIONAL_CHINESE)
{
add_panel(osk_panel_traditional_chinese(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_SIMPLIFIED_CHINESE)
{
add_panel(osk_panel_simplified_chinese(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_JAPANESE)
{
add_panel(osk_panel_japanese(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_JAPANESE_HIRAGANA)
{
add_panel(osk_panel_japanese_hiragana(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_JAPANESE_KATAKANA)
{
add_panel(osk_panel_japanese_katakana(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_ALPHABET)
{
add_panel(osk_panel_alphabet_half_width(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_ALPHABET_FULL_WIDTH)
{
add_panel(osk_panel_alphabet_full_width(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_NUMERAL)
{
add_panel(osk_panel_numeral_half_width(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_NUMERAL_FULL_WIDTH)
{
add_panel(osk_panel_numeral_full_width(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
if (panel_flag & CELL_OSKDIALOG_PANELMODE_URL)
{
add_panel(osk_panel_url(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
// Get initial panel based on first_view_panel
for (usz i = 0; i < m_panels.size(); ++i)
{
if (first_view_panel == m_panels[i].osk_panel_mode)
{
m_panel_index = i;
break;
}
}
}
// Fallback to english in case we forgot something
if (m_panels.empty())
{
osk.error("No OSK panel found. Using english panel.");
add_panel(osk_panel_english(shift_cb, layer_cb, space_cb, delete_cb, enter_cb));
}
initialize_layout(utf16_to_u32string(message), utf16_to_u32string(init_text));
update_panel();
auto& osk_thread = g_fxo->get<named_thread<osk_dialog_thread>>();
const auto notify = std::make_shared<atomic_t<bool>>(false);
osk_thread([&, notify]()
{
const u64 tbit = alloc_thread_bit();
g_thread_bit = tbit;
*notify = true;
notify->notify_one();
if (const auto error = run_input_loop())
{
if (error != selection_code::canceled)
{
rsx_log.error("Osk input loop exited with error code=%d", error);
}
}
thread_bits &= ~tbit;
thread_bits.notify_all();
});
while (osk_thread < thread_state::errored && !*notify)
{
notify->wait(false, atomic_wait_timeout{1'000'000});
}
}
}
}