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:
NeatNit 2018-10-01 22:42:49 +03:00 committed by zhupengfei
parent f405134913
commit 4ee914c7a8
4 changed files with 131 additions and 10 deletions

View file

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

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

View file

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

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;