implemented touch in Qt and SDL
change TouchToPixelPos to return std::pair<int, int> static_cast (SDL) various minor style and code improvements style - PascalCase for function names made touch events private const pointer arg in touch events make TouchToPixelPos a const member function did I do this right? braces on barely-multiline if remove question comment (confirmed in Discord) fixed consts remove unused parameter from TouchEndEvent DRY - High-DPI scaled touch put in separate function also fixes a bug where if you start touching (with either mouse or touchscreen) and drag the mouse to the LEFT of the emulator window, the touch point jumps to the RIGHT side of the touchscreen; draggin to above the window would make it jump to the bottom. implicit conversion from QPoint to QPointF, apparently I have no idea what const even means but I'll put it here anyway remove unused or used-once variables make touch scaling functions const, and put their implementations together removed unused FingerID parameters QTouchEvent forward declaration; add comment to TouchBegin that was lost in an edit better DRY in SDL To do -> TODO(NeatNit) remove unused include
This commit is contained in:
parent
f405134913
commit
4ee914c7a8
4 changed files with 131 additions and 10 deletions
|
@ -41,6 +41,35 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, float touch_y) const {
|
||||||
|
int w, h;
|
||||||
|
SDL_GetWindowSize(render_window, &w, &h);
|
||||||
|
|
||||||
|
touch_x *= w;
|
||||||
|
touch_y *= h;
|
||||||
|
|
||||||
|
return {static_cast<unsigned>(std::max(std::round(touch_x), 0.0f)),
|
||||||
|
static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))};
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmuWindow_SDL2::OnFingerDown(float x, float y) {
|
||||||
|
// TODO(NeatNit): keep track of multitouch using the fingerID and a dictionary of some kind
|
||||||
|
// This isn't critical because the best we can do when we have that is to average them, like the
|
||||||
|
// 3DS does
|
||||||
|
|
||||||
|
const auto [px, py] = TouchToPixelPos(x, y);
|
||||||
|
TouchPressed(px, py);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmuWindow_SDL2::OnFingerMotion(float x, float y) {
|
||||||
|
const auto [px, py] = TouchToPixelPos(x, y);
|
||||||
|
TouchMoved(px, py);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmuWindow_SDL2::OnFingerUp() {
|
||||||
|
TouchReleased();
|
||||||
|
}
|
||||||
|
|
||||||
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
|
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
|
||||||
if (state == SDL_PRESSED) {
|
if (state == SDL_PRESSED) {
|
||||||
InputCommon::GetKeyboard()->PressKey(key);
|
InputCommon::GetKeyboard()->PressKey(key);
|
||||||
|
@ -178,11 +207,26 @@ void EmuWindow_SDL2::PollEvents() {
|
||||||
OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state);
|
OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state);
|
||||||
break;
|
break;
|
||||||
case SDL_MOUSEMOTION:
|
case SDL_MOUSEMOTION:
|
||||||
OnMouseMotion(event.motion.x, event.motion.y);
|
// ignore if it came from touch
|
||||||
|
if (event.button.which != SDL_TOUCH_MOUSEID)
|
||||||
|
OnMouseMotion(event.motion.x, event.motion.y);
|
||||||
break;
|
break;
|
||||||
case SDL_MOUSEBUTTONDOWN:
|
case SDL_MOUSEBUTTONDOWN:
|
||||||
case SDL_MOUSEBUTTONUP:
|
case SDL_MOUSEBUTTONUP:
|
||||||
OnMouseButton(event.button.button, event.button.state, event.button.x, event.button.y);
|
// ignore if it came from touch
|
||||||
|
if (event.button.which != SDL_TOUCH_MOUSEID) {
|
||||||
|
OnMouseButton(event.button.button, event.button.state, event.button.x,
|
||||||
|
event.button.y);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SDL_FINGERDOWN:
|
||||||
|
OnFingerDown(event.tfinger.x, event.tfinger.y);
|
||||||
|
break;
|
||||||
|
case SDL_FINGERMOTION:
|
||||||
|
OnFingerMotion(event.tfinger.x, event.tfinger.y);
|
||||||
|
break;
|
||||||
|
case SDL_FINGERUP:
|
||||||
|
OnFingerUp();
|
||||||
break;
|
break;
|
||||||
case SDL_QUIT:
|
case SDL_QUIT:
|
||||||
is_open = false;
|
is_open = false;
|
||||||
|
|
|
@ -40,6 +40,18 @@ private:
|
||||||
/// Called by PollEvents when a mouse button is pressed or released
|
/// Called by PollEvents when a mouse button is pressed or released
|
||||||
void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
|
void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
|
||||||
|
|
||||||
|
/// Translates pixel position (0..1) to pixel positions
|
||||||
|
std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const;
|
||||||
|
|
||||||
|
/// Called by PollEvents when a finger starts touching the touchscreen
|
||||||
|
void OnFingerDown(float x, float y);
|
||||||
|
|
||||||
|
/// Called by PollEvents when a finger moves while touching the touchscreen
|
||||||
|
void OnFingerMotion(float x, float y);
|
||||||
|
|
||||||
|
/// Called by PollEvents when a finger stops touching the touchscreen
|
||||||
|
void OnFingerUp();
|
||||||
|
|
||||||
/// Called by PollEvents when any event that may cause the window to be resized occurs
|
/// Called by PollEvents when any event that may cause the window to be resized occurs
|
||||||
void OnResize();
|
void OnResize();
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,7 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
|
||||||
std::string window_title = fmt::format("Citra {} | {}-{}", Common::g_build_name,
|
std::string window_title = fmt::format("Citra {} | {}-{}", Common::g_build_name,
|
||||||
Common::g_scm_branch, Common::g_scm_desc);
|
Common::g_scm_branch, Common::g_scm_desc);
|
||||||
setWindowTitle(QString::fromStdString(window_title));
|
setWindowTitle(QString::fromStdString(window_title));
|
||||||
|
setAttribute(Qt::WA_AcceptTouchEvents);
|
||||||
|
|
||||||
InputCommon::Init();
|
InputCommon::Init();
|
||||||
}
|
}
|
||||||
|
@ -191,11 +192,17 @@ QByteArray GRenderWindow::saveGeometry() {
|
||||||
return geometry;
|
return geometry;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal GRenderWindow::windowPixelRatio() {
|
qreal GRenderWindow::windowPixelRatio() const {
|
||||||
// windowHandle() might not be accessible until the window is displayed to screen.
|
// windowHandle() might not be accessible until the window is displayed to screen.
|
||||||
return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
|
return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const {
|
||||||
|
const qreal pixel_ratio = windowPixelRatio();
|
||||||
|
return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
|
||||||
|
static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
|
||||||
|
}
|
||||||
|
|
||||||
void GRenderWindow::closeEvent(QCloseEvent* event) {
|
void GRenderWindow::closeEvent(QCloseEvent* event) {
|
||||||
emit Closed();
|
emit Closed();
|
||||||
QWidget::closeEvent(event);
|
QWidget::closeEvent(event);
|
||||||
|
@ -210,31 +217,81 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
|
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
|
||||||
|
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||||
|
return; // touch input is handled in TouchBeginEvent
|
||||||
|
|
||||||
auto pos = event->pos();
|
auto pos = event->pos();
|
||||||
if (event->button() == Qt::LeftButton) {
|
if (event->button() == Qt::LeftButton) {
|
||||||
qreal pixelRatio = windowPixelRatio();
|
const auto [x, y] = ScaleTouch(pos);
|
||||||
this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio),
|
this->TouchPressed(x, y);
|
||||||
static_cast<unsigned>(pos.y() * pixelRatio));
|
|
||||||
} else if (event->button() == Qt::RightButton) {
|
} else if (event->button() == Qt::RightButton) {
|
||||||
InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
|
InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
|
void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
|
||||||
|
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||||
|
return; // touch input is handled in TouchUpdateEvent
|
||||||
|
|
||||||
auto pos = event->pos();
|
auto pos = event->pos();
|
||||||
qreal pixelRatio = windowPixelRatio();
|
const auto [x, y] = ScaleTouch(pos);
|
||||||
this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u),
|
this->TouchMoved(x, y);
|
||||||
std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u));
|
|
||||||
InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
|
InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
|
void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
|
||||||
|
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||||
|
return; // touch input is handled in TouchEndEvent
|
||||||
|
|
||||||
if (event->button() == Qt::LeftButton)
|
if (event->button() == Qt::LeftButton)
|
||||||
this->TouchReleased();
|
this->TouchReleased();
|
||||||
else if (event->button() == Qt::RightButton)
|
else if (event->button() == Qt::RightButton)
|
||||||
InputCommon::GetMotionEmu()->EndTilt();
|
InputCommon::GetMotionEmu()->EndTilt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
|
||||||
|
// TouchBegin always has exactly one touch point, so take the .first()
|
||||||
|
const auto [x, y] = ScaleTouch(event->touchPoints().first().pos());
|
||||||
|
this->TouchPressed(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
|
||||||
|
QPointF pos;
|
||||||
|
int active_points = 0;
|
||||||
|
|
||||||
|
// average all active touch points
|
||||||
|
for (const auto tp : event->touchPoints()) {
|
||||||
|
if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) {
|
||||||
|
active_points++;
|
||||||
|
pos += tp.pos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos /= active_points;
|
||||||
|
|
||||||
|
const auto [x, y] = ScaleTouch(pos);
|
||||||
|
this->TouchMoved(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::TouchEndEvent() {
|
||||||
|
this->TouchReleased();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GRenderWindow::event(QEvent* event) {
|
||||||
|
if (event->type() == QEvent::TouchBegin) {
|
||||||
|
TouchBeginEvent(static_cast<QTouchEvent*>(event));
|
||||||
|
return true;
|
||||||
|
} else if (event->type() == QEvent::TouchUpdate) {
|
||||||
|
TouchUpdateEvent(static_cast<QTouchEvent*>(event));
|
||||||
|
return true;
|
||||||
|
} else if (event->type() == QEvent::TouchEnd || event->type() == QEvent::TouchCancel) {
|
||||||
|
TouchEndEvent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QWidget::event(event);
|
||||||
|
}
|
||||||
|
|
||||||
void GRenderWindow::focusOutEvent(QFocusEvent* event) {
|
void GRenderWindow::focusOutEvent(QFocusEvent* event) {
|
||||||
QWidget::focusOutEvent(event);
|
QWidget::focusOutEvent(event);
|
||||||
InputCommon::GetKeyboard()->ReleaseAllKeys();
|
InputCommon::GetKeyboard()->ReleaseAllKeys();
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
class QKeyEvent;
|
class QKeyEvent;
|
||||||
class QScreen;
|
class QScreen;
|
||||||
|
class QTouchEvent;
|
||||||
|
|
||||||
class GGLWidgetInternal;
|
class GGLWidgetInternal;
|
||||||
class GMainWindow;
|
class GMainWindow;
|
||||||
|
@ -119,7 +120,7 @@ public:
|
||||||
void restoreGeometry(const QByteArray& geometry); // overridden
|
void restoreGeometry(const QByteArray& geometry); // overridden
|
||||||
QByteArray saveGeometry(); // overridden
|
QByteArray saveGeometry(); // overridden
|
||||||
|
|
||||||
qreal windowPixelRatio();
|
qreal windowPixelRatio() const;
|
||||||
|
|
||||||
void closeEvent(QCloseEvent* event) override;
|
void closeEvent(QCloseEvent* event) override;
|
||||||
|
|
||||||
|
@ -130,6 +131,8 @@ public:
|
||||||
void mouseMoveEvent(QMouseEvent* event) override;
|
void mouseMoveEvent(QMouseEvent* event) override;
|
||||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||||
|
|
||||||
|
bool event(QEvent* event) override;
|
||||||
|
|
||||||
void focusOutEvent(QFocusEvent* event) override;
|
void focusOutEvent(QFocusEvent* event) override;
|
||||||
|
|
||||||
void OnClientAreaResized(unsigned width, unsigned height);
|
void OnClientAreaResized(unsigned width, unsigned height);
|
||||||
|
@ -148,6 +151,11 @@ signals:
|
||||||
void Closed();
|
void Closed();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const;
|
||||||
|
void TouchBeginEvent(const QTouchEvent* event);
|
||||||
|
void TouchUpdateEvent(const QTouchEvent* event);
|
||||||
|
void TouchEndEvent();
|
||||||
|
|
||||||
void OnMinimalClientAreaChangeRequest(
|
void OnMinimalClientAreaChangeRequest(
|
||||||
const std::pair<unsigned, unsigned>& minimal_size) override;
|
const std::pair<unsigned, unsigned>& minimal_size) override;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue