Merge pull request #1463 from FearlessTobi/port-4310

Port citra-emu/citra#4310: "Handle touch input"
This commit is contained in:
bunnei 2018-10-09 19:02:41 -04:00 committed by GitHub
commit 0b3d4db98b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 131 additions and 10 deletions

View File

@ -110,6 +110,7 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, std::string window_title = fmt::format("yuzu {} | {}-{}", 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();
InputCommon::StartJoystickEventHandler(); InputCommon::StartJoystickEventHandler();
@ -190,11 +191,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);
@ -209,31 +216,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();

View File

@ -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;

View File

@ -40,6 +40,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);
@ -219,11 +248,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;

View File

@ -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();