diff options
author | Narr the Reg <juangerman-13@hotmail.com> | 2024-01-05 03:37:43 +0100 |
---|---|---|
committer | Narr the Reg <juangerman-13@hotmail.com> | 2024-01-05 18:41:15 +0100 |
commit | ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613 (patch) | |
tree | 3b95cbb74be05f0ce7a007353f1f9f95e1ed3901 /src/hid_core/resources/touch_screen | |
parent | Merge pull request #12437 from ameerj/gl-amd-fixes (diff) | |
download | yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.gz yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.bz2 yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.lz yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.xz yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.zst yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.zip |
Diffstat (limited to 'src/hid_core/resources/touch_screen')
-rw-r--r-- | src/hid_core/resources/touch_screen/gesture.cpp | 366 | ||||
-rw-r--r-- | src/hid_core/resources/touch_screen/gesture.h | 87 | ||||
-rw-r--r-- | src/hid_core/resources/touch_screen/gesture_types.h | 77 | ||||
-rw-r--r-- | src/hid_core/resources/touch_screen/touch_screen.cpp | 132 | ||||
-rw-r--r-- | src/hid_core/resources/touch_screen/touch_screen.h | 43 | ||||
-rw-r--r-- | src/hid_core/resources/touch_screen/touch_types.h | 90 |
6 files changed, 795 insertions, 0 deletions
diff --git a/src/hid_core/resources/touch_screen/gesture.cpp b/src/hid_core/resources/touch_screen/gesture.cpp new file mode 100644 index 000000000..0ecc0941f --- /dev/null +++ b/src/hid_core/resources/touch_screen/gesture.cpp @@ -0,0 +1,366 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/math_util.h" +#include "common/settings.h" +#include "core/frontend/emu_window.h" +#include "hid_core/frontend/emulated_console.h" +#include "hid_core/hid_core.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/shared_memory_format.h" +#include "hid_core/resources/touch_screen/gesture.h" + +namespace Service::HID { +// HW is around 700, value is set to 400 to make it easier to trigger with mouse +constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s +constexpr f32 angle_threshold = 0.015f; // Threshold in radians +constexpr f32 pinch_threshold = 0.5f; // Threshold in pixels +constexpr f32 press_delay = 0.5f; // Time in seconds +constexpr f32 double_tap_delay = 0.35f; // Time in seconds + +constexpr f32 Square(s32 num) { + return static_cast<f32>(num * num); +} + +Gesture::Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) { + console = hid_core.GetEmulatedConsole(); +} +Gesture::~Gesture() = default; + +void Gesture::OnInit() { + std::scoped_lock shared_lock{*shared_mutex}; + const u64 aruid = applet_resource->GetActiveAruid(); + auto* data = applet_resource->GetAruidData(aruid); + + if (data == nullptr || !data->flag.is_assigned) { + return; + } + + shared_memory = &data->shared_memory_format->gesture; + shared_memory->gesture_lifo.buffer_count = 0; + shared_memory->gesture_lifo.buffer_tail = 0; + force_update = true; +} + +void Gesture::OnRelease() {} + +void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { + std::scoped_lock shared_lock{*shared_mutex}; + const u64 aruid = applet_resource->GetActiveAruid(); + auto* data = applet_resource->GetAruidData(aruid); + + if (data == nullptr || !data->flag.is_assigned) { + return; + } + + shared_memory = &data->shared_memory_format->gesture; + + if (!IsControllerActivated()) { + shared_memory->gesture_lifo.buffer_count = 0; + shared_memory->gesture_lifo.buffer_tail = 0; + return; + } + + ReadTouchInput(); + + GestureProperties gesture = GetGestureProperties(); + f32 time_difference = + static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) / + (1000 * 1000 * 1000); + + // Only update if necessary + if (!ShouldUpdateGesture(gesture, time_difference)) { + return; + } + + last_update_timestamp = shared_memory->gesture_lifo.timestamp; + UpdateGestureSharedMemory(gesture, time_difference); +} + +void Gesture::ReadTouchInput() { + if (!Settings::values.touchscreen.enabled) { + fingers = {}; + return; + } + + const auto touch_status = console->GetTouch(); + for (std::size_t id = 0; id < fingers.size(); ++id) { + fingers[id] = touch_status[id]; + } +} + +bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference) { + const auto& last_entry = GetLastGestureEntry(); + if (force_update) { + force_update = false; + return true; + } + + // Update if coordinates change + for (size_t id = 0; id < MAX_POINTS; id++) { + if (gesture.points[id] != last_gesture.points[id]) { + return true; + } + } + + // Update on press and hold event after 0.5 seconds + if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 && + time_difference > press_delay) { + return enable_press_and_tap; + } + + return false; +} + +void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) { + GestureType type = GestureType::Idle; + GestureAttribute attributes{}; + + const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state; + + // Reset next state to default + next_state.sampling_number = last_entry.sampling_number + 1; + next_state.delta = {}; + next_state.vel_x = 0; + next_state.vel_y = 0; + next_state.direction = GestureDirection::None; + next_state.rotation_angle = 0; + next_state.scale = 0; + + if (gesture.active_points > 0) { + if (last_gesture.active_points == 0) { + NewGesture(gesture, type, attributes); + } else { + UpdateExistingGesture(gesture, type, time_difference); + } + } else { + EndGesture(gesture, last_gesture, type, attributes, time_difference); + } + + // Apply attributes + next_state.detection_count = gesture.detection_count; + next_state.type = type; + next_state.attributes = attributes; + next_state.pos = gesture.mid_point; + next_state.point_count = static_cast<s32>(gesture.active_points); + next_state.points = gesture.points; + last_gesture = gesture; + + shared_memory->gesture_lifo.WriteNextEntry(next_state); +} + +void Gesture::NewGesture(GestureProperties& gesture, GestureType& type, + GestureAttribute& attributes) { + const auto& last_entry = GetLastGestureEntry(); + + gesture.detection_count++; + type = GestureType::Touch; + + // New touch after cancel is not considered new + if (last_entry.type != GestureType::Cancel) { + attributes.is_new_touch.Assign(1); + enable_press_and_tap = true; + } +} + +void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type, + f32 time_difference) { + const auto& last_entry = GetLastGestureEntry(); + + // Promote to pan type if touch moved + for (size_t id = 0; id < MAX_POINTS; id++) { + if (gesture.points[id] != last_gesture.points[id]) { + type = GestureType::Pan; + break; + } + } + + // Number of fingers changed cancel the last event and clear data + if (gesture.active_points != last_gesture.active_points) { + type = GestureType::Cancel; + enable_press_and_tap = false; + gesture.active_points = 0; + gesture.mid_point = {}; + gesture.points.fill({}); + return; + } + + // Calculate extra parameters of panning + if (type == GestureType::Pan) { + UpdatePanEvent(gesture, last_gesture, type, time_difference); + return; + } + + // Promote to press type + if (last_entry.type == GestureType::Touch) { + type = GestureType::Press; + } +} + +void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, + GestureType& type, GestureAttribute& attributes, f32 time_difference) { + const auto& last_entry = GetLastGestureEntry(); + + if (last_gesture_props.active_points != 0) { + switch (last_entry.type) { + case GestureType::Touch: + if (enable_press_and_tap) { + SetTapEvent(gesture, last_gesture_props, type, attributes); + return; + } + type = GestureType::Cancel; + force_update = true; + break; + case GestureType::Press: + case GestureType::Tap: + case GestureType::Swipe: + case GestureType::Pinch: + case GestureType::Rotate: + type = GestureType::Complete; + force_update = true; + break; + case GestureType::Pan: + EndPanEvent(gesture, last_gesture_props, type, time_difference); + break; + default: + break; + } + return; + } + if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) { + gesture.detection_count++; + } +} + +void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, + GestureType& type, GestureAttribute& attributes) { + type = GestureType::Tap; + gesture = last_gesture_props; + force_update = true; + f32 tap_time_difference = + static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000); + last_tap_timestamp = last_update_timestamp; + if (tap_time_difference < double_tap_delay) { + attributes.is_double_tap.Assign(1); + } +} + +void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, + GestureType& type, f32 time_difference) { + const auto& last_entry = GetLastGestureEntry(); + + next_state.delta = gesture.mid_point - last_entry.pos; + next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference; + next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference; + last_pan_time_difference = time_difference; + + // Promote to pinch type + if (std::abs(gesture.average_distance - last_gesture_props.average_distance) > + pinch_threshold) { + type = GestureType::Pinch; + next_state.scale = gesture.average_distance / last_gesture_props.average_distance; + } + + const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) / + (1 + (gesture.angle * last_gesture_props.angle))); + // Promote to rotate type + if (std::abs(angle_between_two_lines) > angle_threshold) { + type = GestureType::Rotate; + next_state.scale = 0; + next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI; + } +} + +void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, + GestureType& type, f32 time_difference) { + const auto& last_entry = GetLastGestureEntry(); + next_state.vel_x = + static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); + next_state.vel_y = + static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference); + const f32 curr_vel = + std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y)); + + // Set swipe event with parameters + if (curr_vel > swipe_threshold) { + SetSwipeEvent(gesture, last_gesture_props, type); + return; + } + + // End panning without swipe + type = GestureType::Complete; + next_state.vel_x = 0; + next_state.vel_y = 0; + force_update = true; +} + +void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, + GestureType& type) { + const auto& last_entry = GetLastGestureEntry(); + + type = GestureType::Swipe; + gesture = last_gesture_props; + force_update = true; + next_state.delta = last_entry.delta; + + if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) { + if (next_state.delta.x > 0) { + next_state.direction = GestureDirection::Right; + return; + } + next_state.direction = GestureDirection::Left; + return; + } + if (next_state.delta.y > 0) { + next_state.direction = GestureDirection::Down; + return; + } + next_state.direction = GestureDirection::Up; +} + +const GestureState& Gesture::GetLastGestureEntry() const { + return shared_memory->gesture_lifo.ReadCurrentEntry().state; +} + +GestureProperties Gesture::GetGestureProperties() { + GestureProperties gesture; + std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers; + const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), + [](const auto& finger) { return finger.pressed; }); + gesture.active_points = + static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); + + for (size_t id = 0; id < gesture.active_points; ++id) { + const auto& [active_x, active_y] = active_fingers[id].position; + gesture.points[id] = { + .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width), + .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height), + }; + + // Hack: There is no touch in docked but games still allow it + if (Settings::IsDockedMode()) { + gesture.points[id] = { + .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width), + .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height), + }; + } + + gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points); + gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points); + } + + for (size_t id = 0; id < gesture.active_points; ++id) { + const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) + + Square(gesture.mid_point.y - gesture.points[id].y)); + gesture.average_distance += distance / static_cast<f32>(gesture.active_points); + } + + gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y), + static_cast<f32>(gesture.mid_point.x - gesture.points[0].x)); + + gesture.detection_count = last_gesture.detection_count; + + return gesture; +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/touch_screen/gesture.h b/src/hid_core/resources/touch_screen/gesture.h new file mode 100644 index 000000000..32e9a8690 --- /dev/null +++ b/src/hid_core/resources/touch_screen/gesture.h @@ -0,0 +1,87 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "common/common_types.h" +#include "hid_core/resources/controller_base.h" +#include "hid_core/resources/touch_screen/touch_types.h" + +namespace Core::HID { +class EmulatedConsole; +} + +namespace Service::HID { +struct GestureSharedMemoryFormat; + +class Gesture final : public ControllerBase { +public: + explicit Gesture(Core::HID::HIDCore& hid_core_); + ~Gesture() override; + + // Called when the controller is initialized + void OnInit() override; + + // When the controller is released + void OnRelease() override; + + // When the controller is requesting an update for the shared memory + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; + +private: + // Reads input from all available input engines + void ReadTouchInput(); + + // Returns true if gesture state needs to be updated + bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference); + + // Updates the shared memory to the next state + void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference); + + // Initializes new gesture + void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes); + + // Updates existing gesture state + void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference); + + // Terminates exiting gesture + void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, + GestureType& type, GestureAttribute& attributes, f32 time_difference); + + // Set current event to a tap event + void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, + GestureType& type, GestureAttribute& attributes); + + // Calculates and set the extra parameters related to a pan event + void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, + GestureType& type, f32 time_difference); + + // Terminates the pan event + void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, + GestureType& type, f32 time_difference); + + // Set current event to a swipe event + void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, + GestureType& type); + + // Retrieves the last gesture entry, as indicated by shared memory indices. + [[nodiscard]] const GestureState& GetLastGestureEntry() const; + + // Returns the average distance, angle and middle point of the active fingers + GestureProperties GetGestureProperties(); + + GestureState next_state{}; + GestureSharedMemoryFormat* shared_memory; + Core::HID::EmulatedConsole* console = nullptr; + + std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{}; + GestureProperties last_gesture{}; + s64 last_update_timestamp{}; + s64 last_tap_timestamp{}; + f32 last_pan_time_difference{}; + bool force_update{false}; + bool enable_press_and_tap{false}; +}; +} // namespace Service::HID diff --git a/src/hid_core/resources/touch_screen/gesture_types.h b/src/hid_core/resources/touch_screen/gesture_types.h new file mode 100644 index 000000000..b4f034cd3 --- /dev/null +++ b/src/hid_core/resources/touch_screen/gesture_types.h @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <array> +#include "common/bit_field.h" +#include "common/common_types.h" +#include "common/point.h" + +namespace Service::HID { +static constexpr size_t MAX_FINGERS = 16; +static constexpr size_t MAX_POINTS = 4; + +// This is nn::hid::GestureType +enum class GestureType : u32 { + Idle, // Nothing touching the screen + Complete, // Set at the end of a touch event + Cancel, // Set when the number of fingers change + Touch, // A finger just touched the screen + Press, // Set if last type is touch and the finger hasn't moved + Tap, // Fast press then release + Pan, // All points moving together across the screen + Swipe, // Fast press movement and release of a single point + Pinch, // All points moving away/closer to the midpoint + Rotate, // All points rotating from the midpoint +}; + +// This is nn::hid::GestureDirection +enum class GestureDirection : u32 { + None, + Left, + Up, + Right, + Down, +}; + +// This is nn::hid::GestureAttribute +struct GestureAttribute { + union { + u32 raw{}; + + BitField<4, 1, u32> is_new_touch; + BitField<8, 1, u32> is_double_tap; + }; +}; +static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size"); + +// This is nn::hid::GestureState +struct GestureState { + s64 sampling_number{}; + s64 detection_count{}; + GestureType type{GestureType::Idle}; + GestureDirection direction{GestureDirection::None}; + Common::Point<s32> pos{}; + Common::Point<s32> delta{}; + f32 vel_x{}; + f32 vel_y{}; + GestureAttribute attributes{}; + f32 scale{}; + f32 rotation_angle{}; + s32 point_count{}; + std::array<Common::Point<s32>, 4> points{}; +}; +static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); + +struct GestureProperties { + std::array<Common::Point<s32>, MAX_POINTS> points{}; + std::size_t active_points{}; + Common::Point<s32> mid_point{}; + s64 detection_count{}; + u64 delta_time{}; + f32 average_distance{}; + f32 angle{}; +}; + +} // namespace Service::HID diff --git a/src/hid_core/resources/touch_screen/touch_screen.cpp b/src/hid_core/resources/touch_screen/touch_screen.cpp new file mode 100644 index 000000000..48d956c51 --- /dev/null +++ b/src/hid_core/resources/touch_screen/touch_screen.cpp @@ -0,0 +1,132 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include "common/common_types.h" +#include "common/settings.h" +#include "core/core_timing.h" +#include "core/frontend/emu_window.h" +#include "hid_core/frontend/emulated_console.h" +#include "hid_core/hid_core.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/shared_memory_format.h" +#include "hid_core/resources/touch_screen/touch_screen.h" + +namespace Service::HID { + +TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_) + : ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width), + touchscreen_height(Layout::ScreenUndocked::Height) { + console = hid_core.GetEmulatedConsole(); +} + +TouchScreen::~TouchScreen() = default; + +void TouchScreen::OnInit() {} + +void TouchScreen::OnRelease() {} + +void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { + const u64 aruid = applet_resource->GetActiveAruid(); + auto* data = applet_resource->GetAruidData(aruid); + + if (data == nullptr || !data->flag.is_assigned) { + return; + } + + TouchScreenSharedMemoryFormat& shared_memory = data->shared_memory_format->touch_screen; + shared_memory.touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); + + if (!IsControllerActivated()) { + shared_memory.touch_screen_lifo.buffer_count = 0; + shared_memory.touch_screen_lifo.buffer_tail = 0; + return; + } + + const auto touch_status = console->GetTouch(); + for (std::size_t id = 0; id < MAX_FINGERS; id++) { + const auto& current_touch = touch_status[id]; + auto& finger = fingers[id]; + finger.id = current_touch.id; + + if (finger.attribute.start_touch) { + finger.attribute.raw = 0; + continue; + } + + if (finger.attribute.end_touch) { + finger.attribute.raw = 0; + finger.pressed = false; + continue; + } + + if (!finger.pressed && current_touch.pressed) { + // Ignore all touch fingers if disabled + if (!Settings::values.touchscreen.enabled) { + continue; + } + + 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; + const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), + [](const auto& finger) { return finger.pressed; }); + const auto active_fingers_count = + static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); + + const u64 timestamp = static_cast<u64>(core_timing.GetGlobalTimeNs().count()); + const auto& last_entry = shared_memory.touch_screen_lifo.ReadCurrentEntry().state; + + next_state.sampling_number = last_entry.sampling_number + 1; + next_state.entry_count = static_cast<s32>(active_fingers_count); + + for (std::size_t id = 0; id < MAX_FINGERS; ++id) { + auto& touch_entry = next_state.states[id]; + if (id < active_fingers_count) { + const auto& [active_x, active_y] = active_fingers[id].position; + touch_entry.position = { + .x = static_cast<u16>(active_x * static_cast<float>(touchscreen_width)), + .y = static_cast<u16>(active_y * static_cast<float>(touchscreen_height)), + }; + touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; + touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; + touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; + touch_entry.delta_time = timestamp - active_fingers[id].last_touch; + fingers[active_fingers[id].id].last_touch = timestamp; + touch_entry.finger = active_fingers[id].id; + touch_entry.attribute.raw = active_fingers[id].attribute.raw; + } else { + // Clear touch entry + touch_entry.attribute.raw = 0; + touch_entry.position = {}; + touch_entry.diameter_x = 0; + touch_entry.diameter_y = 0; + touch_entry.rotation_angle = 0; + touch_entry.delta_time = 0; + touch_entry.finger = 0; + } + } + + shared_memory.touch_screen_lifo.WriteNextEntry(next_state); +} + +void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) { + touchscreen_width = width; + touchscreen_height = height; +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/touch_screen/touch_screen.h b/src/hid_core/resources/touch_screen/touch_screen.h new file mode 100644 index 000000000..4b3824742 --- /dev/null +++ b/src/hid_core/resources/touch_screen/touch_screen.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "hid_core/hid_types.h" +#include "hid_core/resources/controller_base.h" +#include "hid_core/resources/touch_screen/touch_types.h" + +namespace Core::HID { +class EmulatedConsole; +} // namespace Core::HID + +namespace Service::HID { +struct TouchScreenSharedMemoryFormat; + +class TouchScreen final : public ControllerBase { +public: + explicit TouchScreen(Core::HID::HIDCore& hid_core_); + ~TouchScreen() override; + + // Called when the controller is initialized + void OnInit() override; + + // When the controller is released + void OnRelease() override; + + // When the controller is requesting an update for the shared memory + void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; + + void SetTouchscreenDimensions(u32 width, u32 height); + +private: + TouchScreenState next_state{}; + Core::HID::EmulatedConsole* console = nullptr; + + std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{}; + u32 touchscreen_width; + u32 touchscreen_height; +}; +} // namespace Service::HID diff --git a/src/hid_core/resources/touch_screen/touch_types.h b/src/hid_core/resources/touch_screen/touch_types.h new file mode 100644 index 000000000..97ee847da --- /dev/null +++ b/src/hid_core/resources/touch_screen/touch_types.h @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <array> + +#include <array> +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/point.h" +#include "hid_core/hid_types.h" + +namespace Service::HID { +static constexpr std::size_t MAX_FINGERS = 16; +static constexpr size_t MAX_POINTS = 4; + +// This is nn::hid::GestureType +enum class GestureType : u32 { + Idle, // Nothing touching the screen + Complete, // Set at the end of a touch event + Cancel, // Set when the number of fingers change + Touch, // A finger just touched the screen + Press, // Set if last type is touch and the finger hasn't moved + Tap, // Fast press then release + Pan, // All points moving together across the screen + Swipe, // Fast press movement and release of a single point + Pinch, // All points moving away/closer to the midpoint + Rotate, // All points rotating from the midpoint +}; + +// This is nn::hid::GestureDirection +enum class GestureDirection : u32 { + None, + Left, + Up, + Right, + Down, +}; + +// This is nn::hid::GestureAttribute +struct GestureAttribute { + union { + u32 raw{}; + + BitField<4, 1, u32> is_new_touch; + BitField<8, 1, u32> is_double_tap; + }; +}; +static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size"); + +// This is nn::hid::GestureState +struct GestureState { + s64 sampling_number{}; + s64 detection_count{}; + GestureType type{GestureType::Idle}; + GestureDirection direction{GestureDirection::None}; + Common::Point<s32> pos{}; + Common::Point<s32> delta{}; + f32 vel_x{}; + f32 vel_y{}; + GestureAttribute attributes{}; + f32 scale{}; + f32 rotation_angle{}; + s32 point_count{}; + std::array<Common::Point<s32>, 4> points{}; +}; +static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); + +struct GestureProperties { + std::array<Common::Point<s32>, MAX_POINTS> points{}; + std::size_t active_points{}; + Common::Point<s32> mid_point{}; + s64 detection_count{}; + u64 delta_time{}; + f32 average_distance{}; + f32 angle{}; +}; + +// This is nn::hid::TouchScreenState +struct TouchScreenState { + s64 sampling_number{}; + s32 entry_count{}; + INSERT_PADDING_BYTES(4); // Reserved + std::array<Core::HID::TouchState, MAX_FINGERS> states{}; +}; +static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size"); + +} // namespace Service::HID |