#include "stdafx.h" #include "overlay_manager.h" #include "overlay_message_dialog.h" #include "Emu/System.h" #include "Emu/system_config.h" #include "Emu/system_utils.hpp" #include "Emu/Cell/ErrorCodes.h" #include "Emu/IdManager.h" #include "util/Thread.h" #include namespace rsx { namespace overlays { message_dialog::message_dialog(bool allow_custom_background) : custom_background_allowed(allow_custom_background) { background.set_size(virtual_width, virtual_height); background.back_color.a = 0.85f; text_display.set_size(1100, 40); text_display.set_pos(90, 364); text_display.set_font("Arial", 16); text_display.align_text(overlay_element::text_align::center); text_display.set_wrap_text(true); text_display.back_color.a = 0.f; bottom_bar.back_color = color4f(1.f, 1.f, 1.f, 1.f); bottom_bar.set_size(1200, 2); bottom_bar.set_pos(40, 400); for (progress_bar& bar : progress_bars) { bar.set_size(800, 4); bar.back_color = color4f(0.25f, 0.f, 0.f, 0.85f); } btn_ok.set_text(localized_string_id::RSX_OVERLAYS_MSG_DIALOG_YES); btn_ok.set_size(140, 30); btn_ok.set_pos(545, 420); btn_ok.set_font("Arial", 16); btn_cancel.set_text(localized_string_id::RSX_OVERLAYS_MSG_DIALOG_NO); btn_cancel.set_size(140, 30); btn_cancel.set_pos(685, 420); btn_cancel.set_font("Arial", 16); if (g_cfg.sys.enter_button_assignment == enter_button_assign::circle) { btn_ok.set_image_resource(resource_config::standard_image_resource::circle); btn_cancel.set_image_resource(resource_config::standard_image_resource::cross); } else { btn_ok.set_image_resource(resource_config::standard_image_resource::cross); btn_cancel.set_image_resource(resource_config::standard_image_resource::circle); } fade_animation.duration_sec = 0.15f; update_custom_background(); return_code = CELL_MSGDIALOG_BUTTON_NONE; } compiled_resource message_dialog::get_compiled() { if (!visible) { return {}; } if (const auto [dirty, text] = text_guard.get_text(); dirty) { u16 text_w, text_h; text_display.set_pos(90, 364); text_display.set_text(text); text_display.measure_text(text_w, text_h); text_display.translate(0, -(text_h - 16)); } for (u32 i = 0; i < progress_bars.size(); i++) { if (const auto [dirty, text] = ::at32(bar_text_guard, i).get_text(); dirty) { ::at32(progress_bars, i).set_text(text); } } compiled_resource result; update_custom_background(); if (background_image && background_image->get_data()) { result.add(background_poster.get_compiled()); if (background_overlay_image && background_overlay_image->get_data()) { result.add(background_overlay_poster.get_compiled()); } } result.add(background.get_compiled()); result.add(text_display.get_compiled()); if (num_progress_bars > 0) { result.add(::at32(progress_bars, 0).get_compiled()); } if (num_progress_bars > 1) { result.add(::at32(progress_bars, 1).get_compiled()); } if (interactive) { if (!num_progress_bars) result.add(bottom_bar.get_compiled()); if (!cancel_only) result.add(btn_ok.get_compiled()); if (!ok_only) result.add(btn_cancel.get_compiled()); } fade_animation.apply(result); return result; } void message_dialog::on_button_pressed(pad_button button_press, bool /*is_auto_repeat*/) { if (fade_animation.active) return; switch (button_press) { case pad_button::cross: { if (ok_only) { return_code = CELL_MSGDIALOG_BUTTON_OK; } else if (cancel_only) { // Do not accept for cancel-only dialogs return; } else { return_code = CELL_MSGDIALOG_BUTTON_YES; } Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_decide.wav"); break; } case pad_button::circle: { if (ok_only) { // Ignore cancel operation for Ok-only return; } if (cancel_only) { return_code = CELL_MSGDIALOG_BUTTON_ESCAPE; } else { return_code = CELL_MSGDIALOG_BUTTON_NO; } Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav"); break; } default: return; } fade_animation.current = color4f(1.f); fade_animation.end = color4f(0.f); fade_animation.active = true; fade_animation.on_finish = [this] { close(true, true); }; } void message_dialog::close(bool use_callback, bool stop_pad_interception) { if (num_progress_bars > 0) { Emu.GetCallbacks().handle_taskbar_progress(0, 1); } user_interface::close(use_callback, stop_pad_interception); } void message_dialog::update(u64 timestamp_us) { if (fade_animation.active) fade_animation.update(timestamp_us); } error_code message_dialog::show(bool is_blocking, const std::string& text, const MsgDialogType& type, msg_dialog_source source, std::function on_close) { visible = false; m_source = source; num_progress_bars = type.progress_bar_count; if (num_progress_bars) { s16 offset = 58; ::at32(progress_bars, 0).set_pos(240, 412); if (num_progress_bars > 1) { ::at32(progress_bars, 1).set_pos(240, 462); offset = 98; } // Push the other stuff down bottom_bar.translate(0, offset); btn_ok.translate(0, offset); btn_cancel.translate(0, offset); } else { fade_animation.current = color4f(0.f); fade_animation.end = color4f(1.f); fade_animation.active = true; } if (!type.se_mute_on) { if (type.se_normal) Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_system_ok.wav"); else Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_system_ng.wav"); } set_text(text); switch (type.button_type.unshifted()) { case CELL_MSGDIALOG_TYPE_BUTTON_TYPE_NONE: interactive = !type.disable_cancel; if (interactive) { btn_cancel.set_pos(585, btn_cancel.y); btn_cancel.set_text(localized_string_id::RSX_OVERLAYS_MSG_DIALOG_CANCEL); cancel_only = true; } break; case CELL_MSGDIALOG_TYPE_BUTTON_TYPE_OK: btn_ok.set_pos(600, btn_ok.y); btn_ok.set_text(localized_string_id::RSX_OVERLAYS_MSG_DIALOG_OK); interactive = true; ok_only = true; break; case CELL_MSGDIALOG_TYPE_BUTTON_TYPE_YESNO: interactive = true; break; default: break; } this->on_close = std::move(on_close); visible = true; if (is_blocking) { if (interactive) { if (const auto error = run_input_loop()) { if (error != selection_code::canceled) { rsx_log.error("Message dialog input loop exited with error code=%d", error); } return error; } } else { while (!m_stop_input_loop) { refresh(); // Only update the screen at about 60fps since updating it everytime slows down the process std::this_thread::sleep_for(16ms); } } } else { if (!m_stop_input_loop) { const auto notify = std::make_shared>(0); auto& overlayman = g_fxo->get(); if (interactive) { overlayman.attach_thread_input( uid, "Message dialog", [notify]() { *notify = true; notify->notify_one(); }); } else { overlayman.attach_thread_input( uid, "Message dialog", [notify]() { *notify = true; notify->notify_one(); }, nullptr, [&]() { while (!m_stop_input_loop && thread_ctrl::state() != thread_state::aborting) { refresh(); // Only update the screen at about 60fps since updating it everytime slows down the process std::this_thread::sleep_for(16ms); if (!g_fxo->is_init()) { rsx_log.fatal("display_manager was improperly destroyed"); break; } } return 0; }); } while (!Emu.IsStopped() && !*notify) { notify->wait(false, atomic_wait_timeout{1'000'000}); } } } return CELL_OK; } void message_dialog::set_text(std::string text) { text_guard.set_text(std::move(text)); } void message_dialog::update_custom_background() { if (custom_background_allowed && g_cfg.video.shader_preloading_dialog.use_custom_background) { bool dirty = std::exchange(background_blur_strength, g_cfg.video.shader_preloading_dialog.blur_strength.get()) != background_blur_strength; dirty |= std::exchange(background_darkening_strength, g_cfg.video.shader_preloading_dialog.darkening_strength.get()) != background_darkening_strength; if (!background_image) { // Search for any useable background picture in the given order game_content_type content_type = game_content_type::background_picture; for (game_content_type type : {game_content_type::background_picture, game_content_type::overlay_picture, game_content_type::content_icon}) { if (const std::string picture_path = rpcs3::utils::get_game_content_path(type); !picture_path.empty()) { content_type = type; background_image = std::make_unique(picture_path); dirty |= !!background_image->get_data(); break; } } // Search for an overlay picture in the same dir in case we found a real background picture if (background_image && !background_overlay_image && content_type == game_content_type::background_picture) { if (const std::string picture_path = rpcs3::utils::get_game_content_path(game_content_type::overlay_picture); !picture_path.empty()) { background_overlay_image = std::make_unique(picture_path); dirty |= !!background_overlay_image->get_data(); } } } if (dirty && background_image && background_image->get_data()) { const f32 color = (100 - background_darkening_strength) / 100.f; background_poster.fore_color = color4f(color, color, color, 1.); background.back_color.a = 0.f; background_poster.set_size(virtual_width, virtual_height); background_poster.set_raw_image(background_image.get()); background_poster.set_blur_strength(static_cast(background_blur_strength)); ensure(background_image->w > 0); ensure(background_image->h > 0); ensure(background_poster.h > 0); // Set padding in order to keep the aspect ratio if ((background_image->w / static_cast(background_image->h)) > (background_poster.w / static_cast(background_poster.h))) { const int padding = (background_poster.h - static_cast(background_image->h * (background_poster.w / static_cast(background_image->w)))) / 2; background_poster.set_padding(0, 0, padding, padding); } else { const int padding = (background_poster.w - static_cast(background_image->w * (background_poster.h / static_cast(background_image->h)))) / 2; background_poster.set_padding(padding, padding, 0, 0); } if (background_overlay_image && background_overlay_image->get_data()) { constexpr f32 reference_factor = 2.0f / 3.0f; const f32 image_aspect = background_overlay_image->w / static_cast(background_overlay_image->h); const f32 overlay_width = background_overlay_image->w * reference_factor; const f32 overlay_height = overlay_width / image_aspect; const u16 overlay_x = static_cast(std::min(virtual_width - overlay_width, (virtual_width * reference_factor) - (overlay_width / 2.0f))); const u16 overlay_y = static_cast(std::min(virtual_height - overlay_height, (virtual_height * reference_factor) - (overlay_height / 2.0f))); const f32 color = (100 - background_darkening_strength) / 100.f; background_overlay_poster.fore_color = color4f(color, color, color, 1.); background_overlay_poster.set_size(static_cast(overlay_width), static_cast(overlay_height)); background_overlay_poster.set_pos(overlay_x, overlay_y); background_overlay_poster.set_raw_image(background_overlay_image.get()); background_overlay_poster.set_blur_strength(static_cast(background_blur_strength)); } } } else { if (background_image) { background_poster.clear_image(); background_image.reset(); } if (background_overlay_image) { background_overlay_poster.clear_image(); background_overlay_image.reset(); } background.back_color.a = 0.85f; } } u32 message_dialog::progress_bar_count() const { return num_progress_bars; } void message_dialog::progress_bar_set_taskbar_index(s32 index) { taskbar_index = index; } error_code message_dialog::progress_bar_set_message(u32 index, std::string msg) { if (index >= num_progress_bars) return CELL_MSGDIALOG_ERROR_PARAM; ::at32(bar_text_guard, index).set_text(std::move(msg)); return CELL_OK; } error_code message_dialog::progress_bar_increment(u32 index, f32 value) { if (index >= num_progress_bars) return CELL_MSGDIALOG_ERROR_PARAM; ::at32(progress_bars, index).inc(value); if (index == static_cast(taskbar_index) || taskbar_index == -1) Emu.GetCallbacks().handle_taskbar_progress(1, static_cast(value)); return CELL_OK; } error_code message_dialog::progress_bar_set_value(u32 index, f32 value) { if (index >= num_progress_bars) return CELL_MSGDIALOG_ERROR_PARAM; ::at32(progress_bars, index).set_value(value); if (index == static_cast(taskbar_index) || taskbar_index == -1) Emu.GetCallbacks().handle_taskbar_progress(3, static_cast(value)); return CELL_OK; } error_code message_dialog::progress_bar_reset(u32 index) { if (index >= num_progress_bars) return CELL_MSGDIALOG_ERROR_PARAM; ::at32(progress_bars, index).set_value(0.f); Emu.GetCallbacks().handle_taskbar_progress(0, 0); return CELL_OK; } error_code message_dialog::progress_bar_set_limit(u32 index, u32 limit) { if (index >= num_progress_bars) return CELL_MSGDIALOG_ERROR_PARAM; ::at32(progress_bars, index).set_limit(static_cast(limit)); if (index == static_cast(taskbar_index)) { taskbar_limit = limit; Emu.GetCallbacks().handle_taskbar_progress(2, taskbar_limit); } else if (taskbar_index == -1) { taskbar_limit += limit; Emu.GetCallbacks().handle_taskbar_progress(2, taskbar_limit); } return CELL_OK; } } // namespace overlays } // namespace rsx