imgui/renderer: Hide Cursor on Idle Implementation (#1266)

Implement hide cursor on idle w/ idle timeout duration (configurable via GUI). While at it add always and never to hide the cursor options as well.

* Revert commit #1211 as to not interfere with the cursor states.
* Make hide cursor on idle as the default setting w/ timeout duration of 5 seconds to hide.
* Add an input tab in the settings page to add the hide cursor setting, with hiding the idle timeout box with respect to the cursor hide option.

Co-authored-by: georgemoralis <giorgosmrls@gmail.com>
This commit is contained in:
Exhigh 2024-10-08 10:47:42 +04:00 committed by GitHub
parent 96344873d6
commit f139762c64
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 296 additions and 12 deletions

View file

@ -57,6 +57,8 @@ static bool vkValidationGpu = false;
static bool rdocEnable = false;
static bool vkMarkers = false;
static bool vkCrashDiagnostic = false;
static s16 cursorState = HideCursorState::Idle;
static int cursorHideTimeout = 5; // 5 seconds (default)
// Gui
std::filesystem::path settings_install_dir = {};
@ -96,6 +98,14 @@ int getBGMvolume() {
return BGMvolume;
}
s16 getCursorState() {
return cursorState;
}
int getCursorHideTimeout() {
return cursorHideTimeout;
}
u32 getScreenWidth() {
return screenWidth;
}
@ -256,6 +266,14 @@ void setBGMvolume(int volume) {
BGMvolume = volume;
}
void setCursorState(s16 newCursorState) {
cursorState = newCursorState;
}
void setCursorHideTimeout(int newcursorHideTimeout) {
cursorHideTimeout = newcursorHideTimeout;
}
void setLanguage(u32 language) {
m_language = language;
}
@ -450,6 +468,8 @@ void load(const std::filesystem::path& path) {
if (data.contains("Input")) {
const toml::value& input = data.at("Input");
cursorState = toml::find_or<int>(input, "cursorState", HideCursorState::Idle);
cursorHideTimeout = toml::find_or<int>(input, "cursorHideTimeout", 5);
useSpecialPad = toml::find_or<bool>(input, "useSpecialPad", false);
specialPadClass = toml::find_or<int>(input, "specialPadClass", 1);
}
@ -543,6 +563,8 @@ void save(const std::filesystem::path& path) {
data["General"]["updateChannel"] = updateChannel;
data["General"]["showSplash"] = isShowSplash;
data["General"]["autoUpdate"] = isAutoUpdate;
data["Input"]["cursorState"] = cursorState;
data["Input"]["cursorHideTimeout"] = cursorHideTimeout;
data["General"]["backButtonBehavior"] = backButtonBehavior;
data["Input"]["useSpecialPad"] = useSpecialPad;
data["Input"]["specialPadClass"] = specialPadClass;
@ -592,6 +614,8 @@ void setDefaultValues() {
isFullscreen = false;
playBGM = false;
BGMvolume = 50;
cursorState = HideCursorState::Idle;
cursorHideTimeout = 5;
screenWidth = 1280;
screenHeight = 720;
logFilter = "";

View file

@ -8,6 +8,9 @@
#include "types.h"
namespace Config {
enum HideCursorState : s16 { Never, Idle, Always };
void load(const std::filesystem::path& path);
void save(const std::filesystem::path& path);
@ -16,6 +19,9 @@ bool isFullscreenMode();
bool getPlayBGM();
int getBGMvolume();
s16 getCursorState();
int getCursorHideTimeout();
std::string getLogFilter();
std::string getLogType();
std::string getUserName();
@ -51,6 +57,8 @@ void setScreenHeight(u32 height);
void setFullscreenMode(bool enable);
void setPlayBGM(bool enable);
void setBGMvolume(int volume);
void setCursorState(s16 cursorState);
void setCursorHideTimeout(int newcursorHideTimeout);
void setLanguage(u32 language);
void setNeoMode(bool enable);
void setUserName(const std::string& type);

View file

@ -4,6 +4,7 @@
// Based on imgui_impl_sdl3.cpp from Dear ImGui repository
#include <imgui.h>
#include "common/config.h"
#include "imgui_impl_sdl3.h"
// SDL
@ -36,6 +37,8 @@ struct SdlData {
SDL_Cursor* mouse_cursors[ImGuiMouseCursor_COUNT]{};
SDL_Cursor* mouse_last_cursor{};
int mouse_pending_leave_frame{};
ImVec2 prev_mouse_pos{0, 0};
Uint64 lastCursorMoveTime{};
// Gamepad handling
ImVector<SDL_Gamepad*> gamepads{};
@ -371,6 +374,13 @@ bool ProcessEvent(const SDL_Event* event) {
? ImGuiMouseSource_TouchScreen
: ImGuiMouseSource_Mouse);
io.AddMousePosEvent(mouse_pos.x, mouse_pos.y);
if (mouse_pos.x != bd->prev_mouse_pos.x || mouse_pos.y != bd->prev_mouse_pos.y) {
bd->prev_mouse_pos.x = mouse_pos.x;
bd->prev_mouse_pos.y = mouse_pos.y;
if (Config::getCursorState() == Config::HideCursorState::Idle) {
bd->lastCursorMoveTime = bd->time;
}
}
return true;
}
case SDL_EVENT_MOUSE_WHEEL: {
@ -447,6 +457,7 @@ bool ProcessEvent(const SDL_Event* event) {
return false;
bd->mouse_window_id = event->window.windowID;
bd->mouse_pending_leave_frame = 0;
bd->lastCursorMoveTime = bd->time;
return true;
}
// - In some cases, when detaching a window from main viewport SDL may send
@ -459,6 +470,7 @@ bool ProcessEvent(const SDL_Event* event) {
if (GetViewportForWindowId(event->window.windowID) == NULL)
return false;
bd->mouse_pending_leave_frame = ImGui::GetFrameCount() + 1;
bd->lastCursorMoveTime = bd->time;
return true;
}
case SDL_EVENT_WINDOW_FOCUS_GAINED:
@ -600,7 +612,18 @@ static void UpdateMouseData() {
int window_x, window_y;
SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global);
SDL_GetWindowPosition(focused_window, &window_x, &window_y);
io.AddMousePosEvent(mouse_x_global - (float)window_x, mouse_y_global - (float)window_y);
mouse_x_global -= (float)window_x;
mouse_y_global -= (float)window_y;
io.AddMousePosEvent(mouse_x_global, mouse_y_global);
// SDL_EVENT_MOUSE_MOTION isn't triggered before the first frame is rendered
// force update the prev_cursor coords
if (mouse_x_global != bd->prev_mouse_pos.x || mouse_y_global != bd->prev_mouse_pos.y &&
bd->prev_mouse_pos.y == 0 &&
bd->prev_mouse_pos.x == 0) {
bd->prev_mouse_pos.x = mouse_x_global;
bd->prev_mouse_pos.y = mouse_y_global;
bd->lastCursorMoveTime = bd->time;
}
}
}
}
@ -611,10 +634,25 @@ static void UpdateMouseCursor() {
return;
SdlData* bd = GetBackendData();
s16 cursorState = Config::getCursorState();
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) {
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None ||
cursorState == Config::HideCursorState::Always) {
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
SDL_HideCursor();
} else if (cursorState == Config::HideCursorState::Idle &&
bd->time - bd->lastCursorMoveTime >=
Config::getCursorHideTimeout() * SDL_GetPerformanceFrequency()) {
bool wasCursorVisible = SDL_CursorVisible();
SDL_HideCursor();
if (wasCursorVisible) {
SDL_WarpMouseInWindow(SDL_GetKeyboardFocus(), bd->prev_mouse_pos.x,
bd->prev_mouse_pos.y); // Force refresh the cursor state
}
} else {
// Show OS mouse cursor
SDL_Cursor* expected_cursor = bd->mouse_cursors[imgui_cursor]

View file

@ -47,6 +47,8 @@ QStringList languageNames = {"Arabic",
const QVector<int> languageIndexes = {21, 23, 14, 6, 18, 1, 12, 22, 2, 4, 25, 24, 29, 5, 0,
9, 15, 16, 17, 7, 26, 8, 11, 20, 3, 13, 27, 10, 19, 28};
QStringList hideCursorStates = {"Never", "Idle", "Always"};
SettingsDialog::SettingsDialog(std::span<const QString> physical_devices, QWidget* parent)
: QDialog(parent), ui(new Ui::SettingsDialog) {
ui->setupUi(this);
@ -67,6 +69,8 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices, QWidge
completer->setCaseSensitivity(Qt::CaseInsensitive);
ui->consoleLanguageComboBox->setCompleter(completer);
ui->hideCursorComboBox->addItems(hideCursorStates);
InitializeEmulatorLanguages();
LoadValuesFromConfig();
@ -170,6 +174,18 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices, QWidge
});
}
// Input TAB
{
connect(ui->hideCursorComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
[this](s16 index) {
Config::setCursorState(index);
OnCursorStateChanged(index);
});
connect(ui->idleTimeoutSpinBox, &QSpinBox::valueChanged, this,
[](int index) { Config::setCursorHideTimeout(index); });
}
// GPU TAB
{
// First options is auto selection -1, so gpuId on the GUI will always have to subtract 1
@ -246,6 +262,9 @@ void SettingsDialog::LoadValuesFromConfig() {
std::find(languageIndexes.begin(), languageIndexes.end(), Config::GetLanguage())) %
languageIndexes.size());
ui->emulatorLanguageComboBox->setCurrentIndex(languages[Config::getEmulatorLanguage()]);
ui->hideCursorComboBox->setCurrentIndex(Config::getCursorState());
OnCursorStateChanged(Config::getCursorState());
ui->idleTimeoutSpinBox->setValue(Config::getCursorHideTimeout());
ui->graphicsAdapterBox->setCurrentIndex(Config::getGpuId() + 1);
ui->widthSpinBox->setValue(Config::getScreenWidth());
ui->heightSpinBox->setValue(Config::getScreenHeight());
@ -311,6 +330,18 @@ void SettingsDialog::OnLanguageChanged(int index) {
emit LanguageChanged(ui->emulatorLanguageComboBox->itemData(index).toString().toStdString());
}
void SettingsDialog::OnCursorStateChanged(s16 index) {
if (index == -1)
return;
if (index == Config::HideCursorState::Idle) {
ui->idleTimeoutGroupBox->show();
} else {
if (!ui->idleTimeoutGroupBox->isHidden()) {
ui->idleTimeoutGroupBox->hide();
}
}
}
int SettingsDialog::exec() {
return QDialog::exec();
}

View file

@ -33,6 +33,7 @@ private:
void LoadValuesFromConfig();
void InitializeEmulatorLanguages();
void OnLanguageChanged(int index);
void OnCursorStateChanged(s16 index);
std::unique_ptr<Ui::SettingsDialog> ui;

View file

@ -34,9 +34,18 @@
<iconset>
<normaloff>:/images/shadps4.ico</normaloff>:/images/shadps4.ico</iconset>
</property>
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
<property name="modal">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="settingsDialogLayout">
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="enabled">
<bool>true</bool>
</property>
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
@ -51,8 +60,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>836</width>
<height>446</height>
<width>832</width>
<height>431</height>
</rect>
</property>
<property name="sizePolicy">
@ -62,7 +71,7 @@
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<widget class="QWidget" name="generalTab">
<attribute name="title">
@ -369,7 +378,7 @@
<x>10</x>
<y>30</y>
<width>241</width>
<height>71</height>
<height>92</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@ -485,6 +494,185 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="inputTab">
<attribute name="title">
<string>Input</string>
</attribute>
<layout class="QVBoxLayout" name="inputTabVLayout" stretch="0,0">
<item>
<layout class="QHBoxLayout" name="inputTabHLayoutTop" stretch="1,1,1">
<item>
<layout class="QVBoxLayout" name="cursorTabLayoutLeft">
<item>
<widget class="QGroupBox" name="HideCursor">
<property name="title">
<string>Cursor</string>
</property>
<layout class="QVBoxLayout" name="inputCursorLayout">
<item>
<widget class="QGroupBox" name="hideCursorGroupBox">
<property name="title">
<string>Hide Cursor</string>
</property>
<layout class="QVBoxLayout" name="hideCursorLayout">
<item>
<widget class="QComboBox" name="hideCursorComboBox"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="idleTimeoutGroupBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>85</height>
</size>
</property>
<property name="title">
<string>Hide Cursor Idle Timeout</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
</property>
<property name="flat">
<bool>false</bool>
</property>
<layout class="QHBoxLayout" name="IdleTimeoutLayout" stretch="0,0">
<property name="leftMargin">
<number>70</number>
</property>
<property name="topMargin">
<number>11</number>
</property>
<item>
<widget class="QSpinBox" name="idleTimeoutSpinBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LayoutDirection::LeftToRight</enum>
</property>
<property name="wrapping">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::UpDownArrows</enum>
</property>
<property name="suffix">
<string notr="true"/>
</property>
<property name="maximum">
<number>3600</number>
</property>
<property name="value">
<number>5</number>
</property>
<property name="displayIntegerBase">
<number>10</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="idleTimeoutDurationLabel">
<property name="text">
<string>s</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="emptyTabLayoutMiddle">
<item>
<spacer name="emptyHorizontalSpacerMiddle">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="emptyTabLayoutRight">
<item>
<spacer name="emptyHorizontalSpacerRight">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="inputTabHLayoutBottom">
<property name="topMargin">
<number>0</number>
</property>
<item>
<spacer name="emptyVerticalSpacerBottom">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="grphicsTab">
<attribute name="title">
<string>Graphics</string>

View file

@ -313,9 +313,6 @@ void WindowSDL::onKeyPress(const SDL_Event* event) {
if (axis != Input::Axis::AxisMax) {
controller->Axis(0, axis, ax);
}
if (SDL_GetCursor() != NULL) {
SDL_HideCursor();
}
}
void WindowSDL::onGamepadEvent(const SDL_Event* event) {
@ -354,9 +351,6 @@ void WindowSDL::onGamepadEvent(const SDL_Event* event) {
controller->CheckButton(0, button, event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN);
}
}
if (SDL_GetCursor() != NULL) {
SDL_HideCursor();
}
break;
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
axis = event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFTX ? Input::Axis::LeftX