Merge pull request #1463 from FearlessTobi/port-4310
Port citra-emu/citra#4310: "Handle touch input"
This commit is contained in:
		| @@ -110,6 +110,7 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | ||||
|     std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, | ||||
|                                            Common::g_scm_branch, Common::g_scm_desc); | ||||
|     setWindowTitle(QString::fromStdString(window_title)); | ||||
|     setAttribute(Qt::WA_AcceptTouchEvents); | ||||
|  | ||||
|     InputCommon::Init(); | ||||
|     InputCommon::StartJoystickEventHandler(); | ||||
| @@ -190,11 +191,17 @@ QByteArray GRenderWindow::saveGeometry() { | ||||
|         return geometry; | ||||
| } | ||||
|  | ||||
| qreal GRenderWindow::windowPixelRatio() { | ||||
| qreal GRenderWindow::windowPixelRatio() const { | ||||
|     // windowHandle() might not be accessible until the window is displayed to screen. | ||||
|     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) { | ||||
|     emit Closed(); | ||||
|     QWidget::closeEvent(event); | ||||
| @@ -209,31 +216,81 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | ||||
| } | ||||
|  | ||||
| void GRenderWindow::mousePressEvent(QMouseEvent* event) { | ||||
|     if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||||
|         return; // touch input is handled in TouchBeginEvent | ||||
|  | ||||
|     auto pos = event->pos(); | ||||
|     if (event->button() == Qt::LeftButton) { | ||||
|         qreal pixelRatio = windowPixelRatio(); | ||||
|         this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio), | ||||
|                            static_cast<unsigned>(pos.y() * pixelRatio)); | ||||
|         const auto [x, y] = ScaleTouch(pos); | ||||
|         this->TouchPressed(x, y); | ||||
|     } else if (event->button() == Qt::RightButton) { | ||||
|         InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | ||||
|     if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||||
|         return; // touch input is handled in TouchUpdateEvent | ||||
|  | ||||
|     auto pos = event->pos(); | ||||
|     qreal pixelRatio = windowPixelRatio(); | ||||
|     this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u), | ||||
|                      std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u)); | ||||
|     const auto [x, y] = ScaleTouch(pos); | ||||
|     this->TouchMoved(x, y); | ||||
|     InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); | ||||
| } | ||||
|  | ||||
| void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | ||||
|     if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||||
|         return; // touch input is handled in TouchEndEvent | ||||
|  | ||||
|     if (event->button() == Qt::LeftButton) | ||||
|         this->TouchReleased(); | ||||
|     else if (event->button() == Qt::RightButton) | ||||
|         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) { | ||||
|     QWidget::focusOutEvent(event); | ||||
|     InputCommon::GetKeyboard()->ReleaseAllKeys(); | ||||
|   | ||||
| @@ -15,6 +15,7 @@ | ||||
|  | ||||
| class QKeyEvent; | ||||
| class QScreen; | ||||
| class QTouchEvent; | ||||
|  | ||||
| class GGLWidgetInternal; | ||||
| class GMainWindow; | ||||
| @@ -119,7 +120,7 @@ public: | ||||
|     void restoreGeometry(const QByteArray& geometry); // overridden | ||||
|     QByteArray saveGeometry();                        // overridden | ||||
|  | ||||
|     qreal windowPixelRatio(); | ||||
|     qreal windowPixelRatio() const; | ||||
|  | ||||
|     void closeEvent(QCloseEvent* event) override; | ||||
|  | ||||
| @@ -130,6 +131,8 @@ public: | ||||
|     void mouseMoveEvent(QMouseEvent* event) override; | ||||
|     void mouseReleaseEvent(QMouseEvent* event) override; | ||||
|  | ||||
|     bool event(QEvent* event) override; | ||||
|  | ||||
|     void focusOutEvent(QFocusEvent* event) override; | ||||
|  | ||||
|     void OnClientAreaResized(unsigned width, unsigned height); | ||||
| @@ -148,6 +151,11 @@ signals: | ||||
|     void Closed(); | ||||
|  | ||||
| private: | ||||
|     std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const; | ||||
|     void TouchBeginEvent(const QTouchEvent* event); | ||||
|     void TouchUpdateEvent(const QTouchEvent* event); | ||||
|     void TouchEndEvent(); | ||||
|  | ||||
|     void OnMinimalClientAreaChangeRequest( | ||||
|         const std::pair<unsigned, unsigned>& minimal_size) override; | ||||
|  | ||||
|   | ||||
| @@ -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) { | ||||
|     if (state == SDL_PRESSED) { | ||||
|         InputCommon::GetKeyboard()->PressKey(key); | ||||
| @@ -219,11 +248,26 @@ void EmuWindow_SDL2::PollEvents() { | ||||
|             OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state); | ||||
|             break; | ||||
|         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; | ||||
|         case SDL_MOUSEBUTTONDOWN: | ||||
|         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; | ||||
|         case SDL_QUIT: | ||||
|             is_open = false; | ||||
|   | ||||
| @@ -40,6 +40,18 @@ private: | ||||
|     /// Called by PollEvents when a mouse button is pressed or released | ||||
|     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 | ||||
|     void OnResize(); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user