1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-17 13:29:02 +00:00

Attempts mouse event capture.

This commit is contained in:
Thomas Harte 2020-07-04 00:29:37 -04:00
parent 51f4242e9b
commit cdda3f74ab
4 changed files with 176 additions and 37 deletions

View File

@ -709,30 +709,41 @@ void MainWindow::setUIPhase(UIPhase phase) {
ui->topTipLabel->setVisible(phase == UIPhase::SelectingMachine); ui->topTipLabel->setVisible(phase == UIPhase::SelectingMachine);
// Consider setting a window title, if it's knowable. // Consider setting a window title, if it's knowable.
switch(phase) { setWindowTitle();
case UIPhase::SelectingMachine:
setWindowTitle(tr("Select a machine..."));
break;
case UIPhase::RequestingROMs:
setWindowTitle(tr("Provide ROMs..."));
break;
default:
// Update the window title. TODO: clearly I need a proper functional solution for the window title.
if(openFileName.isEmpty()) {
const auto machineType = targets[0]->machine;
setWindowTitle(QString::fromStdString(Machine::LongNameForTargetMachine(machineType)));
} else {
setWindowTitle(openFileName);
}
break;
}
// Set appropriate focus if necessary; e.g. this ensures that machine-picker // Set appropriate focus if necessary; e.g. this ensures that machine-picker
// widgets aren't still selectable after a machine starts. // widgets aren't still selectable after a machine starts.
if(phase != UIPhase::SelectingMachine) { if(phase != UIPhase::SelectingMachine) {
ui->openGLWidget->setFocus(); ui->openGLWidget->setFocus();
} }
// Indicate whether to catch mouse input.
ui->openGLWidget->setMouseDelegate(
(phase == UIPhase::RunningMachine && machine && machine->mouse_machine()) ? this : nullptr
);
}
void MainWindow::setWindowTitle() {
QString title;
switch(uiPhase) {
case UIPhase::SelectingMachine: title = tr("Select a machine..."); break;
case UIPhase::RequestingROMs: title = tr("Provide ROMs..."); break;
default:
// Update the window title. TODO: clearly I need a proper functional solution for the window title.
if(openFileName.isEmpty()) {
const auto machineType = targets[0]->machine;
title = QString::fromStdString(Machine::LongNameForTargetMachine(machineType));
} else {
title = openFileName;
}
break;
}
if(mouseIsCaptured) title += " (press control+escape to release mouse)";
QMainWindow::setWindowTitle(title);
} }
// MARK: - Event Processing // MARK: - Event Processing
@ -817,6 +828,27 @@ bool MainWindow::processEvent(QKeyEvent *event) {
return false; return false;
} }
void MainWindow::setMouseIsCaptured(bool isCaptured) {
mouseIsCaptured = isCaptured;
setWindowTitle();
}
void MainWindow::moveMouse(QPoint vector) {
std::unique_lock lock(machineMutex);
auto mouseMachine = machine->mouse_machine();
if(!mouseMachine) return;
mouseMachine->get_mouse().move(vector.x(), vector.y());
}
void MainWindow::setButtonPressed(int index, bool isPressed) {
std::unique_lock lock(machineMutex);
auto mouseMachine = machine->mouse_machine();
if(!mouseMachine) return;
mouseMachine->get_mouse().set_button_pressed(index, isPressed);
}
// MARK: - New Machine Creation // MARK: - New Machine Creation
#include "../../Analyser/Static/Acorn/Target.hpp" #include "../../Analyser/Static/Acorn/Target.hpp"

View File

@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; } namespace Ui { class MainWindow; }
QT_END_NAMESPACE QT_END_NAMESPACE
class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegate { class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegate, public ScanTargetWidget::MouseDelegate {
Q_OBJECT Q_OBJECT
void createActions(); void createActions();
@ -35,6 +35,10 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
void keyPressEvent(QKeyEvent *event) override; void keyPressEvent(QKeyEvent *event) override;
void keyReleaseEvent(QKeyEvent *event) override; void keyReleaseEvent(QKeyEvent *event) override;
void setMouseIsCaptured(bool) override;
void moveMouse(QPoint) override;
void setButtonPressed(int index, bool isPressed) override;
private: private:
std::unique_ptr<Ui::MainWindow> ui; std::unique_ptr<Ui::MainWindow> ui;
std::unique_ptr<Timer> timer; std::unique_ptr<Timer> timer;
@ -117,6 +121,9 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
void addAtari2600Menu(); void addAtari2600Menu();
void toggleAtari2600Switch(Atari2600Switch toggleSwitch); void toggleAtari2600Switch(Atari2600Switch toggleSwitch);
void setWindowTitle();
bool mouseIsCaptured = false;
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

View File

@ -1,9 +1,12 @@
#include "scantargetwidget.h" #include "scantargetwidget.h"
#include <QApplication> #include <QApplication>
#include <QCursor>
#include <QDebug> #include <QDebug>
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QGuiApplication> #include <QGuiApplication>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QOpenGLContext> #include <QOpenGLContext>
#include <QScreen> #include <QScreen>
#include <QTimer> #include <QTimer>
@ -21,10 +24,10 @@ void ScanTargetWidget::initializeGL() {
} }
void ScanTargetWidget::paintGL() { void ScanTargetWidget::paintGL() {
if(requested_redraw_time_) { if(requestedRedrawTime) {
const auto now = Time::nanos_now(); const auto now = Time::nanos_now();
vsyncPredictor.add_timer_jitter(now - requested_redraw_time_); vsyncPredictor.add_timer_jitter(now - requestedRedrawTime);
requested_redraw_time_ = 0; requestedRedrawTime = 0;
} }
// TODO: if Qt 5.14 can be guaranteed, just use window()->screen(). // TODO: if Qt 5.14 can be guaranteed, just use window()->screen().
@ -81,27 +84,27 @@ void ScanTargetWidget::vsync() {
vsyncPredictor.announce_vsync(); vsyncPredictor.announce_vsync();
const auto time_now = Time::nanos_now(); const auto time_now = Time::nanos_now();
requested_redraw_time_ = vsyncPredictor.suggested_draw_time(); requestedRedrawTime = vsyncPredictor.suggested_draw_time();
const auto delay_time = (requested_redraw_time_ - time_now) / 1'000'000; const auto delay_time = (requestedRedrawTime - time_now) / 1'000'000;
if(delay_time > 0) { if(delay_time > 0) {
QTimer::singleShot(delay_time, this, SLOT(repaint())); QTimer::singleShot(delay_time, this, SLOT(repaint()));
} else { } else {
requested_redraw_time_ = 0; requestedRedrawTime = 0;
repaint(); repaint();
} }
} }
void ScanTargetWidget::resizeGL(int w, int h) { void ScanTargetWidget::resizeGL(int w, int h) {
if(width != w || height != h) { if(rawWidth != w || rawHeight != h) {
width = w; rawWidth = w;
height = h; rawHeight = h;
resize(); resize();
} }
} }
void ScanTargetWidget::resize() { void ScanTargetWidget::resize() {
const int newScaledWidth = int(float(width) * outputScale); const int newScaledWidth = int(float(rawWidth) * outputScale);
const int newScaledHeight = int(float(height) * outputScale); const int newScaledHeight = int(float(rawHeight) * outputScale);
if(newScaledWidth != scaledWidth || newScaledHeight != scaledHeight) { if(newScaledWidth != scaledWidth || newScaledHeight != scaledHeight) {
scaledWidth = newScaledWidth; scaledWidth = newScaledWidth;
@ -121,7 +124,7 @@ void ScanTargetWidget::stop() {
isConnected = false; isConnected = false;
setDefaultClearColour(); setDefaultClearColour();
vsyncPredictor.pause(); vsyncPredictor.pause();
requested_redraw_time_ = 0; requestedRedrawTime = 0;
repaint(); repaint();
} }
@ -130,3 +133,76 @@ void ScanTargetWidget::setDefaultClearColour() {
const QColor backgroundColour = palette().color(QWidget::backgroundRole()); const QColor backgroundColour = palette().color(QWidget::backgroundRole());
glClearColor(backgroundColour.redF(), backgroundColour.greenF(), backgroundColour.blueF(), 1.0); glClearColor(backgroundColour.redF(), backgroundColour.greenF(), backgroundColour.blueF(), 1.0);
} }
void ScanTargetWidget::setMouseDelegate(MouseDelegate *delegate) {
if(!delegate && mouseIsCaptured) {
releaseMouse();
}
mouseDelegate = delegate;
setMouseTracking(delegate);
}
void ScanTargetWidget::keyPressEvent(QKeyEvent *event) {
if(mouseIsCaptured && event->key() == Qt::Key_Escape && event->modifiers()&Qt::ControlModifier) {
releaseMouse();
QCursor cursor;
cursor.setShape(Qt::ArrowCursor);
setCursor(cursor);
}
}
void ScanTargetWidget::releaseMouse() {
QOpenGLWidget::releaseMouse();
mouseIsCaptured = false;
mouseDelegate->setMouseIsCaptured(false);
}
void ScanTargetWidget::mousePressEvent(QMouseEvent *event) {
if(mouseDelegate) {
if(!mouseIsCaptured) {
mouseIsCaptured = true;
grabMouse();
QCursor cursor;
cursor.setPos(mapToGlobal(QPoint(width() / 2, height() / 2)));
cursor.setShape(Qt::BlankCursor);
setCursor(cursor);
mouseDelegate->setMouseIsCaptured(true);
} else {
setMouseButtonPressed(event->button(), true);
}
}
}
void ScanTargetWidget::mouseReleaseEvent(QMouseEvent *event) {
if(mouseDelegate && !mouseIsCaptured) {
setMouseButtonPressed(event->button(), false);
}
}
void ScanTargetWidget::setMouseButtonPressed(Qt::MouseButton button, bool isPressed) {
switch(button) {
default: break;
case Qt::LeftButton: mouseDelegate->setButtonPressed(0, isPressed); break;
case Qt::RightButton: mouseDelegate->setButtonPressed(1, isPressed); break;
case Qt::MiddleButton: mouseDelegate->setButtonPressed(2, isPressed); break;
}
}
void ScanTargetWidget::mouseMoveEvent(QMouseEvent *event) {
// Recentre the mouse cursor upon every move if it is currently captured.
if(mouseDelegate && mouseIsCaptured) {
const QPoint centre = QPoint(width() / 2, height() / 2);
const QPoint vector = event->pos() - centre;
mouseDelegate->moveMouse(vector);
QCursor::setPos(mapToGlobal(centre));
}
}
bool ScanTargetWidget::isMouseCaptured() {
return mouseIsCaptured;
}

View File

@ -8,23 +8,44 @@
#include "../../ClockReceiver/VSyncPredictor.hpp" #include "../../ClockReceiver/VSyncPredictor.hpp"
class ScanTargetWidget : public QOpenGLWidget class ScanTargetWidget : public QOpenGLWidget {
{
public: public:
ScanTargetWidget(QWidget *parent = nullptr); ScanTargetWidget(QWidget *parent = nullptr);
~ScanTargetWidget(); ~ScanTargetWidget();
// Sets the current scan producer; this scan producer will be /// Sets the current scan producer; this scan producer will be
// handed a suitable scan target as soon as one exists. /// handed a suitable scan target as soon as one exists.
void setScanProducer(MachineTypes::ScanProducer *); void setScanProducer(MachineTypes::ScanProducer *);
/// Destructs the current scan target
void stop(); void stop();
struct MouseDelegate {
virtual void setMouseIsCaptured(bool) = 0;
virtual void moveMouse(QPoint) = 0;
virtual void setButtonPressed(int index, bool isPressed) = 0;
};
/// If a delegate is assigned then this widget will respond to clicks by capturing
/// the mouse, unless and until either ::stop() is called or ctrl+escape is pressed.
/// Mouse events can be tracked by the main window while the mouse is captured.
void setMouseDelegate(MouseDelegate *);
/// @returns @c true if the mouse is currently captured; @c false otherwise.
bool isMouseCaptured();
protected: protected:
void initializeGL() override; void initializeGL() override;
void resizeGL(int w, int h) override; void resizeGL(int w, int h) override;
void paintGL() override; void paintGL() override;
void mousePressEvent(QMouseEvent *) override;
void mouseReleaseEvent(QMouseEvent *) override;
void mouseMoveEvent(QMouseEvent *) override;
void keyPressEvent(QKeyEvent *) override;
void releaseMouse();
void setMouseButtonPressed(Qt::MouseButton, bool);
private: private:
// This should be created only once there's an OpenGL context. So it // This should be created only once there's an OpenGL context. So it
// can't be done at creation time. // can't be done at creation time.
@ -34,15 +55,18 @@ class ScanTargetWidget : public QOpenGLWidget
GLuint framebuffer = 0; GLuint framebuffer = 0;
MachineTypes::ScanProducer *producer = nullptr; MachineTypes::ScanProducer *producer = nullptr;
Time::Nanos requested_redraw_time_ = 0; Time::Nanos requestedRedrawTime = 0;
void setDefaultClearColour(); void setDefaultClearColour();
int width = 0, height = 0; int rawWidth = 0, rawHeight = 0;
int scaledWidth = 0, scaledHeight = 0; int scaledWidth = 0, scaledHeight = 0;
float outputScale = 1.0f; float outputScale = 1.0f;
void resize(); void resize();
MouseDelegate *mouseDelegate = nullptr;
bool mouseIsCaptured = false;
private slots: private slots:
void vsync(); void vsync();
}; };