mirror of
https://github.com/xenia-project/xenia.git
synced 2025-12-06 07:12:03 +01:00
Title selection & bug fixes
Added title selection Fixed controller hotkeys for multiple connected controllers Fixed ImGUI dialog box stacking Added a new font for ImGUI
This commit is contained in:
parent
459497f0b6
commit
504fb9f205
|
|
@ -1101,10 +1101,13 @@ void EmulatorWindow::SetInitializingShaderStorage(bool initializing) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notes:
|
// Notes:
|
||||||
|
// SDL and XInput both support the guide button
|
||||||
|
//
|
||||||
// Assumes titles do not use the guide button.
|
// Assumes titles do not use the guide button.
|
||||||
// For titles that do such as dashboards these titles could be excluded based on
|
// For titles that do such as dashboards these titles could be excluded based on
|
||||||
// their title ID.
|
// their title ID.
|
||||||
//
|
//
|
||||||
|
// Xbox Gamebar:
|
||||||
// If the Xbox Gamebar overlay is enabled Windows will consume the guide
|
// If the Xbox Gamebar overlay is enabled Windows will consume the guide
|
||||||
// button's input, this can be seen using hid-demo.
|
// button's input, this can be seen using hid-demo.
|
||||||
//
|
//
|
||||||
|
|
@ -1115,6 +1118,12 @@ void EmulatorWindow::SetInitializingShaderStorage(bool initializing) {
|
||||||
// This is not an issue with DualShock controllers because Windows will not
|
// This is not an issue with DualShock controllers because Windows will not
|
||||||
// open the gamebar overlay using the PlayStation menu button.
|
// open the gamebar overlay using the PlayStation menu button.
|
||||||
//
|
//
|
||||||
|
// Xbox One S Controller:
|
||||||
|
// The guide button on this controller is very buggy no idea why.
|
||||||
|
// Using xinput usually registers after a double tap.
|
||||||
|
// Doesn't work at all using SDL.
|
||||||
|
// Needs more testing.
|
||||||
|
//
|
||||||
// Steam:
|
// Steam:
|
||||||
// If guide button focus is enabled steam will open.
|
// If guide button focus is enabled steam will open.
|
||||||
// Steam uses BACK + GUIDE to open an On-Screen keyboard, however this is not a
|
// Steam uses BACK + GUIDE to open an On-Screen keyboard, however this is not a
|
||||||
|
|
@ -1139,9 +1148,13 @@ const std::map<int, EmulatorWindow::ControllerHotKey> controller_hotkey_map = {
|
||||||
"X + Guide = Toggle Clear Memory Page State", true)},
|
"X + Guide = Toggle Clear Memory Page State", true)},
|
||||||
|
|
||||||
{X_INPUT_GAMEPAD_RIGHT_SHOULDER | X_INPUT_GAMEPAD_GUIDE,
|
{X_INPUT_GAMEPAD_RIGHT_SHOULDER | X_INPUT_GAMEPAD_GUIDE,
|
||||||
|
EmulatorWindow::ControllerHotKey(
|
||||||
|
EmulatorWindow::ButtonFunctions::ClearGPUCache,
|
||||||
|
"Right Shoulder + Guide = Clear GPU Cache", true)},
|
||||||
|
{X_INPUT_GAMEPAD_LEFT_SHOULDER | X_INPUT_GAMEPAD_GUIDE,
|
||||||
EmulatorWindow::ControllerHotKey(
|
EmulatorWindow::ControllerHotKey(
|
||||||
EmulatorWindow::ButtonFunctions::ToggleControllerVibration,
|
EmulatorWindow::ButtonFunctions::ToggleControllerVibration,
|
||||||
"Right Shoulder + Guide = Toggle Controller Vibration", true)},
|
"Left Shoulder + Guide = Toggle Controller Vibration", true)},
|
||||||
|
|
||||||
// CPU Time Scalar with no rumble feedback
|
// CPU Time Scalar with no rumble feedback
|
||||||
{X_INPUT_GAMEPAD_DPAD_DOWN | X_INPUT_GAMEPAD_GUIDE,
|
{X_INPUT_GAMEPAD_DPAD_DOWN | X_INPUT_GAMEPAD_GUIDE,
|
||||||
|
|
@ -1161,14 +1174,21 @@ const std::map<int, EmulatorWindow::ControllerHotKey> controller_hotkey_map = {
|
||||||
{X_INPUT_GAMEPAD_Y, EmulatorWindow::ControllerHotKey(
|
{X_INPUT_GAMEPAD_Y, EmulatorWindow::ControllerHotKey(
|
||||||
EmulatorWindow::ButtonFunctions::ToggleFullscreen,
|
EmulatorWindow::ButtonFunctions::ToggleFullscreen,
|
||||||
"Y = Toggle Fullscreen", true, false)},
|
"Y = Toggle Fullscreen", true, false)},
|
||||||
{X_INPUT_GAMEPAD_START,
|
{X_INPUT_GAMEPAD_START, EmulatorWindow::ControllerHotKey(
|
||||||
EmulatorWindow::ControllerHotKey(
|
EmulatorWindow::ButtonFunctions::RunTitle,
|
||||||
EmulatorWindow::ButtonFunctions::RunPreviouslyPlayedTitle,
|
"Start = Run Selected Title", false, false)},
|
||||||
"Start = Run previously played title", false, false)},
|
|
||||||
{X_INPUT_GAMEPAD_BACK | X_INPUT_GAMEPAD_START,
|
{X_INPUT_GAMEPAD_BACK | X_INPUT_GAMEPAD_START,
|
||||||
EmulatorWindow::ControllerHotKey(
|
EmulatorWindow::ControllerHotKey(
|
||||||
EmulatorWindow::ButtonFunctions::CloseWindow,
|
EmulatorWindow::ButtonFunctions::CloseWindow,
|
||||||
"Back + Start = Close Window", false, false)}};
|
"Back + Start = Close Window", false, false)},
|
||||||
|
{X_INPUT_GAMEPAD_DPAD_DOWN,
|
||||||
|
EmulatorWindow::ControllerHotKey(
|
||||||
|
EmulatorWindow::ButtonFunctions::IncTitleSelect,
|
||||||
|
"D-PAD Down + Guide = Title Selection +1", true, false)},
|
||||||
|
{X_INPUT_GAMEPAD_DPAD_UP,
|
||||||
|
EmulatorWindow::ControllerHotKey(
|
||||||
|
EmulatorWindow::ButtonFunctions::DecTitleSelect,
|
||||||
|
"D-PAD Up + Guide = Title Selection -1", true, false)}};
|
||||||
|
|
||||||
EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
|
EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
|
||||||
int buttons) {
|
int buttons) {
|
||||||
|
|
@ -1210,9 +1230,18 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
|
||||||
// Extra Sleep
|
// Extra Sleep
|
||||||
xe::threading::Sleep(delay);
|
xe::threading::Sleep(delay);
|
||||||
break;
|
break;
|
||||||
case ButtonFunctions::RunPreviouslyPlayedTitle:
|
case ButtonFunctions::RunTitle: {
|
||||||
RunPreviouslyPlayedTitle();
|
if (selected_title_index == -1) selected_title_index++;
|
||||||
break;
|
|
||||||
|
xe::X_STATUS title_success = RunTitle(
|
||||||
|
recently_launched_titles_[selected_title_index].path_to_file);
|
||||||
|
|
||||||
|
if (title_success == X_ERROR_SUCCESS) {
|
||||||
|
imgui_drawer_.get()->ClearDialogs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// RunPreviouslyPlayedTitle();
|
||||||
|
break;
|
||||||
case ButtonFunctions::ClearMemoryPageState:
|
case ButtonFunctions::ClearMemoryPageState:
|
||||||
ToggleGPUSetting(gpu_cvar::ClearMemoryPageState);
|
ToggleGPUSetting(gpu_cvar::ClearMemoryPageState);
|
||||||
|
|
||||||
|
|
@ -1239,12 +1268,24 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
|
||||||
case ButtonFunctions::CpuTimeScalarReset:
|
case ButtonFunctions::CpuTimeScalarReset:
|
||||||
CpuTimeScalarReset();
|
CpuTimeScalarReset();
|
||||||
break;
|
break;
|
||||||
|
case ButtonFunctions::ClearGPUCache:
|
||||||
|
GpuClearCaches();
|
||||||
|
|
||||||
|
// Extra Sleep
|
||||||
|
xe::threading::Sleep(delay);
|
||||||
|
break;
|
||||||
case ButtonFunctions::ToggleControllerVibration:
|
case ButtonFunctions::ToggleControllerVibration:
|
||||||
ToggleControllerVibration();
|
ToggleControllerVibration();
|
||||||
|
|
||||||
// Extra Sleep
|
// Extra Sleep
|
||||||
xe::threading::Sleep(delay);
|
xe::threading::Sleep(delay);
|
||||||
break;
|
break;
|
||||||
|
case ButtonFunctions::IncTitleSelect:
|
||||||
|
selected_title_index++;
|
||||||
|
break;
|
||||||
|
case ButtonFunctions::DecTitleSelect:
|
||||||
|
selected_title_index--;
|
||||||
|
break;
|
||||||
case ButtonFunctions::CloseWindow:
|
case ButtonFunctions::CloseWindow:
|
||||||
window_->RequestClose();
|
window_->RequestClose();
|
||||||
break;
|
break;
|
||||||
|
|
@ -1253,12 +1294,31 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (button_combination.function == ButtonFunctions::IncTitleSelect ||
|
||||||
|
button_combination.function == ButtonFunctions::DecTitleSelect) {
|
||||||
|
selected_title_index = std::clamp(
|
||||||
|
selected_title_index, 0, (int)recently_launched_titles_.size() - 1);
|
||||||
|
|
||||||
|
imgui_drawer_.get()->ClearDialogs();
|
||||||
|
|
||||||
|
// Titles may contain Unicode characters such as At World’s End
|
||||||
|
// Must use ImGUI font that can render these Unicode characters
|
||||||
|
std::string title =
|
||||||
|
std::to_string(selected_title_index + 1) + ": " +
|
||||||
|
recently_launched_titles_[selected_title_index].title_name + "\n\n" +
|
||||||
|
controller_hotkey_map.find(X_INPUT_GAMEPAD_START)->second.pretty;
|
||||||
|
|
||||||
|
xe::ui::ImGuiDialog::ShowMessageBox(imgui_drawer_.get(), "Title Selection",
|
||||||
|
title);
|
||||||
|
}
|
||||||
|
|
||||||
xe::threading::Sleep(delay);
|
xe::threading::Sleep(delay);
|
||||||
|
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmulatorWindow::VibrateController(xe::hid::InputSystem* input_sys,
|
void EmulatorWindow::VibrateController(xe::hid::InputSystem* input_sys,
|
||||||
|
uint32_t user_index,
|
||||||
bool toggle_rumble) {
|
bool toggle_rumble) {
|
||||||
const std::chrono::milliseconds rumble_duration(100);
|
const std::chrono::milliseconds rumble_duration(100);
|
||||||
|
|
||||||
|
|
@ -1266,12 +1326,12 @@ void EmulatorWindow::VibrateController(xe::hid::InputSystem* input_sys,
|
||||||
// otherwise the rumble may fail.
|
// otherwise the rumble may fail.
|
||||||
auto input_lock = input_sys->lock();
|
auto input_lock = input_sys->lock();
|
||||||
|
|
||||||
X_INPUT_VIBRATION vibration;
|
X_INPUT_VIBRATION vibration{};
|
||||||
|
|
||||||
vibration.left_motor_speed = toggle_rumble ? UINT16_MAX : 0;
|
vibration.left_motor_speed = toggle_rumble ? UINT16_MAX : 0;
|
||||||
vibration.right_motor_speed = toggle_rumble ? UINT16_MAX : 0;
|
vibration.right_motor_speed = toggle_rumble ? UINT16_MAX : 0;
|
||||||
|
|
||||||
input_sys->SetState(0, &vibration);
|
input_sys->SetState(user_index, &vibration);
|
||||||
|
|
||||||
// Vibration duration
|
// Vibration duration
|
||||||
if (toggle_rumble) {
|
if (toggle_rumble) {
|
||||||
|
|
@ -1286,26 +1346,31 @@ void EmulatorWindow::GamepadHotKeys() {
|
||||||
|
|
||||||
auto input_sys = emulator_->input_system();
|
auto input_sys = emulator_->input_system();
|
||||||
|
|
||||||
|
// uint8_t users = emulator_->kernel_state()->GetConnectedUsers();
|
||||||
|
// uint8_t users = input_sys->GetConnectedSlots();
|
||||||
|
|
||||||
if (input_sys) {
|
if (input_sys) {
|
||||||
// SDL and XInput both support the guide button
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// Block scope surrounding input_lock used to release the lock
|
auto input_lock = input_sys->lock();
|
||||||
{
|
|
||||||
auto input_lock = input_sys->lock();
|
|
||||||
|
|
||||||
for (uint32_t user_index = 0; user_index < MAX_USERS; ++user_index) {
|
for (uint32_t user_index = 0; user_index < MAX_USERS; ++user_index) {
|
||||||
input_sys->GetState(user_index, &state);
|
X_RESULT result = input_sys->GetState(user_index, &state);
|
||||||
|
|
||||||
|
// Check if the controller is connected
|
||||||
|
if (result == X_ERROR_SUCCESS) {
|
||||||
|
// Release the lock before processing the hotkey
|
||||||
|
input_lock.mutex()->unlock();
|
||||||
|
|
||||||
|
if (ProcessControllerHotkey(state.gamepad.buttons).rumble) {
|
||||||
|
// Enable Vibration
|
||||||
|
VibrateController(input_sys, user_index, true);
|
||||||
|
|
||||||
|
// Disable Vibration
|
||||||
|
VibrateController(input_sys, user_index, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ProcessControllerHotkey(state.gamepad.buttons).rumble) {
|
|
||||||
// Enable Vibration
|
|
||||||
VibrateController(input_sys, true);
|
|
||||||
|
|
||||||
// Disable Vibration
|
|
||||||
VibrateController(input_sys, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
xe::threading::Sleep(thread_delay);
|
xe::threading::Sleep(thread_delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1383,6 +1448,8 @@ void EmulatorWindow::DisplayHotKeysConfig() {
|
||||||
"Clear Memory Page State: " +
|
"Clear Memory Page State: " +
|
||||||
std::string(cvars::d3d12_clear_memory_page_state ? "true\n" : "false\n");
|
std::string(cvars::d3d12_clear_memory_page_state ? "true\n" : "false\n");
|
||||||
|
|
||||||
|
imgui_drawer_.get()->ClearDialogs();
|
||||||
|
|
||||||
xe::ui::ImGuiDialog::ShowMessageBox(imgui_drawer_.get(), "Controller Hotkeys",
|
xe::ui::ImGuiDialog::ShowMessageBox(imgui_drawer_.get(), "Controller Hotkeys",
|
||||||
msg);
|
msg);
|
||||||
}
|
}
|
||||||
|
|
@ -1396,6 +1463,8 @@ xe::X_STATUS EmulatorWindow::RunTitle(std::filesystem::path path) {
|
||||||
|
|
||||||
XELOGE(log_msg);
|
XELOGE(log_msg);
|
||||||
|
|
||||||
|
imgui_drawer_.get()->ClearDialogs();
|
||||||
|
|
||||||
xe::ui::ImGuiDialog::ShowMessageBox(imgui_drawer_.get(),
|
xe::ui::ImGuiDialog::ShowMessageBox(imgui_drawer_.get(),
|
||||||
"Title Launch Failed!", log_msg);
|
"Title Launch Failed!", log_msg);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,8 @@ class EmulatorWindow {
|
||||||
|
|
||||||
std::unique_ptr<xe::threading::Thread> Gamepad_HotKeys_Listener;
|
std::unique_ptr<xe::threading::Thread> Gamepad_HotKeys_Listener;
|
||||||
|
|
||||||
|
int selected_title_index = -1;
|
||||||
|
|
||||||
Emulator* emulator() const { return emulator_; }
|
Emulator* emulator() const { return emulator_; }
|
||||||
ui::WindowedAppContext& app_context() const { return app_context_; }
|
ui::WindowedAppContext& app_context() const { return app_context_; }
|
||||||
ui::Window* window() const { return window_.get(); }
|
ui::Window* window() const { return window_.get(); }
|
||||||
|
|
@ -73,17 +75,20 @@ class EmulatorWindow {
|
||||||
void ToggleFullscreen();
|
void ToggleFullscreen();
|
||||||
void SetInitializingShaderStorage(bool initializing);
|
void SetInitializingShaderStorage(bool initializing);
|
||||||
|
|
||||||
// Types of button functions for hotkeys.
|
// Types of button functions for hotkeys.
|
||||||
enum class ButtonFunctions {
|
enum class ButtonFunctions {
|
||||||
ToggleFullscreen,
|
ToggleFullscreen,
|
||||||
RunPreviouslyPlayedTitle,
|
RunTitle,
|
||||||
CpuTimeScalarSetHalf,
|
CpuTimeScalarSetHalf,
|
||||||
CpuTimeScalarSetDouble,
|
CpuTimeScalarSetDouble,
|
||||||
CpuTimeScalarReset,
|
CpuTimeScalarReset,
|
||||||
|
ClearGPUCache,
|
||||||
ToggleControllerVibration,
|
ToggleControllerVibration,
|
||||||
ClearMemoryPageState,
|
ClearMemoryPageState,
|
||||||
ReadbackResolve,
|
ReadbackResolve,
|
||||||
CloseWindow,
|
CloseWindow,
|
||||||
|
IncTitleSelect,
|
||||||
|
DecTitleSelect,
|
||||||
Unknown
|
Unknown
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -198,7 +203,8 @@ class EmulatorWindow {
|
||||||
void ShowBuildCommit();
|
void ShowBuildCommit();
|
||||||
|
|
||||||
EmulatorWindow::ControllerHotKey ProcessControllerHotkey(int buttons);
|
EmulatorWindow::ControllerHotKey ProcessControllerHotkey(int buttons);
|
||||||
void VibrateController(xe::hid::InputSystem* input_sys, bool vibrate = true);
|
void VibrateController(xe::hid::InputSystem* input_sys, uint32_t user_index,
|
||||||
|
bool vibrate = true);
|
||||||
void GamepadHotKeys();
|
void GamepadHotKeys();
|
||||||
void ToggleGPUSetting(gpu_cvar index);
|
void ToggleGPUSetting(gpu_cvar index);
|
||||||
bool IsUseNexusForGameBarEnabled();
|
bool IsUseNexusForGameBarEnabled();
|
||||||
|
|
|
||||||
|
|
@ -109,13 +109,29 @@ void ImGuiDrawer::Initialize() {
|
||||||
ImFontConfig font_config;
|
ImFontConfig font_config;
|
||||||
font_config.OversampleH = font_config.OversampleV = 1;
|
font_config.OversampleH = font_config.OversampleV = 1;
|
||||||
font_config.PixelSnapH = true;
|
font_config.PixelSnapH = true;
|
||||||
|
|
||||||
|
// https://jrgraphix.net/r/Unicode/
|
||||||
static const ImWchar font_glyph_ranges[] = {
|
static const ImWchar font_glyph_ranges[] = {
|
||||||
0x0020,
|
0x0020, 0x00FF, // Basic Latin + Latin Supplement
|
||||||
0x00FF, // Basic Latin + Latin Supplement
|
0x2000, 0x206F, // General Punctuation
|
||||||
0,
|
0,
|
||||||
};
|
};
|
||||||
io.Fonts->AddFontFromMemoryCompressedBase85TTF(
|
io.Fonts->AddFontFromMemoryCompressedBase85TTF(
|
||||||
kProggyTinyCompressedDataBase85, 10.0f, &font_config, font_glyph_ranges);
|
kProggyTinyCompressedDataBase85, 10.0f, &font_config,
|
||||||
|
io.Fonts->GetGlyphRangesDefault());
|
||||||
|
|
||||||
|
font_config.MergeMode = true;
|
||||||
|
|
||||||
|
const char* alt_font = "C:\\Windows\\Fonts\\segoeui.ttf";
|
||||||
|
if (std::filesystem::exists(alt_font)) {
|
||||||
|
io.Fonts->AddFontFromFileTTF(alt_font, 16.0f, &font_config,
|
||||||
|
font_glyph_ranges);
|
||||||
|
} else {
|
||||||
|
XELOGW(
|
||||||
|
"Unable to load Segoe UI; General Punctuation characters will be "
|
||||||
|
"boxes");
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(benvanik): jp font on other platforms?
|
// TODO(benvanik): jp font on other platforms?
|
||||||
// https://github.com/Koruri/kibitaki looks really good, but is 1.5MiB.
|
// https://github.com/Koruri/kibitaki looks really good, but is 1.5MiB.
|
||||||
const char* jp_font_path = "C:\\Windows\\Fonts\\msgothic.ttc";
|
const char* jp_font_path = "C:\\Windows\\Fonts\\msgothic.ttc";
|
||||||
|
|
@ -328,6 +344,14 @@ void ImGuiDrawer::Draw(UIDrawContext& ui_draw_context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImGuiDrawer::ClearDialogs() {
|
||||||
|
size_t dialog_loop = 0;
|
||||||
|
|
||||||
|
while (dialog_loop < dialogs_.size()) {
|
||||||
|
RemoveDialog(dialogs_[dialog_loop++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ImGuiDrawer::RenderDrawLists(ImDrawData* data,
|
void ImGuiDrawer::RenderDrawLists(ImDrawData* data,
|
||||||
UIDrawContext& ui_draw_context) {
|
UIDrawContext& ui_draw_context) {
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,8 @@ class ImGuiDrawer : public WindowInputListener, public UIDrawer {
|
||||||
|
|
||||||
void Draw(UIDrawContext& ui_draw_context) override;
|
void Draw(UIDrawContext& ui_draw_context) override;
|
||||||
|
|
||||||
|
void ClearDialogs();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void OnKeyDown(KeyEvent& e) override;
|
void OnKeyDown(KeyEvent& e) override;
|
||||||
void OnKeyUp(KeyEvent& e) override;
|
void OnKeyUp(KeyEvent& e) override;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue