From 52990612826f1815e17d1bfbe0477f95bdb88a9b Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 21 Jan 2023 11:57:10 +0100 Subject: [PATCH] overlays/osk: visualize separate windows --- rpcs3/Emu/Cell/Modules/cellOskDialog.cpp | 22 +-- rpcs3/Emu/Cell/Modules/cellOskDialog.h | 12 +- rpcs3/Emu/RSX/Overlays/overlay_osk.cpp | 165 ++++++++++++++++------- rpcs3/Emu/RSX/Overlays/overlay_osk.h | 23 ++-- 4 files changed, 153 insertions(+), 69 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp b/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp index df69691b1c..c9afb6995a 100644 --- a/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp +++ b/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp @@ -60,7 +60,7 @@ void osk_info::reset() lock_ext_input = false; device_mask = 0; input_field_window_width = 0; - input_field_background_transparency = 0.0f; + input_field_background_transparency = 1.0f; input_field_layout_info = {}; input_panel_layout_info = {}; key_layout_options = CELL_OSKDIALOG_10KEY_PANEL; @@ -540,14 +540,16 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr dia .panel_flag = allowOskPanelFlg, .support_language = info.supported_languages, .first_view_panel = firstViewPanel, - .x_align = info.layout.x_align, - .y_align = info.layout.y_align, - .x_offset = info.layout.x_offset, - .y_offset = info.layout.y_offset, + .layout = info.layout, + .input_layout = info.input_field_layout_info, + .panel_layout = info.input_panel_layout_info, + .input_field_window_width = info.input_field_window_width, + .input_field_background_transparency = info.input_field_background_transparency, .initial_scale = info.initial_scale, - .base_color = info.base_color.load(), - .dimmer_enabled = info.dimmer_enabled.load(), - .intercept_input = false + .base_color = info.base_color, + .dimmer_enabled = info.dimmer_enabled, + .use_separate_windows = info.use_separate_windows, + .intercept_input = false // We handle the interception manually based on the device mask }); }); @@ -984,6 +986,7 @@ error_code cellOskDialogExtSendFinishMessage(u32 /*CellOskDialogFinishReason*/ f return CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED; } + // TODO: only hide the dialog if we use separate windows osk->Close(finishReason); return CELL_OK; @@ -1125,6 +1128,9 @@ error_code cellOskDialogExtSetPointerEnable(b8 enable) { cellOskDialog.warning("cellOskDialogExtSetPointerEnable(enable=%d)", enable); + // TODO: While the pointer is already displayed in the osk overlay, it is not really useful right now. + // On real hardware, this may be used for actual PS Move or mouse input. + g_fxo->get().pointer_enabled = enable; return CELL_OK; diff --git a/rpcs3/Emu/Cell/Modules/cellOskDialog.h b/rpcs3/Emu/Cell/Modules/cellOskDialog.h index b3b3f3ddd6..6a925f7807 100644 --- a/rpcs3/Emu/Cell/Modules/cellOskDialog.h +++ b/rpcs3/Emu/Cell/Modules/cellOskDialog.h @@ -287,13 +287,15 @@ public: u32 panel_flag = 0; u32 support_language = 0; u32 first_view_panel = 0; - u32 x_align = 0; - u32 y_align = 0; - f32 x_offset = 0.0f; - f32 y_offset = 0.0f; + osk_window_layout layout{}; + osk_window_layout input_layout{}; // Only used with separate windows + osk_window_layout panel_layout{}; // Only used with separate windows + u32 input_field_window_width = 0; // Only used with separate windows + f32 input_field_background_transparency = 1.0f; // Only used with separate windows f32 initial_scale = 1.0f; color base_color{}; bool dimmer_enabled = false; + bool use_separate_windows = false; bool intercept_input = false; }; @@ -330,7 +332,7 @@ struct osk_info atomic_t lock_ext_input = false; atomic_t device_mask = 0; // OSK ignores input from the specified devices. 0 means all devices can influence the OSK atomic_t input_field_window_width = 0; - atomic_t input_field_background_transparency = 0.0f; + atomic_t input_field_background_transparency = 1.0f; osk_window_layout input_field_layout_info{}; osk_window_layout input_panel_layout_info{}; atomic_t key_layout_options = CELL_OSKDIALOG_10KEY_PANEL; diff --git a/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp index 3f0ce62273..6a0beda88f 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp @@ -99,7 +99,7 @@ namespace rsx m_grid.resize(cell_count); num_shift_layers_by_charset.clear(); - const position2u grid_origin(m_frame.x, m_frame.y + m_title.h + m_preview.h); + const position2u grid_origin(m_panel_frame.x, m_panel_frame.y); const u32 old_index = (selected_y * num_columns) + selected_x; @@ -215,98 +215,137 @@ namespace rsx void osk_dialog::update_layout() { + const bool show_panel = m_show_panel || !m_use_separate_windows; + const u16 title_height = get_scaled(30); const u16 preview_height = get_scaled((flags & CELL_OSKDIALOG_NO_RETURN) ? 40 : 90); // Place elements with absolute positioning const u16 button_margin = get_scaled(30); const u16 button_height = get_scaled(30); - const u16 frame_w = num_columns * cell_size_x; - const u16 frame_h = num_rows * cell_size_y + title_height + preview_height; + const u16 panel_w = show_panel ? (num_columns * cell_size_x) : 0; + const u16 panel_h = show_panel ? (num_rows * cell_size_y) : 0; + const u16 input_w = m_use_separate_windows ? m_input_field_window_width : panel_w; + const u16 input_h = title_height + preview_height; + const u16 button_h = show_panel ? (button_height + button_margin) : 0; + const u16 total_w = std::max(input_w, panel_w); + const u16 total_h = input_h + panel_h + button_h; f32 origin_x = 0.0f; f32 origin_y = 0.0f; - switch (m_x_align) + // TODO: Instead of an actual alignment, the layout mode may tell us which corner of the dialog we should use for positioning. + // TODO: Align separate windows. + // TODO: Make sure separate windows don't overlap. + // TODO: Does the y offset set by the game need to be added or subtracted? + + switch (m_layout.x_align) { case CELL_OSKDIALOG_LAYOUTMODE_X_ALIGN_RIGHT: origin_x = virtual_width; break; case CELL_OSKDIALOG_LAYOUTMODE_X_ALIGN_CENTER: - origin_x = static_cast(virtual_width - frame_w) / 2.0f; + origin_x = static_cast(virtual_width - total_w) / 2.0f; break; case CELL_OSKDIALOG_LAYOUTMODE_X_ALIGN_LEFT: default: break; } - switch (m_y_align) + switch (m_layout.y_align) { case CELL_OSKDIALOG_LAYOUTMODE_Y_ALIGN_BOTTOM: origin_y = virtual_height; break; case CELL_OSKDIALOG_LAYOUTMODE_Y_ALIGN_CENTER: - origin_y = static_cast(virtual_height - frame_h) / 2.0f; + origin_y = static_cast(virtual_height - total_h) / 2.0f; break; case CELL_OSKDIALOG_LAYOUTMODE_Y_ALIGN_TOP: default: break; } - // TODO: does the y offset need to be added or subtracted? - // Calculate initial position and analog movement range. constexpr f32 margin = 50.0f; // Let's add a minimal margin on all sides const u16 x_min = static_cast(margin); - const u16 x_max = static_cast(static_cast(virtual_width - frame_w) - margin); + const u16 x_max = static_cast(static_cast(virtual_width - total_w) - margin); const u16 y_min = static_cast(margin); - const u16 y_max = static_cast(static_cast(virtual_height - (frame_h + button_height + button_margin)) - margin); - u16 frame_x = 0; - u16 frame_y = 0; + const u16 y_max = static_cast(static_cast(virtual_height - total_h) - margin); + u16 input_x = 0; + u16 input_y = 0; + u16 panel_x = 0; + u16 panel_y = 0; - // x pos should only be 0 the first time - if (m_x_pos == 0) + // x pos should only be 0 the first time, because we always add a margin + if (m_x_input_pos == 0) { - frame_x = m_x_pos = static_cast(std::clamp(origin_x + m_x_offset, x_min, x_max)); - frame_y = m_y_pos = static_cast(std::clamp(origin_y + m_y_offset, y_min, y_max)); + const osk_window_layout& input_layout = m_use_separate_windows ? m_input_layout : m_layout; + const osk_window_layout& panel_layout = m_use_separate_windows ? m_panel_layout : m_layout; + + if (m_use_separate_windows) + { + input_x = m_x_input_pos = static_cast(std::clamp(origin_x + input_layout.x_offset, x_min, x_max)); + input_y = m_y_input_pos = static_cast(std::clamp(origin_y + input_layout.y_offset, y_min, y_max)); + panel_x = m_x_panel_pos = static_cast(std::clamp(origin_x + panel_layout.x_offset, x_min, x_max)); + panel_y = m_y_panel_pos = static_cast(std::clamp(origin_y + panel_layout.y_offset, y_min + input_h, y_max + input_h)); + } + else + { + input_x = panel_x = m_x_input_pos = m_x_panel_pos = static_cast(std::clamp(origin_x + input_layout.x_offset, x_min, x_max)); + input_y = m_y_input_pos = static_cast(std::clamp(origin_y + input_layout.y_offset, y_min, y_max)); + panel_y = m_y_panel_pos = input_y + input_h; + } + } + else if (m_use_separate_windows) + { + input_x = m_x_input_pos = std::clamp(m_x_input_pos, x_min, x_max); + input_y = m_y_input_pos = std::clamp(m_y_input_pos, y_min, y_max); + panel_x = m_x_panel_pos = std::clamp(m_x_panel_pos, x_min, x_max); + panel_y = m_y_panel_pos = std::clamp(m_y_panel_pos, y_min + input_h, y_max + input_h); } else { - frame_x = m_x_pos = std::clamp(m_x_pos, x_min, x_max); - frame_y = m_y_pos = std::clamp(m_y_pos, y_min, y_max); + input_x = panel_x = m_x_input_pos = m_x_panel_pos = std::clamp(m_x_input_pos, x_min, x_max); + input_y = m_y_input_pos = std::clamp(m_y_input_pos, y_min, y_max); + panel_y = m_y_panel_pos = input_y + input_h; } - m_frame.set_pos(frame_x, frame_y); - m_frame.set_size(frame_w, frame_h); + m_input_frame.set_pos(input_x, input_y); + m_input_frame.set_size(input_w, input_h); - m_title.set_pos(frame_x, frame_y); - m_title.set_size(frame_w, title_height); + m_panel_frame.set_pos(panel_x, panel_y); + m_panel_frame.set_size(panel_w, panel_h); + + m_title.set_pos(input_x, input_y); + m_title.set_size(input_w, title_height); m_title.set_padding(get_scaled(15), 0, get_scaled(5), 0); - m_preview.set_pos(frame_x, frame_y + title_height); - m_preview.set_size(frame_w, preview_height); + m_preview.set_pos(input_x, input_y + title_height); + m_preview.set_size(input_w, preview_height); m_preview.set_padding(get_scaled(15), 0, get_scaled(10), 0); - m_btn_cancel.set_pos(frame_x, frame_y + frame_h + button_margin); + const u16 button_y = panel_y + panel_h + button_margin; + + m_btn_cancel.set_pos(panel_x, button_y); m_btn_cancel.set_size(get_scaled(140), button_height); m_btn_cancel.set_text(localized_string_id::RSX_OVERLAYS_OSK_DIALOG_CANCEL); m_btn_cancel.set_text_vertical_adjust(get_scaled(5)); - m_btn_space.set_pos(frame_x + get_scaled(100), frame_y + frame_h + button_margin); + m_btn_space.set_pos(panel_x + get_scaled(100), button_y); m_btn_space.set_size(get_scaled(100), button_height); m_btn_space.set_text(localized_string_id::RSX_OVERLAYS_OSK_DIALOG_SPACE); m_btn_space.set_text_vertical_adjust(get_scaled(5)); - m_btn_delete.set_pos(frame_x + get_scaled(200), frame_y + frame_h + button_margin); + m_btn_delete.set_pos(panel_x + get_scaled(200), button_y); m_btn_delete.set_size(get_scaled(100), button_height); m_btn_delete.set_text(localized_string_id::RSX_OVERLAYS_OSK_DIALOG_BACKSPACE); m_btn_delete.set_text_vertical_adjust(get_scaled(5)); - m_btn_shift.set_pos(frame_x + get_scaled(320), frame_y + frame_h + button_margin); + m_btn_shift.set_pos(panel_x + get_scaled(320), button_y); m_btn_shift.set_size(get_scaled(80), button_height); m_btn_shift.set_text(localized_string_id::RSX_OVERLAYS_OSK_DIALOG_SHIFT); m_btn_shift.set_text_vertical_adjust(get_scaled(5)); - m_btn_accept.set_pos(frame_x + get_scaled(400), frame_y + frame_h + button_margin); + m_btn_accept.set_pos(panel_x + get_scaled(400), button_y); m_btn_accept.set_size(get_scaled(100), button_height); m_btn_accept.set_text(localized_string_id::RSX_OVERLAYS_OSK_DIALOG_ACCEPT); m_btn_accept.set_text_vertical_adjust(get_scaled(5)); @@ -329,7 +368,6 @@ namespace rsx m_background.set_size(virtual_width, virtual_height); m_title.set_unicode_text(title); - m_title.back_color.a = 0.7f; // Uses the dimmed color of the frame background scale_font(m_title); m_preview.password_mode = m_password_mode; @@ -670,10 +708,10 @@ namespace rsx { switch (button_press) { - case pad_button::rs_left: m_x_pos -= 5; break; - case pad_button::rs_right: m_x_pos += 5; break; - case pad_button::rs_down: m_y_pos += 5; break; - case pad_button::rs_up: m_y_pos -= 5; break; + case pad_button::rs_left: m_x_input_pos -= 5; m_x_panel_pos -= 5; break; + case pad_button::rs_right: m_x_input_pos += 5; m_x_panel_pos += 5; break; + case pad_button::rs_down: m_y_input_pos += 5; m_y_panel_pos += 5; break; + case pad_button::rs_up: m_y_input_pos -= 5; m_y_panel_pos -= 5; break; default: break; } update_panel(); @@ -955,11 +993,25 @@ namespace rsx return {}; } - if (m_update) + if (!m_update) { - m_cached_resource.clear(); - m_cached_resource.add(m_background.get_compiled()); - m_cached_resource.add(m_frame.get_compiled()); + fade_animation.apply(m_cached_resource); + return m_cached_resource; + } + + m_cached_resource.clear(); + m_cached_resource.add(m_background.get_compiled()); + + if (m_use_separate_windows && !m_show_panel) + { + m_cached_resource.add(m_input_frame.get_compiled()); + m_cached_resource.add(m_title.get_compiled()); + m_cached_resource.add(m_preview.get_compiled()); + } + else + { + m_cached_resource.add(m_input_frame.get_compiled()); + m_cached_resource.add(m_panel_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()); @@ -1067,9 +1119,10 @@ namespace rsx m_cached_resource.add(m_pointer.get_compiled()); m_reset_pulse = false; - m_update = false; } + m_update = false; + fade_animation.apply(m_cached_resource); return m_cached_resource; } @@ -1084,17 +1137,33 @@ namespace rsx state = OskDialogState::Open; flags = params.prohibit_flags; char_limit = params.charlimit; - m_x_align = params.x_align; - m_y_align = params.y_align; - m_x_offset = params.x_offset; - m_y_offset = params.y_offset; + m_layout = params.layout; + m_input_layout = params.input_layout; + m_panel_layout = params.panel_layout; + m_input_field_window_width = params.input_field_window_width; m_scaling = params.initial_scale; - m_frame.back_color.r = params.base_color.r; - m_frame.back_color.g = params.base_color.g; - m_frame.back_color.b = params.base_color.b; - m_frame.back_color.a = params.base_color.a; + m_input_frame.back_color.r = params.base_color.r; + m_input_frame.back_color.g = params.base_color.g; + m_input_frame.back_color.b = params.base_color.b; + m_input_frame.back_color.a = params.base_color.a; + m_panel_frame.back_color = m_input_frame.back_color; m_background.back_color.a = params.dimmer_enabled ? 0.8f : 0.0f; m_start_pad_interception = params.intercept_input; + m_use_separate_windows = params.use_separate_windows; + + if (m_use_separate_windows) + { + // When using separate windows, we show the text field, but hide the pad input panel if the device mask contains CELL_OSKDIALOG_DEVICE_MASK_PAD. + // TODO: If controller input is allowed and the user presses a button, show the pad input panel. + // TODO: If keyboard input is allowed and the user presses a key, hide the pad input panel. + m_show_panel = pad_input_enabled; + m_title.back_color.a = std::clamp(params.input_field_background_transparency, 0.0f, 1.0f); + m_preview.back_color.a = std::clamp(params.input_field_background_transparency, 0.0f, 1.0f); + } + else + { + m_title.back_color.a = 0.7f; // Uses the dimmed color of the frame background + } 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); }; diff --git a/rpcs3/Emu/RSX/Overlays/overlay_osk.h b/rpcs3/Emu/RSX/Overlays/overlay_osk.h index 12bb6b6d46..41081bf714 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_osk.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_osk.h @@ -38,13 +38,18 @@ namespace rsx callback_t callback; }; - // Base UI - u32 m_x_align = 0; - u32 m_y_align = 0; - f32 m_x_offset = 0.0f; - f32 m_y_offset = 0.0f; + // Base UI configuration + bool m_use_separate_windows = false; + bool m_show_panel = true; + osk_window_layout m_layout = {}; + osk_window_layout m_input_layout = {}; // Only used with separate windows + osk_window_layout m_panel_layout = {}; // Only used with separate windows + u32 m_input_field_window_width = 0; // Only used with separate windows f32 m_scaling = 1.0f; - overlay_element m_frame; + + // Base UI + overlay_element m_input_frame; + overlay_element m_panel_frame; overlay_element m_background; label m_title; edit_text m_preview; @@ -58,8 +63,10 @@ namespace rsx cursor_item m_pointer{}; // Analog movement - u16 m_x_pos = 0; - u16 m_y_pos = 0; + u16 m_x_input_pos = 0; + u16 m_y_input_pos = 0; + u16 m_x_panel_pos = 0; + u16 m_y_panel_pos = 0; // Grid u16 cell_size_x = 0;