Merge pull request #8372 from german77/touch
input_common: touch: Rewrite touch driver to support multiple touch points
This commit is contained in:
commit
439e621674
|
@ -27,12 +27,19 @@ void EmulatedConsole::SetTouchParams() {
|
|||
// We can't use mouse as touch if native mouse is enabled
|
||||
touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
|
||||
}
|
||||
touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0"};
|
||||
touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1"};
|
||||
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
|
||||
Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
|
||||
Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"};
|
||||
|
||||
const auto button_index =
|
||||
static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
|
||||
#include "common/input.h"
|
||||
|
@ -196,6 +197,9 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus&
|
|||
x = std::clamp(x, 0.0f, 1.0f);
|
||||
y = std::clamp(y, 0.0f, 1.0f);
|
||||
|
||||
// Limit id to maximum number of fingers
|
||||
status.id = std::clamp(status.id, 0, 16);
|
||||
|
||||
if (status.pressed.inverted) {
|
||||
status.pressed.value = !status.pressed.value;
|
||||
}
|
||||
|
|
|
@ -44,7 +44,6 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
|
|||
for (std::size_t id = 0; id < MAX_FINGERS; id++) {
|
||||
const auto& current_touch = touch_status[id];
|
||||
auto& finger = fingers[id];
|
||||
finger.position = current_touch.position;
|
||||
finger.id = current_touch.id;
|
||||
|
||||
if (finger.attribute.start_touch) {
|
||||
|
@ -61,13 +60,18 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
|
|||
if (!finger.pressed && current_touch.pressed) {
|
||||
finger.attribute.start_touch.Assign(1);
|
||||
finger.pressed = true;
|
||||
finger.position = current_touch.position;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (finger.pressed && !current_touch.pressed) {
|
||||
finger.attribute.raw = 0;
|
||||
finger.attribute.end_touch.Assign(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only update position if touch is not on a special frame
|
||||
finger.position = current_touch.position;
|
||||
}
|
||||
|
||||
std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers;
|
||||
|
|
|
@ -14,38 +14,93 @@ constexpr PadIdentifier identifier = {
|
|||
|
||||
TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
PreSetController(identifier);
|
||||
ReleaseAllTouch();
|
||||
}
|
||||
|
||||
void TouchScreen::TouchMoved(float x, float y, std::size_t finger) {
|
||||
if (finger >= 16) {
|
||||
void TouchScreen::TouchMoved(float x, float y, std::size_t finger_id) {
|
||||
const auto index = GetIndexFromFingerId(finger_id);
|
||||
if (!index) {
|
||||
// Touch doesn't exist handle it as a new one
|
||||
TouchPressed(x, y, finger_id);
|
||||
return;
|
||||
}
|
||||
TouchPressed(x, y, finger);
|
||||
const auto i = index.value();
|
||||
fingers[i].is_active = true;
|
||||
SetButton(identifier, static_cast<int>(i), true);
|
||||
SetAxis(identifier, static_cast<int>(i * 2), x);
|
||||
SetAxis(identifier, static_cast<int>(i * 2 + 1), y);
|
||||
}
|
||||
|
||||
void TouchScreen::TouchPressed(float x, float y, std::size_t finger) {
|
||||
if (finger >= 16) {
|
||||
void TouchScreen::TouchPressed(float x, float y, std::size_t finger_id) {
|
||||
if (GetIndexFromFingerId(finger_id)) {
|
||||
// Touch already exist. Just update the data
|
||||
TouchMoved(x, y, finger_id);
|
||||
return;
|
||||
}
|
||||
SetButton(identifier, static_cast<int>(finger), true);
|
||||
SetAxis(identifier, static_cast<int>(finger * 2), x);
|
||||
SetAxis(identifier, static_cast<int>(finger * 2 + 1), y);
|
||||
const auto index = GetNextFreeIndex();
|
||||
if (!index) {
|
||||
// No free entries. Ignore input
|
||||
return;
|
||||
}
|
||||
const auto i = index.value();
|
||||
fingers[i].is_enabled = true;
|
||||
fingers[i].finger_id = finger_id;
|
||||
TouchMoved(x, y, finger_id);
|
||||
}
|
||||
|
||||
void TouchScreen::TouchReleased(std::size_t finger) {
|
||||
if (finger >= 16) {
|
||||
void TouchScreen::TouchReleased(std::size_t finger_id) {
|
||||
const auto index = GetIndexFromFingerId(finger_id);
|
||||
if (!index) {
|
||||
return;
|
||||
}
|
||||
SetButton(identifier, static_cast<int>(finger), false);
|
||||
SetAxis(identifier, static_cast<int>(finger * 2), 0.0f);
|
||||
SetAxis(identifier, static_cast<int>(finger * 2 + 1), 0.0f);
|
||||
const auto i = index.value();
|
||||
fingers[i].is_enabled = false;
|
||||
SetButton(identifier, static_cast<int>(i), false);
|
||||
SetAxis(identifier, static_cast<int>(i * 2), 0.0f);
|
||||
SetAxis(identifier, static_cast<int>(i * 2 + 1), 0.0f);
|
||||
}
|
||||
|
||||
std::optional<std::size_t> TouchScreen::GetIndexFromFingerId(std::size_t finger_id) const {
|
||||
for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) {
|
||||
const auto& finger = fingers[index];
|
||||
if (!finger.is_enabled) {
|
||||
continue;
|
||||
}
|
||||
if (finger.finger_id == finger_id) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::size_t> TouchScreen::GetNextFreeIndex() const {
|
||||
for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) {
|
||||
if (!fingers[index].is_enabled) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void TouchScreen::ClearActiveFlag() {
|
||||
for (auto& finger : fingers) {
|
||||
finger.is_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TouchScreen::ReleaseInactiveTouch() {
|
||||
for (const auto& finger : fingers) {
|
||||
if (!finger.is_active) {
|
||||
TouchReleased(finger.finger_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TouchScreen::ReleaseAllTouch() {
|
||||
for (int index = 0; index < 16; ++index) {
|
||||
SetButton(identifier, index, false);
|
||||
SetAxis(identifier, index * 2, 0.0f);
|
||||
SetAxis(identifier, index * 2 + 1, 0.0f);
|
||||
for (const auto& finger : fingers) {
|
||||
if (finger.is_enabled) {
|
||||
TouchReleased(finger.finger_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,41 +3,65 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
/**
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
* A touch device factory representing a touch screen. It receives touch events and forward them
|
||||
* to all touch devices it created.
|
||||
*/
|
||||
class TouchScreen final : public InputEngine {
|
||||
public:
|
||||
explicit TouchScreen(std::string input_engine_);
|
||||
|
||||
/**
|
||||
* Signals that mouse has moved.
|
||||
* @param x the x-coordinate of the cursor
|
||||
* @param y the y-coordinate of the cursor
|
||||
* @param center_x the x-coordinate of the middle of the screen
|
||||
* @param center_y the y-coordinate of the middle of the screen
|
||||
* Signals that touch has moved and marks this touch point as active
|
||||
* @param x new horizontal position
|
||||
* @param y new vertical position
|
||||
* @param finger_id of the touch point to be updated
|
||||
*/
|
||||
void TouchMoved(float x, float y, std::size_t finger);
|
||||
void TouchMoved(float x, float y, std::size_t finger_id);
|
||||
|
||||
/**
|
||||
* Sets the status of all buttons bound with the key to pressed
|
||||
* @param key_code the code of the key to press
|
||||
* Signals and creates a new touch point with this finger id
|
||||
* @param x starting horizontal position
|
||||
* @param y starting vertical position
|
||||
* @param finger_id to be assigned to the new touch point
|
||||
*/
|
||||
void TouchPressed(float x, float y, std::size_t finger);
|
||||
void TouchPressed(float x, float y, std::size_t finger_id);
|
||||
|
||||
/**
|
||||
* Sets the status of all buttons bound with the key to released
|
||||
* @param key_code the code of the key to release
|
||||
* Signals and resets the touch point related to the this finger id
|
||||
* @param finger_id to be released
|
||||
*/
|
||||
void TouchReleased(std::size_t finger);
|
||||
void TouchReleased(std::size_t finger_id);
|
||||
|
||||
/// Resets the active flag for each touch point
|
||||
void ClearActiveFlag();
|
||||
|
||||
/// Releases all touch that haven't been marked as active
|
||||
void ReleaseInactiveTouch();
|
||||
|
||||
/// Resets all inputs to their initial value
|
||||
void ReleaseAllTouch();
|
||||
|
||||
private:
|
||||
static constexpr std::size_t MAX_FINGER_COUNT = 16;
|
||||
|
||||
struct TouchStatus {
|
||||
std::size_t finger_id{};
|
||||
bool is_enabled{};
|
||||
bool is_active{};
|
||||
};
|
||||
|
||||
std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const;
|
||||
|
||||
std::optional<std::size_t> GetNextFreeIndex() const;
|
||||
|
||||
std::array<TouchStatus, MAX_FINGER_COUNT> fingers{};
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
|
|
|
@ -772,65 +772,25 @@ void GRenderWindow::wheelEvent(QWheelEvent* event) {
|
|||
void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
|
||||
QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints();
|
||||
for (const auto& touch_point : touch_points) {
|
||||
if (!TouchUpdate(touch_point)) {
|
||||
TouchStart(touch_point);
|
||||
}
|
||||
const auto [x, y] = ScaleTouch(touch_point.pos());
|
||||
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
|
||||
input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, touch_point.id());
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
|
||||
QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints();
|
||||
input_subsystem->GetTouchScreen()->ClearActiveFlag();
|
||||
for (const auto& touch_point : touch_points) {
|
||||
if (!TouchUpdate(touch_point)) {
|
||||
TouchStart(touch_point);
|
||||
}
|
||||
}
|
||||
// Release all inactive points
|
||||
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
|
||||
if (!TouchExist(touch_ids[id], touch_points)) {
|
||||
touch_ids[id] = 0;
|
||||
input_subsystem->GetTouchScreen()->TouchReleased(id);
|
||||
}
|
||||
const auto [x, y] = ScaleTouch(touch_point.pos());
|
||||
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
|
||||
input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, touch_point.id());
|
||||
}
|
||||
input_subsystem->GetTouchScreen()->ReleaseInactiveTouch();
|
||||
}
|
||||
|
||||
void GRenderWindow::TouchEndEvent() {
|
||||
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
|
||||
if (touch_ids[id] != 0) {
|
||||
touch_ids[id] = 0;
|
||||
input_subsystem->GetTouchScreen()->TouchReleased(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::TouchStart(const QTouchEvent::TouchPoint& touch_point) {
|
||||
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
|
||||
if (touch_ids[id] == 0) {
|
||||
touch_ids[id] = touch_point.id() + 1;
|
||||
const auto [x, y] = ScaleTouch(touch_point.pos());
|
||||
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
|
||||
input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GRenderWindow::TouchUpdate(const QTouchEvent::TouchPoint& touch_point) {
|
||||
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
|
||||
if (touch_ids[id] == static_cast<std::size_t>(touch_point.id() + 1)) {
|
||||
const auto [x, y] = ScaleTouch(touch_point.pos());
|
||||
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
|
||||
input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GRenderWindow::TouchExist(std::size_t id,
|
||||
const QList<QTouchEvent::TouchPoint>& touch_points) const {
|
||||
return std::any_of(touch_points.begin(), touch_points.end(), [id](const auto& point) {
|
||||
return id == static_cast<std::size_t>(point.id() + 1);
|
||||
});
|
||||
input_subsystem->GetTouchScreen()->ReleaseAllTouch();
|
||||
}
|
||||
|
||||
bool GRenderWindow::event(QEvent* event) {
|
||||
|
|
|
@ -217,10 +217,6 @@ private:
|
|||
void TouchUpdateEvent(const QTouchEvent* event);
|
||||
void TouchEndEvent();
|
||||
|
||||
void TouchStart(const QTouchEvent::TouchPoint& touch_point);
|
||||
bool TouchUpdate(const QTouchEvent::TouchPoint& touch_point);
|
||||
bool TouchExist(std::size_t id, const QList<QTouchEvent::TouchPoint>& touch_points) const;
|
||||
|
||||
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
|
||||
|
||||
bool InitializeOpenGL();
|
||||
|
@ -246,8 +242,6 @@ private:
|
|||
bool first_frame = false;
|
||||
InputCommon::TasInput::TasState last_tas_state;
|
||||
|
||||
std::array<std::size_t, 16> touch_ids{};
|
||||
|
||||
Core::System& system;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -93,7 +93,7 @@ void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) {
|
|||
}
|
||||
|
||||
void EmuWindow_SDL2::OnFingerUp() {
|
||||
input_subsystem->GetTouchScreen()->TouchReleased(0);
|
||||
input_subsystem->GetTouchScreen()->ReleaseAllTouch();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
|
||||
|
|
Loading…
Reference in New Issue