diff options
| author | 2018-10-09 19:02:41 -0400 | |
|---|---|---|
| committer | 2018-10-09 19:02:41 -0400 | |
| commit | 0b3d4db98b5bc6f81a22e4618dbabbdafcec0ada (patch) | |
| tree | e3574b49f5a9e4c762e709ada72e0c1ba16e2fff /src | |
| parent | Merge pull request #1459 from ogniK5377/break (diff) | |
| parent | implemented touch in Qt and SDL (diff) | |
| download | yuzu-0b3d4db98b5bc6f81a22e4618dbabbdafcec0ada.tar.gz yuzu-0b3d4db98b5bc6f81a22e4618dbabbdafcec0ada.tar.xz yuzu-0b3d4db98b5bc6f81a22e4618dbabbdafcec0ada.zip | |
Merge pull request #1463 from FearlessTobi/port-4310
Port citra-emu/citra#4310: "Handle touch input"
Diffstat (limited to 'src')
| -rw-r--r-- | src/yuzu/bootmanager.cpp | 71 | ||||
| -rw-r--r-- | src/yuzu/bootmanager.h | 10 | ||||
| -rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | 48 | ||||
| -rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2.h | 12 |
4 files changed, 131 insertions, 10 deletions
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 4e4c108ab..e8ab23326 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -110,6 +110,7 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | |||
| 110 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, | 110 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, |
| 111 | Common::g_scm_branch, Common::g_scm_desc); | 111 | Common::g_scm_branch, Common::g_scm_desc); |
| 112 | setWindowTitle(QString::fromStdString(window_title)); | 112 | setWindowTitle(QString::fromStdString(window_title)); |
| 113 | setAttribute(Qt::WA_AcceptTouchEvents); | ||
| 113 | 114 | ||
| 114 | InputCommon::Init(); | 115 | InputCommon::Init(); |
| 115 | InputCommon::StartJoystickEventHandler(); | 116 | InputCommon::StartJoystickEventHandler(); |
| @@ -190,11 +191,17 @@ QByteArray GRenderWindow::saveGeometry() { | |||
| 190 | return geometry; | 191 | return geometry; |
| 191 | } | 192 | } |
| 192 | 193 | ||
| 193 | qreal GRenderWindow::windowPixelRatio() { | 194 | qreal GRenderWindow::windowPixelRatio() const { |
| 194 | // windowHandle() might not be accessible until the window is displayed to screen. | 195 | // windowHandle() might not be accessible until the window is displayed to screen. |
| 195 | return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; | 196 | return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; |
| 196 | } | 197 | } |
| 197 | 198 | ||
| 199 | std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const { | ||
| 200 | const qreal pixel_ratio = windowPixelRatio(); | ||
| 201 | return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), | ||
| 202 | static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; | ||
| 203 | } | ||
| 204 | |||
| 198 | void GRenderWindow::closeEvent(QCloseEvent* event) { | 205 | void GRenderWindow::closeEvent(QCloseEvent* event) { |
| 199 | emit Closed(); | 206 | emit Closed(); |
| 200 | QWidget::closeEvent(event); | 207 | QWidget::closeEvent(event); |
| @@ -209,31 +216,81 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | |||
| 209 | } | 216 | } |
| 210 | 217 | ||
| 211 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { | 218 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { |
| 219 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 220 | return; // touch input is handled in TouchBeginEvent | ||
| 221 | |||
| 212 | auto pos = event->pos(); | 222 | auto pos = event->pos(); |
| 213 | if (event->button() == Qt::LeftButton) { | 223 | if (event->button() == Qt::LeftButton) { |
| 214 | qreal pixelRatio = windowPixelRatio(); | 224 | const auto [x, y] = ScaleTouch(pos); |
| 215 | this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio), | 225 | this->TouchPressed(x, y); |
| 216 | static_cast<unsigned>(pos.y() * pixelRatio)); | ||
| 217 | } else if (event->button() == Qt::RightButton) { | 226 | } else if (event->button() == Qt::RightButton) { |
| 218 | InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); | 227 | InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); |
| 219 | } | 228 | } |
| 220 | } | 229 | } |
| 221 | 230 | ||
| 222 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | 231 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { |
| 232 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 233 | return; // touch input is handled in TouchUpdateEvent | ||
| 234 | |||
| 223 | auto pos = event->pos(); | 235 | auto pos = event->pos(); |
| 224 | qreal pixelRatio = windowPixelRatio(); | 236 | const auto [x, y] = ScaleTouch(pos); |
| 225 | this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u), | 237 | this->TouchMoved(x, y); |
| 226 | std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u)); | ||
| 227 | InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); | 238 | InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); |
| 228 | } | 239 | } |
| 229 | 240 | ||
| 230 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | 241 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { |
| 242 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 243 | return; // touch input is handled in TouchEndEvent | ||
| 244 | |||
| 231 | if (event->button() == Qt::LeftButton) | 245 | if (event->button() == Qt::LeftButton) |
| 232 | this->TouchReleased(); | 246 | this->TouchReleased(); |
| 233 | else if (event->button() == Qt::RightButton) | 247 | else if (event->button() == Qt::RightButton) |
| 234 | InputCommon::GetMotionEmu()->EndTilt(); | 248 | InputCommon::GetMotionEmu()->EndTilt(); |
| 235 | } | 249 | } |
| 236 | 250 | ||
| 251 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { | ||
| 252 | // TouchBegin always has exactly one touch point, so take the .first() | ||
| 253 | const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); | ||
| 254 | this->TouchPressed(x, y); | ||
| 255 | } | ||
| 256 | |||
| 257 | void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) { | ||
| 258 | QPointF pos; | ||
| 259 | int active_points = 0; | ||
| 260 | |||
| 261 | // average all active touch points | ||
| 262 | for (const auto tp : event->touchPoints()) { | ||
| 263 | if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) { | ||
| 264 | active_points++; | ||
| 265 | pos += tp.pos(); | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | pos /= active_points; | ||
| 270 | |||
| 271 | const auto [x, y] = ScaleTouch(pos); | ||
| 272 | this->TouchMoved(x, y); | ||
| 273 | } | ||
| 274 | |||
| 275 | void GRenderWindow::TouchEndEvent() { | ||
| 276 | this->TouchReleased(); | ||
| 277 | } | ||
| 278 | |||
| 279 | bool GRenderWindow::event(QEvent* event) { | ||
| 280 | if (event->type() == QEvent::TouchBegin) { | ||
| 281 | TouchBeginEvent(static_cast<QTouchEvent*>(event)); | ||
| 282 | return true; | ||
| 283 | } else if (event->type() == QEvent::TouchUpdate) { | ||
| 284 | TouchUpdateEvent(static_cast<QTouchEvent*>(event)); | ||
| 285 | return true; | ||
| 286 | } else if (event->type() == QEvent::TouchEnd || event->type() == QEvent::TouchCancel) { | ||
| 287 | TouchEndEvent(); | ||
| 288 | return true; | ||
| 289 | } | ||
| 290 | |||
| 291 | return QWidget::event(event); | ||
| 292 | } | ||
| 293 | |||
| 237 | void GRenderWindow::focusOutEvent(QFocusEvent* event) { | 294 | void GRenderWindow::focusOutEvent(QFocusEvent* event) { |
| 238 | QWidget::focusOutEvent(event); | 295 | QWidget::focusOutEvent(event); |
| 239 | InputCommon::GetKeyboard()->ReleaseAllKeys(); | 296 | InputCommon::GetKeyboard()->ReleaseAllKeys(); |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index f133bfadf..873985564 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | 15 | ||
| 16 | class QKeyEvent; | 16 | class QKeyEvent; |
| 17 | class QScreen; | 17 | class QScreen; |
| 18 | class QTouchEvent; | ||
| 18 | 19 | ||
| 19 | class GGLWidgetInternal; | 20 | class GGLWidgetInternal; |
| 20 | class GMainWindow; | 21 | class GMainWindow; |
| @@ -119,7 +120,7 @@ public: | |||
| 119 | void restoreGeometry(const QByteArray& geometry); // overridden | 120 | void restoreGeometry(const QByteArray& geometry); // overridden |
| 120 | QByteArray saveGeometry(); // overridden | 121 | QByteArray saveGeometry(); // overridden |
| 121 | 122 | ||
| 122 | qreal windowPixelRatio(); | 123 | qreal windowPixelRatio() const; |
| 123 | 124 | ||
| 124 | void closeEvent(QCloseEvent* event) override; | 125 | void closeEvent(QCloseEvent* event) override; |
| 125 | 126 | ||
| @@ -130,6 +131,8 @@ public: | |||
| 130 | void mouseMoveEvent(QMouseEvent* event) override; | 131 | void mouseMoveEvent(QMouseEvent* event) override; |
| 131 | void mouseReleaseEvent(QMouseEvent* event) override; | 132 | void mouseReleaseEvent(QMouseEvent* event) override; |
| 132 | 133 | ||
| 134 | bool event(QEvent* event) override; | ||
| 135 | |||
| 133 | void focusOutEvent(QFocusEvent* event) override; | 136 | void focusOutEvent(QFocusEvent* event) override; |
| 134 | 137 | ||
| 135 | void OnClientAreaResized(unsigned width, unsigned height); | 138 | void OnClientAreaResized(unsigned width, unsigned height); |
| @@ -148,6 +151,11 @@ signals: | |||
| 148 | void Closed(); | 151 | void Closed(); |
| 149 | 152 | ||
| 150 | private: | 153 | private: |
| 154 | std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const; | ||
| 155 | void TouchBeginEvent(const QTouchEvent* event); | ||
| 156 | void TouchUpdateEvent(const QTouchEvent* event); | ||
| 157 | void TouchEndEvent(); | ||
| 158 | |||
| 151 | void OnMinimalClientAreaChangeRequest( | 159 | void OnMinimalClientAreaChangeRequest( |
| 152 | const std::pair<unsigned, unsigned>& minimal_size) override; | 160 | const std::pair<unsigned, unsigned>& minimal_size) override; |
| 153 | 161 | ||
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 155095095..a9ad92a80 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -40,6 +40,35 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { | |||
| 40 | } | 40 | } |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, float touch_y) const { | ||
| 44 | int w, h; | ||
| 45 | SDL_GetWindowSize(render_window, &w, &h); | ||
| 46 | |||
| 47 | touch_x *= w; | ||
| 48 | touch_y *= h; | ||
| 49 | |||
| 50 | return {static_cast<unsigned>(std::max(std::round(touch_x), 0.0f)), | ||
| 51 | static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))}; | ||
| 52 | } | ||
| 53 | |||
| 54 | void EmuWindow_SDL2::OnFingerDown(float x, float y) { | ||
| 55 | // TODO(NeatNit): keep track of multitouch using the fingerID and a dictionary of some kind | ||
| 56 | // This isn't critical because the best we can do when we have that is to average them, like the | ||
| 57 | // 3DS does | ||
| 58 | |||
| 59 | const auto [px, py] = TouchToPixelPos(x, y); | ||
| 60 | TouchPressed(px, py); | ||
| 61 | } | ||
| 62 | |||
| 63 | void EmuWindow_SDL2::OnFingerMotion(float x, float y) { | ||
| 64 | const auto [px, py] = TouchToPixelPos(x, y); | ||
| 65 | TouchMoved(px, py); | ||
| 66 | } | ||
| 67 | |||
| 68 | void EmuWindow_SDL2::OnFingerUp() { | ||
| 69 | TouchReleased(); | ||
| 70 | } | ||
| 71 | |||
| 43 | void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { | 72 | void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { |
| 44 | if (state == SDL_PRESSED) { | 73 | if (state == SDL_PRESSED) { |
| 45 | InputCommon::GetKeyboard()->PressKey(key); | 74 | InputCommon::GetKeyboard()->PressKey(key); |
| @@ -219,11 +248,26 @@ void EmuWindow_SDL2::PollEvents() { | |||
| 219 | OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state); | 248 | OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state); |
| 220 | break; | 249 | break; |
| 221 | case SDL_MOUSEMOTION: | 250 | case SDL_MOUSEMOTION: |
| 222 | OnMouseMotion(event.motion.x, event.motion.y); | 251 | // ignore if it came from touch |
| 252 | if (event.button.which != SDL_TOUCH_MOUSEID) | ||
| 253 | OnMouseMotion(event.motion.x, event.motion.y); | ||
| 223 | break; | 254 | break; |
| 224 | case SDL_MOUSEBUTTONDOWN: | 255 | case SDL_MOUSEBUTTONDOWN: |
| 225 | case SDL_MOUSEBUTTONUP: | 256 | case SDL_MOUSEBUTTONUP: |
| 226 | OnMouseButton(event.button.button, event.button.state, event.button.x, event.button.y); | 257 | // ignore if it came from touch |
| 258 | if (event.button.which != SDL_TOUCH_MOUSEID) { | ||
| 259 | OnMouseButton(event.button.button, event.button.state, event.button.x, | ||
| 260 | event.button.y); | ||
| 261 | } | ||
| 262 | break; | ||
| 263 | case SDL_FINGERDOWN: | ||
| 264 | OnFingerDown(event.tfinger.x, event.tfinger.y); | ||
| 265 | break; | ||
| 266 | case SDL_FINGERMOTION: | ||
| 267 | OnFingerMotion(event.tfinger.x, event.tfinger.y); | ||
| 268 | break; | ||
| 269 | case SDL_FINGERUP: | ||
| 270 | OnFingerUp(); | ||
| 227 | break; | 271 | break; |
| 228 | case SDL_QUIT: | 272 | case SDL_QUIT: |
| 229 | is_open = false; | 273 | is_open = false; |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index d34902109..b0d4116cc 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h | |||
| @@ -40,6 +40,18 @@ private: | |||
| 40 | /// Called by PollEvents when a mouse button is pressed or released | 40 | /// Called by PollEvents when a mouse button is pressed or released |
| 41 | void OnMouseButton(u32 button, u8 state, s32 x, s32 y); | 41 | void OnMouseButton(u32 button, u8 state, s32 x, s32 y); |
| 42 | 42 | ||
| 43 | /// Translates pixel position (0..1) to pixel positions | ||
| 44 | std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const; | ||
| 45 | |||
| 46 | /// Called by PollEvents when a finger starts touching the touchscreen | ||
| 47 | void OnFingerDown(float x, float y); | ||
| 48 | |||
| 49 | /// Called by PollEvents when a finger moves while touching the touchscreen | ||
| 50 | void OnFingerMotion(float x, float y); | ||
| 51 | |||
| 52 | /// Called by PollEvents when a finger stops touching the touchscreen | ||
| 53 | void OnFingerUp(); | ||
| 54 | |||
| 43 | /// Called by PollEvents when any event that may cause the window to be resized occurs | 55 | /// Called by PollEvents when any event that may cause the window to be resized occurs |
| 44 | void OnResize(); | 56 | void OnResize(); |
| 45 | 57 | ||