Merge pull request #1463 from FearlessTobi/port-4310
Port citra-emu/citra#4310: "Handle touch input"
This commit is contained in:
		
						commit
						0b3d4db98b
					
				@ -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();
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
 | 
				
			|||||||
@ -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();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user