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:
|
||||
// SDL and XInput both support 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
|
||||
// their title ID.
|
||||
//
|
||||
// Xbox Gamebar:
|
||||
// If the Xbox Gamebar overlay is enabled Windows will consume the guide
|
||||
// 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
|
||||
// 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:
|
||||
// If guide button focus is enabled steam will open.
|
||||
// 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_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::ButtonFunctions::ToggleControllerVibration,
|
||||
"Right Shoulder + Guide = Toggle Controller Vibration", true)},
|
||||
"Left Shoulder + Guide = Toggle Controller Vibration", true)},
|
||||
|
||||
// CPU Time Scalar with no rumble feedback
|
||||
{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(
|
||||
EmulatorWindow::ButtonFunctions::ToggleFullscreen,
|
||||
"Y = Toggle Fullscreen", true, false)},
|
||||
{X_INPUT_GAMEPAD_START,
|
||||
EmulatorWindow::ControllerHotKey(
|
||||
EmulatorWindow::ButtonFunctions::RunPreviouslyPlayedTitle,
|
||||
"Start = Run previously played title", false, false)},
|
||||
{X_INPUT_GAMEPAD_START, EmulatorWindow::ControllerHotKey(
|
||||
EmulatorWindow::ButtonFunctions::RunTitle,
|
||||
"Start = Run Selected Title", false, false)},
|
||||
{X_INPUT_GAMEPAD_BACK | X_INPUT_GAMEPAD_START,
|
||||
EmulatorWindow::ControllerHotKey(
|
||||
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(
|
||||
int buttons) {
|
||||
|
@ -1210,9 +1230,18 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
|
|||
// Extra Sleep
|
||||
xe::threading::Sleep(delay);
|
||||
break;
|
||||
case ButtonFunctions::RunPreviouslyPlayedTitle:
|
||||
RunPreviouslyPlayedTitle();
|
||||
break;
|
||||
case ButtonFunctions::RunTitle: {
|
||||
if (selected_title_index == -1) selected_title_index++;
|
||||
|
||||
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:
|
||||
ToggleGPUSetting(gpu_cvar::ClearMemoryPageState);
|
||||
|
||||
|
@ -1239,12 +1268,24 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
|
|||
case ButtonFunctions::CpuTimeScalarReset:
|
||||
CpuTimeScalarReset();
|
||||
break;
|
||||
case ButtonFunctions::ClearGPUCache:
|
||||
GpuClearCaches();
|
||||
|
||||
// Extra Sleep
|
||||
xe::threading::Sleep(delay);
|
||||
break;
|
||||
case ButtonFunctions::ToggleControllerVibration:
|
||||
ToggleControllerVibration();
|
||||
|
||||
// Extra Sleep
|
||||
xe::threading::Sleep(delay);
|
||||
break;
|
||||
case ButtonFunctions::IncTitleSelect:
|
||||
selected_title_index++;
|
||||
break;
|
||||
case ButtonFunctions::DecTitleSelect:
|
||||
selected_title_index--;
|
||||
break;
|
||||
case ButtonFunctions::CloseWindow:
|
||||
window_->RequestClose();
|
||||
break;
|
||||
|
@ -1253,12 +1294,31 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
|
|||
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);
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void EmulatorWindow::VibrateController(xe::hid::InputSystem* input_sys,
|
||||
uint32_t user_index,
|
||||
bool toggle_rumble) {
|
||||
const std::chrono::milliseconds rumble_duration(100);
|
||||
|
||||
|
@ -1266,12 +1326,12 @@ void EmulatorWindow::VibrateController(xe::hid::InputSystem* input_sys,
|
|||
// otherwise the rumble may fail.
|
||||
auto input_lock = input_sys->lock();
|
||||
|
||||
X_INPUT_VIBRATION vibration;
|
||||
X_INPUT_VIBRATION vibration{};
|
||||
|
||||
vibration.left_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
|
||||
if (toggle_rumble) {
|
||||
|
@ -1286,26 +1346,31 @@ void EmulatorWindow::GamepadHotKeys() {
|
|||
|
||||
auto input_sys = emulator_->input_system();
|
||||
|
||||
// uint8_t users = emulator_->kernel_state()->GetConnectedUsers();
|
||||
// uint8_t users = input_sys->GetConnectedSlots();
|
||||
|
||||
if (input_sys) {
|
||||
// SDL and XInput both support the guide button
|
||||
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) {
|
||||
input_sys->GetState(user_index, &state);
|
||||
for (uint32_t user_index = 0; user_index < MAX_USERS; ++user_index) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1383,6 +1448,8 @@ void EmulatorWindow::DisplayHotKeysConfig() {
|
|||
"Clear Memory Page State: " +
|
||||
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",
|
||||
msg);
|
||||
}
|
||||
|
@ -1396,6 +1463,8 @@ xe::X_STATUS EmulatorWindow::RunTitle(std::filesystem::path path) {
|
|||
|
||||
XELOGE(log_msg);
|
||||
|
||||
imgui_drawer_.get()->ClearDialogs();
|
||||
|
||||
xe::ui::ImGuiDialog::ShowMessageBox(imgui_drawer_.get(),
|
||||
"Title Launch Failed!", log_msg);
|
||||
|
||||
|
|
|
@ -56,7 +56,9 @@ class EmulatorWindow {
|
|||
Emulator* emulator, ui::WindowedAppContext& app_context);
|
||||
|
||||
std::unique_ptr<xe::threading::Thread> Gamepad_HotKeys_Listener;
|
||||
|
||||
|
||||
int selected_title_index = -1;
|
||||
|
||||
Emulator* emulator() const { return emulator_; }
|
||||
ui::WindowedAppContext& app_context() const { return app_context_; }
|
||||
ui::Window* window() const { return window_.get(); }
|
||||
|
@ -73,17 +75,20 @@ class EmulatorWindow {
|
|||
void ToggleFullscreen();
|
||||
void SetInitializingShaderStorage(bool initializing);
|
||||
|
||||
// Types of button functions for hotkeys.
|
||||
// Types of button functions for hotkeys.
|
||||
enum class ButtonFunctions {
|
||||
ToggleFullscreen,
|
||||
RunPreviouslyPlayedTitle,
|
||||
RunTitle,
|
||||
CpuTimeScalarSetHalf,
|
||||
CpuTimeScalarSetDouble,
|
||||
CpuTimeScalarReset,
|
||||
ClearGPUCache,
|
||||
ToggleControllerVibration,
|
||||
ClearMemoryPageState,
|
||||
ReadbackResolve,
|
||||
CloseWindow,
|
||||
IncTitleSelect,
|
||||
DecTitleSelect,
|
||||
Unknown
|
||||
};
|
||||
|
||||
|
@ -113,7 +118,7 @@ class EmulatorWindow {
|
|||
this->rumble = rumble;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
class EmulatorWindowListener final : public ui::WindowListener,
|
||||
public ui::WindowInputListener {
|
||||
|
@ -198,12 +203,13 @@ class EmulatorWindow {
|
|||
void ShowBuildCommit();
|
||||
|
||||
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 ToggleGPUSetting(gpu_cvar index);
|
||||
bool IsUseNexusForGameBarEnabled();
|
||||
void DisplayHotKeysConfig();
|
||||
|
||||
|
||||
xe::X_STATUS RunTitle(std::filesystem::path path);
|
||||
void RunPreviouslyPlayedTitle();
|
||||
void FillRecentlyLaunchedTitlesMenu(xe::ui::MenuItem* recent_menu);
|
||||
|
|
|
@ -109,13 +109,29 @@ void ImGuiDrawer::Initialize() {
|
|||
ImFontConfig font_config;
|
||||
font_config.OversampleH = font_config.OversampleV = 1;
|
||||
font_config.PixelSnapH = true;
|
||||
|
||||
// https://jrgraphix.net/r/Unicode/
|
||||
static const ImWchar font_glyph_ranges[] = {
|
||||
0x0020,
|
||||
0x00FF, // Basic Latin + Latin Supplement
|
||||
0x0020, 0x00FF, // Basic Latin + Latin Supplement
|
||||
0x2000, 0x206F, // General Punctuation
|
||||
0,
|
||||
};
|
||||
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?
|
||||
// https://github.com/Koruri/kibitaki looks really good, but is 1.5MiB.
|
||||
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,
|
||||
UIDrawContext& ui_draw_context) {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
|
|
@ -51,6 +51,8 @@ class ImGuiDrawer : public WindowInputListener, public UIDrawer {
|
|||
}
|
||||
|
||||
void Draw(UIDrawContext& ui_draw_context) override;
|
||||
|
||||
void ClearDialogs();
|
||||
|
||||
protected:
|
||||
void OnKeyDown(KeyEvent& e) override;
|
||||
|
|
Loading…
Reference in New Issue