Qt: Implement relative mouse mode

This commit is contained in:
Connor McLaughlin 2020-12-27 14:08:13 +10:00
parent 114d4a2c1d
commit ca42d027ac
13 changed files with 116 additions and 8 deletions

View file

@ -47,7 +47,7 @@ float Controller::GetVibrationMotorStrength(u32 motor)
void Controller::LoadSettings(const char* section) {} void Controller::LoadSettings(const char* section) {}
bool Controller::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale) bool Controller::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode)
{ {
return false; return false;
} }

View file

@ -64,7 +64,7 @@ public:
virtual void LoadSettings(const char* section); virtual void LoadSettings(const char* section);
/// Returns the software cursor to use for this controller, if any. /// Returns the software cursor to use for this controller, if any.
virtual bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale); virtual bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode);
/// Creates a new controller of the specified type. /// Creates a new controller of the specified type.
static std::unique_ptr<Controller> Create(ControllerType type, u32 index); static std::unique_ptr<Controller> Create(ControllerType type, u32 index);

View file

@ -917,19 +917,27 @@ void HostInterface::UpdateSoftwareCursor()
{ {
if (System::IsShutdown()) if (System::IsShutdown())
{ {
SetMouseMode(false, false);
m_display->ClearSoftwareCursor(); m_display->ClearSoftwareCursor();
return; return;
} }
const Common::RGBA8Image* image = nullptr; const Common::RGBA8Image* image = nullptr;
float image_scale = 1.0f; float image_scale = 1.0f;
bool relative_mode = false;
bool hide_cursor = false;
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{ {
Controller* controller = System::GetController(i); Controller* controller = System::GetController(i);
if (controller && controller->GetSoftwareCursor(&image, &image_scale)) if (controller && controller->GetSoftwareCursor(&image, &image_scale, &relative_mode))
{
hide_cursor = true;
break; break;
} }
}
SetMouseMode(relative_mode, hide_cursor);
if (image && image->IsValid()) if (image && image->IsValid())
{ {
@ -967,6 +975,8 @@ void HostInterface::RecreateSystem()
System::ResetPerformanceCounters(); System::ResetPerformanceCounters();
} }
void HostInterface::SetMouseMode(bool relative, bool hide_cursor) {}
void HostInterface::DisplayLoadingScreen(const char* message, int progress_min /*= -1*/, int progress_max /*= -1*/, void HostInterface::DisplayLoadingScreen(const char* message, int progress_min /*= -1*/, int progress_max /*= -1*/,
int progress_value /*= -1*/) int progress_value /*= -1*/)
{ {

View file

@ -175,6 +175,9 @@ protected:
/// Switches the GPU renderer by saving state, recreating the display window, and restoring state (if needed). /// Switches the GPU renderer by saving state, recreating the display window, and restoring state (if needed).
virtual void RecreateSystem(); virtual void RecreateSystem();
/// Enables "relative" mouse mode, locking the cursor position and returning relative coordinates.
virtual void SetMouseMode(bool relative, bool hide_cursor);
/// Sets the user directory to the program directory, i.e. "portable mode". /// Sets the user directory to the program directory, i.e. "portable mode".
void SetUserDirectoryToProgramDirectory(); void SetUserDirectoryToProgramDirectory();

View file

@ -280,12 +280,13 @@ void NamcoGunCon::LoadSettings(const char* section)
m_x_scale = g_host_interface->GetFloatSettingValue(section, "XScale", 1.0f); m_x_scale = g_host_interface->GetFloatSettingValue(section, "XScale", 1.0f);
} }
bool NamcoGunCon::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale) bool NamcoGunCon::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode)
{ {
if (!m_crosshair_image.IsValid()) if (!m_crosshair_image.IsValid())
return false; return false;
*image = &m_crosshair_image; *image = &m_crosshair_image;
*image_scale = m_crosshair_image_scale; *image_scale = m_crosshair_image_scale;
*relative_mode = false;
return true; return true;
} }

View file

@ -34,7 +34,7 @@ public:
void Reset() override; void Reset() override;
bool DoState(StateWrapper& sw, bool apply_input_state) override; bool DoState(StateWrapper& sw, bool apply_input_state) override;
void LoadSettings(const char* section) override; void LoadSettings(const char* section) override;
bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale) override; bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode) override;
void SetAxisState(s32 axis_code, float value) override; void SetAxisState(s32 axis_code, float value) override;
void SetButtonState(s32 button_code, bool pressed) override; void SetButtonState(s32 button_code, bool pressed) override;

View file

@ -39,6 +39,8 @@ public:
void SetButtonState(Button button, bool pressed); void SetButtonState(Button button, bool pressed);
bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode) override;
private: private:
void UpdatePosition(); void UpdatePosition();

View file

@ -290,6 +290,22 @@ void MainWindow::focusDisplayWidget()
m_display_widget->setFocus(); m_display_widget->setFocus();
} }
void MainWindow::onMouseModeRequested(bool relative_mode, bool hide_cursor)
{
if (!m_display_widget)
return;
const bool paused = System::IsPaused();
if (hide_cursor)
m_display_widget->setCursor(Qt::BlankCursor);
else
m_display_widget->unsetCursor();
m_relative_mouse_mode = relative_mode;
m_display_widget->setRelativeMode(!paused && relative_mode);
}
void MainWindow::onEmulationStarting() void MainWindow::onEmulationStarting()
{ {
m_emulation_running = true; m_emulation_running = true;
@ -327,6 +343,9 @@ void MainWindow::onEmulationPaused(bool paused)
{ {
QSignalBlocker blocker(m_ui.actionPause); QSignalBlocker blocker(m_ui.actionPause);
m_ui.actionPause->setChecked(paused); m_ui.actionPause->setChecked(paused);
if (m_display_widget)
m_display_widget->setRelativeMode(!paused && m_relative_mouse_mode);
} }
void MainWindow::onStateSaved(const QString& game_code, bool global, qint32 slot) void MainWindow::onStateSaved(const QString& game_code, bool global, qint32 slot)
@ -372,6 +391,9 @@ void MainWindow::onApplicationStateChanged(Qt::ApplicationState state)
{ {
m_host_interface->pauseSystem(true); m_host_interface->pauseSystem(true);
m_was_paused_by_focus_loss = true; m_was_paused_by_focus_loss = true;
if (m_display_widget)
m_display_widget->setRelativeMode(false);
} }
} }
else else
@ -381,6 +403,9 @@ void MainWindow::onApplicationStateChanged(Qt::ApplicationState state)
if (System::IsPaused()) if (System::IsPaused())
m_host_interface->pauseSystem(false); m_host_interface->pauseSystem(false);
m_was_paused_by_focus_loss = false; m_was_paused_by_focus_loss = false;
if (m_display_widget)
m_display_widget->setRelativeMode(m_relative_mouse_mode);
} }
} }
} }
@ -974,6 +999,7 @@ void MainWindow::connectSignals()
&MainWindow::onSystemPerformanceCountersUpdated); &MainWindow::onSystemPerformanceCountersUpdated);
connect(m_host_interface, &QtHostInterface::runningGameChanged, this, &MainWindow::onRunningGameChanged); connect(m_host_interface, &QtHostInterface::runningGameChanged, this, &MainWindow::onRunningGameChanged);
connect(m_host_interface, &QtHostInterface::exitRequested, this, &MainWindow::close); connect(m_host_interface, &QtHostInterface::exitRequested, this, &MainWindow::close);
connect(m_host_interface, &QtHostInterface::mouseModeRequested, this, &MainWindow::onMouseModeRequested);
// These need to be queued connections to stop crashing due to menus opening/closing and switching focus. // These need to be queued connections to stop crashing due to menus opening/closing and switching focus.
connect(m_game_list_widget, &GameListWidget::entrySelected, this, &MainWindow::onGameListEntrySelected, connect(m_game_list_widget, &GameListWidget::entrySelected, this, &MainWindow::onGameListEntrySelected,

View file

@ -52,6 +52,7 @@ private Q_SLOTS:
void displaySizeRequested(qint32 width, qint32 height); void displaySizeRequested(qint32 width, qint32 height);
void destroyDisplay(); void destroyDisplay();
void focusDisplayWidget(); void focusDisplayWidget();
void onMouseModeRequested(bool relative_mode, bool hide_cursor);
void setTheme(const QString& theme); void setTheme(const QString& theme);
void updateTheme(); void updateTheme();
@ -148,6 +149,7 @@ private:
bool m_emulation_running = false; bool m_emulation_running = false;
bool m_was_paused_by_focus_loss = false; bool m_was_paused_by_focus_loss = false;
bool m_open_debugger_on_start = false; bool m_open_debugger_on_start = false;
bool m_relative_mouse_mode = false;
GDBServer* m_gdb_server = nullptr; GDBServer* m_gdb_server = nullptr;
}; };

View file

@ -92,6 +92,29 @@ std::optional<WindowInfo> QtDisplayWidget::getWindowInfo() const
return wi; return wi;
} }
void QtDisplayWidget::setRelativeMode(bool enabled)
{
if (m_relative_mouse_enabled == enabled)
return;
if (enabled)
{
m_relative_mouse_start_position = QCursor::pos();
const QPoint center_pos = mapToGlobal(QPoint(width() / 2, height() / 2));
QCursor::setPos(center_pos);
m_relative_mouse_last_position = center_pos;
grabMouse();
}
else
{
QCursor::setPos(m_relative_mouse_start_position);
releaseMouse();
}
m_relative_mouse_enabled = enabled;
}
QPaintEngine* QtDisplayWidget::paintEngine() const QPaintEngine* QtDisplayWidget::paintEngine() const
{ {
return nullptr; return nullptr;
@ -113,10 +136,35 @@ bool QtDisplayWidget::event(QEvent* event)
case QEvent::MouseMove: case QEvent::MouseMove:
{ {
const qreal dpr = devicePixelRatioFromScreen();
const QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event); const QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
emit windowMouseMoveEvent(static_cast<int>(static_cast<double>(mouse_event->x()) * dpr),
static_cast<int>(static_cast<double>(mouse_event->y()) * dpr)); if (!m_relative_mouse_enabled)
{
const qreal dpr = devicePixelRatioFromScreen();
const int scaled_x = static_cast<int>(static_cast<qreal>(mouse_event->x()) * dpr);
const int scaled_y = static_cast<int>(static_cast<qreal>(mouse_event->y()) * dpr);
windowMouseMoveEvent(scaled_x, scaled_y);
}
else
{
const QPoint center_pos = mapToGlobal(QPoint((width() + 1) / 2, (height() + 1) / 2));
const QPoint mouse_pos = mapToGlobal(mouse_event->pos());
const int dx = mouse_pos.x() - center_pos.x();
const int dy = mouse_pos.y() - center_pos.y();
m_relative_mouse_last_position.setX(m_relative_mouse_last_position.x() + dx);
m_relative_mouse_last_position.setY(m_relative_mouse_last_position.y() + dy);
windowMouseMoveEvent(m_relative_mouse_last_position.x(), m_relative_mouse_last_position.y());
QCursor::setPos(center_pos);
#if 0
qCritical() << "center" << center_pos.x() << "," << center_pos.y();
qCritical() << "mouse" << mouse_pos.x() << "," << mouse_pos.y();
qCritical() << "dxdy" << dx << "," << dy;
#endif
}
return true; return true;
} }

View file

@ -20,6 +20,8 @@ public:
std::optional<WindowInfo> getWindowInfo() const; std::optional<WindowInfo> getWindowInfo() const;
void setRelativeMode(bool enabled);
Q_SIGNALS: Q_SIGNALS:
void windowResizedEvent(int width, int height); void windowResizedEvent(int width, int height);
void windowRestoredEvent(); void windowRestoredEvent();
@ -30,4 +32,9 @@ Q_SIGNALS:
protected: protected:
bool event(QEvent* event) override; bool event(QEvent* event) override;
private:
QPoint m_relative_mouse_start_position{};
QPoint m_relative_mouse_last_position{};
bool m_relative_mouse_enabled = false;
}; };

View file

@ -577,6 +577,7 @@ void QtHostInterface::updateDisplayState()
if (!System::IsShutdown()) if (!System::IsShutdown())
{ {
g_gpu->UpdateResolutionScale(); g_gpu->UpdateResolutionScale();
UpdateSoftwareCursor();
redrawDisplayWindow(); redrawDisplayWindow();
} }
UpdateSpeedLimiterState(); UpdateSpeedLimiterState();
@ -736,6 +737,11 @@ void QtHostInterface::UpdateInputMap()
updateInputMap(); updateInputMap();
} }
void QtHostInterface::SetMouseMode(bool relative, bool hide_cursor)
{
emit mouseModeRequested(relative, hide_cursor);
}
void QtHostInterface::updateInputMap() void QtHostInterface::updateInputMap()
{ {
if (!isOnWorkerThread()) if (!isOnWorkerThread())

View file

@ -139,6 +139,7 @@ Q_SIGNALS:
void runningGameChanged(const QString& filename, const QString& game_code, const QString& game_title); void runningGameChanged(const QString& filename, const QString& game_code, const QString& game_title);
void exitRequested(); void exitRequested();
void inputProfileLoaded(); void inputProfileLoaded();
void mouseModeRequested(bool relative, bool hide_cursor);
public Q_SLOTS: public Q_SLOTS:
void setDefaultSettings(); void setDefaultSettings();
@ -203,6 +204,8 @@ protected:
void SetDefaultSettings(SettingsInterface& si) override; void SetDefaultSettings(SettingsInterface& si) override;
void UpdateInputMap() override; void UpdateInputMap() override;
void SetMouseMode(bool relative, bool hide_cursor) override;
private: private:
enum : u32 enum : u32
{ {