From 1649da2a354d1d06486d6d77e508b0b749fe7302 Mon Sep 17 00:00:00 2001 From: James Rowe Date: Sat, 1 Apr 2017 07:05:47 -0600 Subject: [PATCH] Implement proper touch screen support by emulating the stylus Using a mouse for touch screen controls doesn't accurately encompass the feel that one gets when using the 3ds stylus. This PR adds experimental support for using a stylus for touch screen controls. --- dist/stylus.png | Bin 0 -> 12541 bytes src/citra_qt/CMakeLists.txt | 9 +++- src/citra_qt/bootmanager.cpp | 80 ++++++++++++++++++++++++++++----- src/citra_qt/bootmanager.h | 11 +++++ src/citra_qt/citra-qt.rc | 1 - src/citra_qt/main.cpp | 5 ++- src/citra_qt/overlay.cpp | 66 +++++++++++++++++++++++++++ src/citra_qt/overlay.h | 32 +++++++++++++ src/citra_qt/resources.qrc | 7 +++ src/citra_qt/stylus.cpp | 84 +++++++++++++++++++++++++++++++++++ src/citra_qt/stylus.h | 61 +++++++++++++++++++++++++ 11 files changed, 341 insertions(+), 15 deletions(-) create mode 100644 dist/stylus.png create mode 100644 src/citra_qt/overlay.cpp create mode 100644 src/citra_qt/overlay.h create mode 100644 src/citra_qt/resources.qrc create mode 100644 src/citra_qt/stylus.cpp create mode 100644 src/citra_qt/stylus.h diff --git a/dist/stylus.png b/dist/stylus.png new file mode 100644 index 0000000000000000000000000000000000000000..d64aed43195975dfa89d14e5f1b57231ddb4fc78 GIT binary patch literal 12541 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 literal 0 HcmV?d00001 diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 2b1c59a92..2fe3ae670 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -1,4 +1,5 @@ set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) @@ -29,6 +30,8 @@ set(SRCS game_list.cpp hotkeys.cpp main.cpp + overlay.cpp + stylus.cpp ui_settings.cpp citra-qt.rc Info.plist @@ -63,6 +66,8 @@ set(HEADERS game_list_p.h hotkeys.h main.h + overlay.h + stylus.h ui_settings.h ) @@ -92,10 +97,10 @@ endif() if (APPLE) set(MACOSX_ICON "../../dist/citra.icns") set_source_files_properties(${MACOSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) - add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS} ${MACOSX_ICON}) + add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS} ${MACOSX_ICON} resources.qrc) set_target_properties(citra-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) else() - add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS}) + add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS} resources.qrc) endif() target_link_libraries(citra-qt core video_core audio_core common input_common) target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index bae576d6a..4f844709d 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -100,13 +100,18 @@ private: bool do_painting; }; +Stylus* GRenderWindow::GetStylus() { + return stylus.get(); +} + GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) : QWidget(parent), child(nullptr), emu_thread(emu_thread) { std::string window_title = Common::StringFromFormat("Citra %s| %s-%s", Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc); setWindowTitle(QString::fromStdString(window_title)); - + setFocusPolicy(Qt::StrongFocus); + setMouseTracking(true); InputCommon::Init(); } @@ -148,6 +153,16 @@ void GRenderWindow::DoneCurrent() { void GRenderWindow::PollEvents() {} +void GRenderWindow::UpdateStylusSize(unsigned width, unsigned height) { + if (overlay) { + QRect rect = QApplication::desktop()->screenGeometry(); + LOG_WARNING(Frontend, "rect %d, %d", rect.width(), rect.height()); + overlay->setFixedWidth(rect.width()); + overlay->setFixedHeight(rect.height() - 1); + overlay->update(); + } +} + // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). // // Older versions get the window size (density independent pixels), @@ -159,6 +174,7 @@ void GRenderWindow::OnFramebufferSizeChanged() { qreal pixelRatio = windowPixelRatio(); unsigned width = child->QPaintDevice::width() * pixelRatio; unsigned height = child->QPaintDevice::height() * pixelRatio; + UpdateStylusSize(width, height); UpdateCurrentFramebufferLayout(width, height); } @@ -209,12 +225,22 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { InputCommon::GetKeyboard()->ReleaseKey(event->key()); } +void GRenderWindow::UpdateStylusPosition(QMouseEvent* event) { + auto pos = event->pos(); + stylus->UpdatePosition(pos.x(), pos.y()); + overlay->update(); +} + void GRenderWindow::mousePressEvent(QMouseEvent* event) { auto pos = event->pos(); - if (event->button() == Qt::LeftButton) { - qreal pixelRatio = windowPixelRatio(); - this->TouchPressed(static_cast(pos.x() * pixelRatio), - static_cast(pos.y() * pixelRatio)); + if (event->button() == Qt::LeftButton && stylus->Contains(pos)) { + stylus->SetHoldPoint(pos); + if (stylus->IsNearCenter(pos)) { + stylus->SetState(StylusState::HOLD); + } else { + stylus->SetState(StylusState::ROTATE); + } + QApplication::setOverrideCursor(Qt::ClosedHandCursor); } else if (event->button() == Qt::RightButton) { motion_emu->BeginTilt(pos.x(), pos.y()); } @@ -222,17 +248,44 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) { void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { auto pos = event->pos(); - qreal pixelRatio = windowPixelRatio(); - this->TouchMoved(std::max(static_cast(pos.x() * pixelRatio), 0u), - std::max(static_cast(pos.y() * pixelRatio), 0u)); - motion_emu->Tilt(pos.x(), pos.y()); + + if (stylus->GetState() == StylusState::HOLD) { + UpdateStylusPosition(event); + qreal pixelRatio = windowPixelRatio(); + QPoint p = stylus->GetTouchPoint(); + QPoint screen = mapToGlobal(QWidget::geometry().topLeft()); + this->TouchMoved(std::max(static_cast((p.x() - screen.x()) * pixelRatio), 0u), + std::max(static_cast((p.y() - screen.y()) * pixelRatio), 0u)); + } else if (stylus->GetState() == StylusState::ROTATE) { + stylus->Rotate(pos); + overlay->update(); + } + if (event->button() == Qt::LeftButton) { + } else if (event->button() == Qt::RightButton) { + motion_emu->Tilt(pos.x(), pos.y()); + } +} +void GRenderWindow::mouseDoubleClickEvent(QMouseEvent* e) { + if (e->button() == Qt::LeftButton && stylus->Contains(e->pos())) { + qreal pixelRatio = windowPixelRatio(); + QPoint p = stylus->GetTouchPoint(); + QPoint screen = mapToGlobal(QWidget::geometry().topLeft()); + LOG_ERROR(Frontend, "screen %d, %d", screen.x(), screen.y()); + this->TouchPressed(static_cast((p.x() - screen.x()) * pixelRatio), + static_cast((p.y() - screen.y()) * pixelRatio)); + stylus->SetState(StylusState::HOLD); + stylus->SetHoldPoint(e->pos()); + } } void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { - if (event->button() == Qt::LeftButton) + if (event->button() == Qt::LeftButton) { this->TouchReleased(); - else if (event->button() == Qt::RightButton) + stylus->SetState(StylusState::DROP); + QApplication::restoreOverrideCursor(); + } else if (event->button() == Qt::RightButton) { motion_emu->EndTilt(); + } } void GRenderWindow::focusOutEvent(QFocusEvent* event) { @@ -287,13 +340,18 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest( void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { motion_emu = std::make_unique(*this); + stylus = std::make_unique(); + overlay = std::make_unique(this); this->emu_thread = emu_thread; child->DisablePainting(); + overlay->show(); } void GRenderWindow::OnEmulationStopping() { motion_emu = nullptr; + stylus = nullptr; emu_thread = nullptr; + overlay = nullptr; child->EnablePainting(); } diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index 9d39f1af8..07685d551 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h @@ -9,10 +9,13 @@ #include #include #include +#include "citra_qt/overlay.h" +#include "citra_qt/stylus.h" #include "common/thread.h" #include "core/frontend/emu_window.h" #include "core/frontend/motion_emu.h" + class QKeyEvent; class QScreen; @@ -127,12 +130,14 @@ public: void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; + void GRenderWindow::mouseDoubleClickEvent(QMouseEvent* e) override; void focusOutEvent(QFocusEvent* event) override; void OnClientAreaResized(unsigned width, unsigned height); void InitRenderTarget(); + Stylus* GRenderWindow::GetStylus(); public slots: void moveContext(); // overridden @@ -146,6 +151,8 @@ signals: void Closed(); private: + void UpdateStylusPosition(QMouseEvent* event); + void UpdateStylusSize(unsigned width, unsigned height); void OnMinimalClientAreaChangeRequest( const std::pair& minimal_size) override; @@ -157,6 +164,10 @@ private: /// Motion sensors emulation std::unique_ptr motion_emu; + std::unique_ptr stylus; + std::unique_ptr overlay; + + StylusState stylus_state; protected: void showEvent(QShowEvent* event) override; diff --git a/src/citra_qt/citra-qt.rc b/src/citra_qt/citra-qt.rc index fea603004..480c3bf12 100644 --- a/src/citra_qt/citra-qt.rc +++ b/src/citra_qt/citra-qt.rc @@ -6,4 +6,3 @@ // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. CITRA_ICON ICON "../../dist/citra.ico" - diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 2723a0217..821971540 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -71,10 +71,13 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { setWindowTitle(QString("Citra %1| %2-%3") .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); + setWindowIcon(QIcon(":citra_icon.ico")); + setMouseTracking(true); + centralWidget()->setMouseTracking(true); show(); game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); - + game_list->setMouseTracking(true); QStringList args = QApplication::arguments(); if (args.length() >= 2) { BootGame(args[1]); diff --git a/src/citra_qt/overlay.cpp b/src/citra_qt/overlay.cpp new file mode 100644 index 000000000..210b47fae --- /dev/null +++ b/src/citra_qt/overlay.cpp @@ -0,0 +1,66 @@ + +#include + +#include "citra_qt/bootmanager.h" +#include "citra_qt/overlay.h" +#include "citra_qt/stylus.h" + +Overlay::Overlay(GRenderWindow* parent) : QWidget(parent), parent(parent) { + // setWindowFlags(Qt::Widget | Qt::FramelessWindowHint); + // // setParent(0); // Create TopLevel-Widget + // setAttribute(Qt::WA_NoSystemBackground, true); + // setAttribute(Qt::WA_TranslucentBackground, true); + // setWindowFlags(Qt::FramelessWindowHint | Qt::ToolTip | Qt::WindowStaysOnTopHint); + setWindowFlags(Qt::FramelessWindowHint | Qt::ToolTip); + setAttribute(Qt::WA_TransparentForMouseEvents); + setAttribute(Qt::WA_NoSystemBackground); + setAttribute(Qt::WA_TranslucentBackground); + setFocusPolicy(Qt::NoFocus); + setMouseTracking(false); + move(0, 0); +} + +void Overlay::keyPressEvent(QKeyEvent* event) { + parent->keyPressEvent(event); +} + +void Overlay::keyReleaseEvent(QKeyEvent* event) { + parent->keyReleaseEvent(event); +} + +void Overlay::mousePressEvent(QMouseEvent* event) { + parent->mousePressEvent(event); + QApplication::setActiveWindow(window()); +} +void Overlay::mouseMoveEvent(QMouseEvent* event) { + parent->mouseMoveEvent(event); + // window()->setFocus(); + QApplication::setActiveWindow(window()); +} +void Overlay::mouseDoubleClickEvent(QMouseEvent* event) { + parent->mouseDoubleClickEvent(event); + // window()->setFocus(); + QApplication::setActiveWindow(window()); +} +void Overlay::mouseReleaseEvent(QMouseEvent* event) { + parent->mouseReleaseEvent(event); + // window()->setFocus(); + QApplication::setActiveWindow(window()); +} + +void Overlay::paintEvent(QPaintEvent* ev) { + Stylus* stylus = parent->GetStylus(); + QPainter painter(this); + // set the viewport to match the screen size + // painter.setWindow(QApplication::desktop()->screenGeometry()); + painter.setRenderHint(QPainter::Antialiasing, true); + // painter.setPen(palette().dark().color()); + // painter.setBrush(Qt::NoBrush); + // painter.drawRect(QRect(0, 0, width() - 1, height() - 1)); + // QPoint p = stylus->GetTouchPoint(); + // painter.drawEllipse(QRect(p.x() - 5, p.y() - 5, 10, 10)); + QTransform t = stylus->GetTransform(); + painter.setTransform(t); + painter.drawPixmap(0, 0, stylus->GetPix()); + // painter.drawRect(0, 0, stylus->GetRect().width(), stylus->GetRect().height()); +} \ No newline at end of file diff --git a/src/citra_qt/overlay.h b/src/citra_qt/overlay.h new file mode 100644 index 000000000..7d4a115b3 --- /dev/null +++ b/src/citra_qt/overlay.h @@ -0,0 +1,32 @@ +// Copyright 20XX Citra Emulator Project +// Licensed under GPLv2 or any later version (but not GPLv824 cause that one sucked) +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +class Stylus; +class GRenderWindow; + +class Overlay : public QWidget { + Q_OBJECT +public: + Overlay(GRenderWindow* parent); + +protected: + void paintEvent(QPaintEvent* ev) override; + + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void mouseDoubleClickEvent(QMouseEvent* event) override; + + void keyPressEvent(QKeyEvent* event); + void keyReleaseEvent(QKeyEvent* event); + +private: + GRenderWindow* parent; + Stylus* stylus; +}; \ No newline at end of file diff --git a/src/citra_qt/resources.qrc b/src/citra_qt/resources.qrc new file mode 100644 index 000000000..1d081b2aa --- /dev/null +++ b/src/citra_qt/resources.qrc @@ -0,0 +1,7 @@ + + + + ../../dist/citra.ico + ../../dist/stylus.png + + \ No newline at end of file diff --git a/src/citra_qt/stylus.cpp b/src/citra_qt/stylus.cpp new file mode 100644 index 000000000..fcac8fe94 --- /dev/null +++ b/src/citra_qt/stylus.cpp @@ -0,0 +1,84 @@ +#include +#include + +#include "citra_qt/stylus.h" +#include "common/logging/log.h" + +Stylus::Stylus() { + pic = QPixmap(":stylus.png"); + x = QApplication::desktop()->screenGeometry().width() / 2; + y = QApplication::desktop()->screenGeometry().height() / 2; + rotation = 0; +} + +Stylus::~Stylus() {} + +void Stylus::SetHoldPoint(QPoint p) { + // hold = QPoint(p.x() - x, p.y() - y); + diff = QPoint(p.x() - x, p.y() - y); + hold = p; +} + +void Stylus::Rotate(QPoint n) { + QPoint center = GetCenter(); + // get the angle from the center to the initial click point + qreal init_x = hold.x() - center.x(); + qreal init_y = hold.y() - center.y(); + qreal initial_angle = std::atan2(init_y, init_x); + qreal x = n.x() - center.x(); + qreal y = n.y() - center.y(); + + qreal mv_angle = std::atan2(y, x); + + // get the changed angle + rotation = (mv_angle - initial_angle) * 180 / M_PI; + + if (std::fabs(rotation) > 360.0) { + rotation = fmod(rotation, 360); + } +} + +bool Stylus::IsNearCenter(QPoint n) { + // QPoint c = GetCenter(); + // return (n - c).manhattanLength() < 75; + // jokes on you i'm now checking to see if its the top half or the bottom half + QTransform t = GetTransform(); + QRect top = QRect(0, 0, pic.width(), pic.height() / 2); + return t.mapRect(top).contains(n); +} + +QPoint Stylus::GetCenter() { + return QRect(x, y, pic.width(), pic.height()).center(); +} + +void Stylus::UpdatePosition(unsigned x, unsigned y) { + this->x = x - diff.x(); + this->y = y - diff.y(); +} + +QTransform Stylus::GetTransform() { + QTransform t; + t.translate(x, y); + t.rotate(rotation); + return t; +} + +QRect Stylus::GetRect() { + return QRect(0, 0, pic.width(), pic.height()); +} + +QPolygon Stylus::GetPoly() { + QTransform t = GetTransform(); + QRect r = GetRect(); + return t.mapToPolygon(r); +} + +bool Stylus::Contains(QPoint pos) { + QPolygon p = GetPoly(); + return p.containsPoint(pos, Qt::OddEvenFill); +} + +QPoint Stylus::GetTouchPoint() { + QTransform t = GetTransform(); + return t.map(QPoint(pic.width() / 2, pic.height() - 10)); +} \ No newline at end of file diff --git a/src/citra_qt/stylus.h b/src/citra_qt/stylus.h new file mode 100644 index 000000000..c714ab88e --- /dev/null +++ b/src/citra_qt/stylus.h @@ -0,0 +1,61 @@ +// Copyright 20XX Citra Emulator Project +// Licensed under GPLv2 or any later version (but not GPLv824 cause that one sucked) +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +enum class StylusState : unsigned { ROTATE, HOLD, DROP, COUNT }; + +class Stylus { +public: + Stylus(); + ~Stylus(); + StylusState GetState() { + return state; + } + + void Rotate(QPoint p); + + void SetHoldPoint(QPoint p); + + void SetState(StylusState s) { + state = s; + } + void UpdatePosition(unsigned x, unsigned y); + unsigned GetX() { + return x; + } + unsigned GetY() { + return y; + } + + QPixmap GetPix() { + return pic; + } + + QTransform GetTransform(); + + QPolygon GetPoly(); + + bool Contains(QPoint p); + + QPoint GetTouchPoint(); + + bool IsNearCenter(QPoint p); + + QPoint GetCenter(); + + QRect GetRect(); + +private: + unsigned x; + unsigned y; + qreal rotation; + QPixmap pic; + StylusState state; + QPoint hold; + QPoint diff; +}; \ No newline at end of file