early-access version 1331

This commit is contained in:
pineappleEA 2021-01-17 01:28:21 +01:00
parent 1f59a2561a
commit 233493ad87
47 changed files with 527 additions and 664 deletions

View file

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 1330. This is the source code for early-access 1331.
## Legal Notice ## Legal Notice

View file

@ -1,5 +1,26 @@
<RCC> <RCC>
<qresource prefix="controller"> <qresource prefix="controller">
<file alias="dual_joycon">dual_joycon.png</file>
<file alias="dual_joycon_dark">dual_joycon_dark.png</file>
<file alias="dual_joycon_midnight">dual_joycon_midnight.png</file>
<file alias="handheld">handheld.png</file>
<file alias="handheld_dark">handheld_dark.png</file>
<file alias="handheld_midnight">handheld_midnight.png</file>
<file alias="pro_controller">pro_controller.png</file>
<file alias="pro_controller_dark">pro_controller_dark.png</file>
<file alias="pro_controller_midnight">pro_controller_midnight.png</file>
<file alias="single_joycon_left">single_joycon_left.png</file>
<file alias="single_joycon_left_dark">single_joycon_left_dark.png</file>
<file alias="single_joycon_left_midnight">single_joycon_left_midnight.png</file>
<file alias="single_joycon_right">single_joycon_right.png</file>
<file alias="single_joycon_right_dark">single_joycon_right_dark.png</file>
<file alias="single_joycon_right_midnight">single_joycon_right_midnight.png</file>
<file alias="single_joycon_left_vertical">single_joycon_left_vertical.png</file>
<file alias="single_joycon_left_vertical_dark">single_joycon_left_vertical_dark.png</file>
<file alias="single_joycon_left_vertical_midnight">single_joycon_left_vertical_midnight.png</file>
<file alias="single_joycon_right_vertical">single_joycon_right_vertical.png</file>
<file alias="single_joycon_right_vertical_dark">single_joycon_right_vertical_dark.png</file>
<file alias="single_joycon_right_vertical_midnight">single_joycon_right_vertical_midnight.png</file>
<file alias="applet_dual_joycon">applet_dual_joycon.png</file> <file alias="applet_dual_joycon">applet_dual_joycon.png</file>
<file alias="applet_dual_joycon_dark">applet_dual_joycon_dark.png</file> <file alias="applet_dual_joycon_dark">applet_dual_joycon_dark.png</file>
<file alias="applet_dual_joycon_midnight">applet_dual_joycon_midnight.png</file> <file alias="applet_dual_joycon_midnight">applet_dual_joycon_midnight.png</file>

View file

@ -38,4 +38,34 @@ u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
#endif #endif
u128 Multiply64Into128(u64 a, u64 b) {
u128 result;
#ifdef _MSC_VER
result[0] = _umul128(a, b, &result[1]);
#else
unsigned __int128 tmp = a;
tmp *= b;
std::memcpy(&result, &tmp, sizeof(u128));
#endif
return result;
}
std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) {
u64 remainder = dividend[0] % divisor;
u64 accum = dividend[0] / divisor;
if (dividend[1] == 0)
return {accum, remainder};
// We ignore dividend[1] / divisor as that overflows
const u64 first_segment = (dividend[1] % divisor) << 32;
accum += (first_segment / divisor) << 32;
const u64 second_segment = (first_segment % divisor) << 32;
accum += (second_segment / divisor);
remainder += second_segment % divisor;
if (remainder >= divisor) {
accum++;
remainder -= divisor;
}
return {accum, remainder};
}
} // namespace Common } // namespace Common

View file

@ -12,4 +12,11 @@ namespace Common {
// This function multiplies 2 u64 values and divides it by a u64 value. // This function multiplies 2 u64 values and divides it by a u64 value.
[[nodiscard]] u64 MultiplyAndDivide64(u64 a, u64 b, u64 d); [[nodiscard]] u64 MultiplyAndDivide64(u64 a, u64 b, u64 d);
// This function multiplies 2 u64 values and produces a u128 value;
[[nodiscard]] u128 Multiply64Into128(u64 a, u64 b);
// This function divides a u128 by a u32 value and produces two u64 values:
// the result of division and the remainder
[[nodiscard]] std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor);
} // namespace Common } // namespace Common

View file

@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "common/uint128.h"
#include "common/wall_clock.h" #include "common/wall_clock.h"
#ifdef ARCHITECTURE_x86_64 #ifdef ARCHITECTURE_x86_64
@ -40,11 +41,16 @@ public:
} }
u64 GetClockCycles() override { u64 GetClockCycles() override {
return GetTimeNS().count() * (emulated_clock_frequency / 1000) / 1000000; std::chrono::nanoseconds time_now = GetTimeNS();
const u128 temporary =
Common::Multiply64Into128(time_now.count(), emulated_clock_frequency);
return Common::Divide128On32(temporary, 1000000000).first;
} }
u64 GetCPUCycles() override { u64 GetCPUCycles() override {
return GetTimeNS().count() * (emulated_cpu_frequency / 1000) / 1000000; std::chrono::nanoseconds time_now = GetTimeNS();
const u128 temporary = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency);
return Common::Divide128On32(temporary, 1000000000).first;
} }
void Pause([[maybe_unused]] bool is_paused) override { void Pause([[maybe_unused]] bool is_paused) override {

View file

@ -19,6 +19,7 @@ add_library(core STATIC
core.h core.h
core_timing.cpp core_timing.cpp
core_timing.h core_timing.h
core_timing_util.cpp
core_timing_util.h core_timing_util.h
cpu_manager.cpp cpu_manager.cpp
cpu_manager.h cpu_manager.h

84
src/core/core_timing_util.cpp Executable file
View file

@ -0,0 +1,84 @@
// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "core/core_timing_util.h"
#include <cinttypes>
#include <limits>
#include "common/logging/log.h"
#include "common/uint128.h"
#include "core/hardware_properties.h"
namespace Core::Timing {
constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / Hardware::BASE_CLOCK_RATE;
s64 msToCycles(std::chrono::milliseconds ms) {
if (static_cast<u64>(ms.count() / 1000) > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (static_cast<u64>(ms.count()) > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return Hardware::BASE_CLOCK_RATE * (ms.count() / 1000);
}
return (Hardware::BASE_CLOCK_RATE * ms.count()) / 1000;
}
s64 usToCycles(std::chrono::microseconds us) {
if (static_cast<u64>(us.count() / 1000000) > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (static_cast<u64>(us.count()) > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return Hardware::BASE_CLOCK_RATE * (us.count() / 1000000);
}
return (Hardware::BASE_CLOCK_RATE * us.count()) / 1000000;
}
s64 nsToCycles(std::chrono::nanoseconds ns) {
const u128 temporal = Common::Multiply64Into128(ns.count(), Hardware::BASE_CLOCK_RATE);
return Common::Divide128On32(temporal, static_cast<u32>(1000000000)).first;
}
u64 msToClockCycles(std::chrono::milliseconds ns) {
const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
return Common::Divide128On32(temp, 1000).first;
}
u64 usToClockCycles(std::chrono::microseconds ns) {
const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
return Common::Divide128On32(temp, 1000000).first;
}
u64 nsToClockCycles(std::chrono::nanoseconds ns) {
const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
return Common::Divide128On32(temp, 1000000000).first;
}
u64 CpuCyclesToClockCycles(u64 ticks) {
const u128 temporal = Common::Multiply64Into128(ticks, Hardware::CNTFREQ);
return Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
}
std::chrono::milliseconds CyclesToMs(s64 cycles) {
const u128 temporal = Common::Multiply64Into128(cycles, 1000);
u64 ms = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
return std::chrono::milliseconds(ms);
}
std::chrono::nanoseconds CyclesToNs(s64 cycles) {
const u128 temporal = Common::Multiply64Into128(cycles, 1000000000);
u64 ns = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
return std::chrono::nanoseconds(ns);
}
std::chrono::microseconds CyclesToUs(s64 cycles) {
const u128 temporal = Common::Multiply64Into128(cycles, 1000000);
u64 us = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
return std::chrono::microseconds(us);
}
} // namespace Core::Timing

View file

@ -1,59 +1,24 @@
// Copyright 2020 yuzu Emulator Project // Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2+
// Refer to the license.txt file included. // Refer to the license.txt file included.
#pragma once #pragma once
#include <chrono> #include <chrono>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hardware_properties.h"
namespace Core::Timing { namespace Core::Timing {
namespace detail { s64 msToCycles(std::chrono::milliseconds ms);
constexpr u64 CNTFREQ_ADJUSTED = Hardware::CNTFREQ / 1000; s64 usToCycles(std::chrono::microseconds us);
constexpr u64 BASE_CLOCK_RATE_ADJUSTED = Hardware::BASE_CLOCK_RATE / 1000; s64 nsToCycles(std::chrono::nanoseconds ns);
} // namespace detail u64 msToClockCycles(std::chrono::milliseconds ns);
u64 usToClockCycles(std::chrono::microseconds ns);
u64 nsToClockCycles(std::chrono::nanoseconds ns);
std::chrono::milliseconds CyclesToMs(s64 cycles);
std::chrono::nanoseconds CyclesToNs(s64 cycles);
std::chrono::microseconds CyclesToUs(s64 cycles);
[[nodiscard]] constexpr s64 msToCycles(std::chrono::milliseconds ms) { u64 CpuCyclesToClockCycles(u64 ticks);
return ms.count() * detail::BASE_CLOCK_RATE_ADJUSTED;
}
[[nodiscard]] constexpr s64 usToCycles(std::chrono::microseconds us) {
return us.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000;
}
[[nodiscard]] constexpr s64 nsToCycles(std::chrono::nanoseconds ns) {
return ns.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000000;
}
[[nodiscard]] constexpr u64 msToClockCycles(std::chrono::milliseconds ms) {
return static_cast<u64>(ms.count()) * detail::CNTFREQ_ADJUSTED;
}
[[nodiscard]] constexpr u64 usToClockCycles(std::chrono::microseconds us) {
return us.count() * detail::CNTFREQ_ADJUSTED / 1000;
}
[[nodiscard]] constexpr u64 nsToClockCycles(std::chrono::nanoseconds ns) {
return ns.count() * detail::CNTFREQ_ADJUSTED / 1000000;
}
[[nodiscard]] constexpr u64 CpuCyclesToClockCycles(u64 ticks) {
return ticks * detail::CNTFREQ_ADJUSTED / detail::BASE_CLOCK_RATE_ADJUSTED;
}
[[nodiscard]] constexpr std::chrono::milliseconds CyclesToMs(s64 cycles) {
return std::chrono::milliseconds(cycles / detail::BASE_CLOCK_RATE_ADJUSTED);
}
[[nodiscard]] constexpr std::chrono::nanoseconds CyclesToNs(s64 cycles) {
return std::chrono::nanoseconds(cycles * 1000000 / detail::BASE_CLOCK_RATE_ADJUSTED);
}
[[nodiscard]] constexpr std::chrono::microseconds CyclesToUs(s64 cycles) {
return std::chrono::microseconds(cycles * 1000 / detail::BASE_CLOCK_RATE_ADJUSTED);
}
} // namespace Core::Timing } // namespace Core::Timing

View file

@ -21,18 +21,21 @@ public:
std::mutex mutex; std::mutex mutex;
Input::TouchStatus status; bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false
float touch_x = 0.0f; ///< Touchpad X-position
float touch_y = 0.0f; ///< Touchpad Y-position
private: private:
class Device : public Input::TouchDevice { class Device : public Input::TouchDevice {
public: public:
explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {} explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
Input::TouchStatus GetStatus() const override { std::tuple<float, float, bool> GetStatus() const override {
if (auto state = touch_state.lock()) { if (auto state = touch_state.lock()) {
std::lock_guard guard{state->mutex}; std::lock_guard guard{state->mutex};
return state->status; return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
} }
return {}; return std::make_tuple(0.0f, 0.0f, false);
} }
private: private:
@ -76,44 +79,36 @@ std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsi
return std::make_tuple(new_x, new_y); return std::make_tuple(new_x, new_y);
} }
void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) { void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) { if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
return; return;
}
if (id >= touch_state->status.size()) {
return;
}
std::lock_guard guard{touch_state->mutex}; std::lock_guard guard{touch_state->mutex};
const float x = touch_state->touch_x =
static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) / static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left); static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
const float y = touch_state->touch_y =
static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) / static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top); static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top);
touch_state->status[id] = std::make_tuple(x, y, true); touch_state->touch_pressed = true;
} }
void EmuWindow::TouchReleased(std::size_t id) { void EmuWindow::TouchReleased() {
if (id >= touch_state->status.size()) {
return;
}
std::lock_guard guard{touch_state->mutex}; std::lock_guard guard{touch_state->mutex};
touch_state->status[id] = std::make_tuple(0.0f, 0.0f, false); touch_state->touch_pressed = false;
touch_state->touch_x = 0;
touch_state->touch_y = 0;
} }
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) { void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
if (id >= touch_state->status.size()) { if (!touch_state->touch_pressed)
return;
}
if (!std::get<2>(touch_state->status[id]))
return; return;
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y); std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
TouchPressed(framebuffer_x, framebuffer_y, id); TouchPressed(framebuffer_x, framebuffer_y);
} }
void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {

View file

@ -117,23 +117,18 @@ public:
* Signal that a touch pressed event has occurred (e.g. mouse click pressed) * Signal that a touch pressed event has occurred (e.g. mouse click pressed)
* @param framebuffer_x Framebuffer x-coordinate that was pressed * @param framebuffer_x Framebuffer x-coordinate that was pressed
* @param framebuffer_y Framebuffer y-coordinate that was pressed * @param framebuffer_y Framebuffer y-coordinate that was pressed
* @param id Touch event ID
*/ */
void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id); void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y);
/** /// Signal that a touch released event has occurred (e.g. mouse click released)
* Signal that a touch released event has occurred (e.g. mouse click released) void TouchReleased();
* @param id Touch event ID
*/
void TouchReleased(std::size_t id);
/** /**
* Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window) * Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
* @param framebuffer_x Framebuffer x-coordinate * @param framebuffer_x Framebuffer x-coordinate
* @param framebuffer_y Framebuffer y-coordinate * @param framebuffer_y Framebuffer y-coordinate
* @param id Touch event ID
*/ */
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id); void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
/** /**
* Returns currently active configuration. * Returns currently active configuration.

View file

@ -21,11 +21,6 @@ enum class AnalogDirection : u8 {
UP, UP,
DOWN, DOWN,
}; };
struct AnalogProperties {
float deadzone;
float range;
float threshold;
};
/// An abstract class template for an input device (a button, an analog input, etc.). /// An abstract class template for an input device (a button, an analog input, etc.).
template <typename StatusType> template <typename StatusType>
@ -35,12 +30,6 @@ public:
virtual StatusType GetStatus() const { virtual StatusType GetStatus() const {
return {}; return {};
} }
virtual StatusType GetRawStatus() const {
return GetStatus();
}
virtual AnalogProperties GetAnalogProperties() const {
return {};
}
virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const { virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const {
return {}; return {};
} }
@ -174,11 +163,10 @@ using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common
using MotionDevice = InputDevice<MotionStatus>; using MotionDevice = InputDevice<MotionStatus>;
/** /**
* A touch status is an object that returns an array of 16 tuple elements of two floats and a bool. * A touch status is an object that returns a tuple of two floats and a bool. The floats are
* The floats are x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is * x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed.
* pressed.
*/ */
using TouchStatus = std::array<std::tuple<float, float, bool>, 16>; using TouchStatus = std::tuple<float, float, bool>;
/** /**
* A touch device is an input device that returns a touch status object * A touch device is an input device that returns a touch status object

View file

@ -100,12 +100,12 @@ public:
/** /**
* Checks whether any of the buttons in the parameter list is pressed once. * Checks whether any of the buttons in the parameter list is pressed once.
* *
* @tparam HIDButton The buttons to check. * @tparam T The buttons to check.
* *
* @returns True when at least one of the buttons is pressed once. * @returns True when at least one of the buttons is pressed once.
*/ */
template <HIDButton... T> template <HIDButton... T>
[[nodiscard]] bool IsAnyButtonPressedOnce() { [[nodiscard]] bool IsAnyButtonPressedOnce() const {
return (IsButtonPressedOnce(T) || ...); return (IsButtonPressedOnce(T) || ...);
} }
@ -121,12 +121,12 @@ public:
/** /**
* Checks whether any of the buttons in the parameter list is held down. * Checks whether any of the buttons in the parameter list is held down.
* *
* @tparam HIDButton The buttons to check. * @tparam T The buttons to check.
* *
* @returns True when at least one of the buttons is held down. * @returns True when at least one of the buttons is held down.
*/ */
template <HIDButton... T> template <HIDButton... T>
[[nodiscard]] bool IsAnyButtonHeld() { [[nodiscard]] bool IsAnyButtonHeld() const {
return (IsButtonHeld(T) || ...); return (IsButtonHeld(T) || ...);
} }

View file

@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <algorithm>
#include <cstring> #include <cstring>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/core_timing.h" #include "core/core_timing.h"
@ -17,13 +16,7 @@ constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
Controller_Touchscreen::Controller_Touchscreen(Core::System& system) : ControllerBase(system) {} Controller_Touchscreen::Controller_Touchscreen(Core::System& system) : ControllerBase(system) {}
Controller_Touchscreen::~Controller_Touchscreen() = default; Controller_Touchscreen::~Controller_Touchscreen() = default;
void Controller_Touchscreen::OnInit() { void Controller_Touchscreen::OnInit() {}
for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
mouse_finger_id[id] = MAX_FINGERS;
keyboard_finger_id[id] = MAX_FINGERS;
udp_finger_id[id] = MAX_FINGERS;
}
}
void Controller_Touchscreen::OnRelease() {} void Controller_Touchscreen::OnRelease() {}
@ -47,106 +40,38 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
cur_entry.sampling_number = last_entry.sampling_number + 1; cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number; cur_entry.sampling_number2 = cur_entry.sampling_number;
const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus(); bool pressed = false;
const Input::TouchStatus& udp_status = touch_udp_device->GetStatus(); float x, y;
for (std::size_t id = 0; id < mouse_status.size(); ++id) { std::tie(x, y, pressed) = touch_device->GetStatus();
mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]); auto& touch_entry = cur_entry.states[0];
udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]); touch_entry.attribute.raw = 0;
if (!pressed && touch_btn_device) {
std::tie(x, y, pressed) = touch_btn_device->GetStatus();
}
if (pressed && Settings::values.touchscreen.enabled) {
touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::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;
const u64 tick = core_timing.GetCPUTicks();
touch_entry.delta_time = tick - last_touch;
last_touch = tick;
touch_entry.finger = Settings::values.touchscreen.finger;
cur_entry.entry_count = 1;
} else {
cur_entry.entry_count = 0;
} }
if (Settings::values.use_touch_from_button) {
const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
for (std::size_t id = 0; id < mouse_status.size(); ++id) {
keyboard_finger_id[id] =
UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
}
}
std::array<Finger, 16> 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 tick = core_timing.GetCPUTicks();
cur_entry.entry_count = static_cast<s32_le>(active_fingers_count);
for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
auto& touch_entry = cur_entry.states[id];
if (id < active_fingers_count) {
touch_entry.x = static_cast<u16>(active_fingers[id].x * Layout::ScreenUndocked::Width);
touch_entry.y = static_cast<u16>(active_fingers[id].y * Layout::ScreenUndocked::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 = tick - active_fingers[id].last_touch;
fingers[active_fingers[id].id].last_touch = tick;
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.x = 0;
touch_entry.y = 0;
touch_entry.diameter_x = 0;
touch_entry.diameter_y = 0;
touch_entry.rotation_angle = 0;
touch_entry.delta_time = 0;
touch_entry.finger = 0;
}
}
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory)); std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory));
} }
void Controller_Touchscreen::OnLoadInputDevices() { void Controller_Touchscreen::OnLoadInputDevices() {
touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window"); touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device);
touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp"); if (Settings::values.use_touch_from_button) {
touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button"); touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
} } else {
touch_btn_device.reset();
std::optional<std::size_t> Controller_Touchscreen::GetUnusedFingerID() const {
std::size_t first_free_id = 0;
while (first_free_id < MAX_FINGERS) {
if (!fingers[first_free_id].pressed) {
return first_free_id;
} else {
first_free_id++;
}
} }
return std::nullopt;
} }
std::size_t Controller_Touchscreen::UpdateTouchInputEvent(
const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
const auto& [x, y, pressed] = touch_input;
if (pressed) {
Attributes attribute{};
if (finger_id == MAX_FINGERS) {
const auto first_free_id = GetUnusedFingerID();
if (!first_free_id) {
// Invalid finger id do nothing
return MAX_FINGERS;
}
finger_id = first_free_id.value();
fingers[finger_id].pressed = true;
fingers[finger_id].id = static_cast<u32_le>(finger_id);
attribute.start_touch.Assign(1);
}
fingers[finger_id].x = x;
fingers[finger_id].y = y;
fingers[finger_id].attribute = attribute;
return finger_id;
}
if (finger_id != MAX_FINGERS) {
if (!fingers[finger_id].attribute.end_touch) {
fingers[finger_id].attribute.end_touch.Assign(1);
fingers[finger_id].attribute.start_touch.Assign(0);
return finger_id;
}
fingers[finger_id].pressed = false;
}
return MAX_FINGERS;
}
} // namespace Service::HID } // namespace Service::HID

View file

@ -30,18 +30,6 @@ public:
void OnLoadInputDevices() override; void OnLoadInputDevices() override;
private: private:
static constexpr std::size_t MAX_FINGERS = 16;
// Returns an unused finger id, if there is no fingers available std::nullopt will be returned
std::optional<std::size_t> GetUnusedFingerID() const;
// If the touch is new it tries to assing a new finger id, if there is no fingers avaliable no
// changes will be made. Updates the coordinates if the finger id it's already set. If the touch
// ends delays the output by one frame to set the end_touch flag before finally freeing the
// finger id
std::size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
std::size_t finger_id);
struct Attributes { struct Attributes {
union { union {
u32 raw{}; u32 raw{};
@ -67,7 +55,7 @@ private:
s64_le sampling_number; s64_le sampling_number;
s64_le sampling_number2; s64_le sampling_number2;
s32_le entry_count; s32_le entry_count;
std::array<TouchState, MAX_FINGERS> states; std::array<TouchState, 16> states;
}; };
static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size"); static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size");
@ -78,23 +66,9 @@ private:
}; };
static_assert(sizeof(TouchScreenSharedMemory) == 0x3000, static_assert(sizeof(TouchScreenSharedMemory) == 0x3000,
"TouchScreenSharedMemory is an invalid size"); "TouchScreenSharedMemory is an invalid size");
struct Finger {
u64_le last_touch{};
float x{};
float y{};
u32_le id{};
bool pressed{};
Attributes attribute;
};
TouchScreenSharedMemory shared_memory{}; TouchScreenSharedMemory shared_memory{};
std::unique_ptr<Input::TouchDevice> touch_mouse_device; std::unique_ptr<Input::TouchDevice> touch_device;
std::unique_ptr<Input::TouchDevice> touch_udp_device;
std::unique_ptr<Input::TouchDevice> touch_btn_device; std::unique_ptr<Input::TouchDevice> touch_btn_device;
std::array<std::size_t, MAX_FINGERS> mouse_finger_id; s64_le last_touch{};
std::array<std::size_t, MAX_FINGERS> keyboard_finger_id;
std::array<std::size_t, MAX_FINGERS> udp_finger_id;
std::array<Finger, MAX_FINGERS> fingers;
}; };
} // namespace Service::HID } // namespace Service::HID

View file

@ -37,7 +37,6 @@ NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input,
Tegra::ChCommandHeaderList cmdlist(1); Tegra::ChCommandHeaderList cmdlist(1);
cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F}; cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F};
system.GPU().PushCommandBuffer(cmdlist); system.GPU().PushCommandBuffer(cmdlist);
system.GPU().MemoryManager().InvalidateQueuedCaches();
} }
return UnmapBuffer(input, output); return UnmapBuffer(input, output);
} }

View file

@ -193,13 +193,7 @@ NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vec
return NvResult::InvalidState; return NvResult::InvalidState;
} }
if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) { if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) {
if (vic_device) { gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
// UnmapVicFrame defers texture_cache invalidation of the frame address until
// the stream is over
gpu.MemoryManager().UnmapVicFrame(object->dma_map_addr, *size);
} else {
gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
}
} else { } else {
// This occurs quite frequently, however does not seem to impact functionality // This occurs quite frequently, however does not seem to impact functionality
LOG_DEBUG(Service_NVDRV, "invalid offset=0x{:X} dma=0x{:X}", object->addr, LOG_DEBUG(Service_NVDRV, "invalid offset=0x{:X} dma=0x{:X}", object->addr,

View file

@ -160,7 +160,6 @@ protected:
s32_le nvmap_fd{}; s32_le nvmap_fd{};
u32_le submit_timeout{}; u32_le submit_timeout{};
bool vic_device{};
std::shared_ptr<nvmap> nvmap_dev; std::shared_ptr<nvmap> nvmap_dev;
SyncpointManager& syncpoint_manager; SyncpointManager& syncpoint_manager;
std::array<u32, MaxSyncPoints> device_syncpoints{}; std::array<u32, MaxSyncPoints> device_syncpoints{};

View file

@ -12,9 +12,8 @@
namespace Service::Nvidia::Devices { namespace Service::Nvidia::Devices {
nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev, nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
SyncpointManager& syncpoint_manager) SyncpointManager& syncpoint_manager)
: nvhost_nvdec_common(system, std::move(nvmap_dev), syncpoint_manager) { : nvhost_nvdec_common(system, std::move(nvmap_dev), syncpoint_manager) {}
vic_device = true;
}
nvhost_vic::~nvhost_vic() = default; nvhost_vic::~nvhost_vic() = default;
NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {

View file

@ -195,9 +195,8 @@ struct Memory::Impl {
switch (type) { switch (type) {
case Common::PageType::Unmapped: { case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory, LOG_ERROR(HW_Memory,
"Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {}) " "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
"at PC 0x{:08X}", current_vaddr, src_addr, size);
current_vaddr, src_addr, size, system.CurrentArmInterface().GetPC());
std::memset(dest_buffer, 0, copy_amount); std::memset(dest_buffer, 0, copy_amount);
break; break;
} }
@ -241,9 +240,8 @@ struct Memory::Impl {
switch (type) { switch (type) {
case Common::PageType::Unmapped: { case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory, LOG_ERROR(HW_Memory,
"Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {}) " "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
"at PC 0x{:08X}", current_vaddr, src_addr, size);
current_vaddr, src_addr, size, system.CurrentArmInterface().GetPC());
std::memset(dest_buffer, 0, copy_amount); std::memset(dest_buffer, 0, copy_amount);
break; break;
} }
@ -293,9 +291,8 @@ struct Memory::Impl {
switch (type) { switch (type) {
case Common::PageType::Unmapped: { case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory, LOG_ERROR(HW_Memory,
"Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {}) " "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
"at PC 0x{:08X}", current_vaddr, dest_addr, size);
current_vaddr, dest_addr, size, system.CurrentArmInterface().GetPC());
break; break;
} }
case Common::PageType::Memory: { case Common::PageType::Memory: {
@ -337,9 +334,8 @@ struct Memory::Impl {
switch (type) { switch (type) {
case Common::PageType::Unmapped: { case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory, LOG_ERROR(HW_Memory,
"Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {}) " "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
"at PC 0x{:08X}", current_vaddr, dest_addr, size);
current_vaddr, dest_addr, size, system.CurrentArmInterface().GetPC());
break; break;
} }
case Common::PageType::Memory: { case Common::PageType::Memory: {
@ -387,9 +383,8 @@ struct Memory::Impl {
switch (type) { switch (type) {
case Common::PageType::Unmapped: { case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory, LOG_ERROR(HW_Memory,
"Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {}) " "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
"at PC 0x{:08X}", current_vaddr, dest_addr, size);
current_vaddr, dest_addr, size, system.CurrentArmInterface().GetPC());
break; break;
} }
case Common::PageType::Memory: { case Common::PageType::Memory: {
@ -434,9 +429,8 @@ struct Memory::Impl {
switch (type) { switch (type) {
case Common::PageType::Unmapped: { case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory, LOG_ERROR(HW_Memory,
"Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {}) " "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
"at PC 0x{:08X}", current_vaddr, src_addr, size);
current_vaddr, src_addr, size, system.CurrentArmInterface().GetPC());
ZeroBlock(process, dest_addr, copy_amount); ZeroBlock(process, dest_addr, copy_amount);
break; break;
} }
@ -607,8 +601,7 @@ struct Memory::Impl {
} }
switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
case Common::PageType::Unmapped: case Common::PageType::Unmapped:
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X} at PC 0x{:08X}", sizeof(T) * 8, vaddr, LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
system.CurrentArmInterface().GetPC());
return 0; return 0;
case Common::PageType::Memory: case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
@ -645,9 +638,8 @@ struct Memory::Impl {
} }
switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
case Common::PageType::Unmapped: case Common::PageType::Unmapped:
LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X} at PC 0x{:08X}", LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
sizeof(data) * 8, static_cast<u32>(data), vaddr, static_cast<u32>(data), vaddr);
system.CurrentArmInterface().GetPC());
return; return;
case Common::PageType::Memory: case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);

View file

@ -185,16 +185,6 @@ public:
return {0.0f, 0.0f}; return {0.0f, 0.0f};
} }
std::tuple<float, float> GetRawStatus() const override {
const float x = GetAxis(axis_x);
const float y = GetAxis(axis_y);
return {x, y};
}
Input::AnalogProperties GetAnalogProperties() const override {
return {deadzone, range, 0.5f};
}
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
const auto [x, y] = GetStatus(); const auto [x, y] = GetStatus();
const float directional_deadzone = 0.5f; const float directional_deadzone = 0.5f;

View file

@ -373,16 +373,6 @@ public:
return {}; return {};
} }
std::tuple<float, float> GetRawStatus() const override {
const float x = joystick->GetAxis(axis_x, range);
const float y = joystick->GetAxis(axis_y, range);
return {x, -y};
}
Input::AnalogProperties GetAnalogProperties() const override {
return {deadzone, range, 0.5f};
}
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
const auto [x, y] = GetStatus(); const auto [x, y] = GetStatus();
const float directional_deadzone = 0.5f; const float directional_deadzone = 0.5f;

View file

@ -25,19 +25,18 @@ public:
} }
} }
Input::TouchStatus GetStatus() const override { std::tuple<float, float, bool> GetStatus() const override {
Input::TouchStatus touch_status{}; for (const auto& m : map) {
for (std::size_t id = 0; id < map.size() && id < touch_status.size(); ++id) { const bool state = std::get<0>(m)->GetStatus();
const bool state = std::get<0>(map[id])->GetStatus();
if (state) { if (state) {
const float x = static_cast<float>(std::get<1>(map[id])) / const float x = static_cast<float>(std::get<1>(m)) /
static_cast<int>(Layout::ScreenUndocked::Width); static_cast<int>(Layout::ScreenUndocked::Width);
const float y = static_cast<float>(std::get<2>(map[id])) / const float y = static_cast<float>(std::get<2>(m)) /
static_cast<int>(Layout::ScreenUndocked::Height); static_cast<int>(Layout::ScreenUndocked::Height);
touch_status[id] = {x, y, true}; return {x, y, true};
} }
} }
return touch_status; return {};
} }
private: private:

View file

@ -136,7 +136,6 @@ static void SocketLoop(Socket* socket) {
Client::Client() { Client::Client() {
LOG_INFO(Input, "Udp Initialization started"); LOG_INFO(Input, "Udp Initialization started");
finger_id.fill(MAX_TOUCH_FINGERS);
ReloadSockets(); ReloadSockets();
} }
@ -177,7 +176,7 @@ void Client::ReloadSockets() {
std::string server_token; std::string server_token;
std::size_t client = 0; std::size_t client = 0;
while (std::getline(servers_ss, server_token, ',')) { while (std::getline(servers_ss, server_token, ',')) {
if (client == MAX_UDP_CLIENTS) { if (client == max_udp_clients) {
break; break;
} }
std::stringstream server_ss(server_token); std::stringstream server_ss(server_token);
@ -195,7 +194,7 @@ void Client::ReloadSockets() {
for (std::size_t pad = 0; pad < 4; ++pad) { for (std::size_t pad = 0; pad < 4; ++pad) {
const std::size_t client_number = const std::size_t client_number =
GetClientNumber(udp_input_address, udp_input_port, pad); GetClientNumber(udp_input_address, udp_input_port, pad);
if (client_number != MAX_UDP_CLIENTS) { if (client_number != max_udp_clients) {
LOG_ERROR(Input, "Duplicated UDP servers found"); LOG_ERROR(Input, "Duplicated UDP servers found");
continue; continue;
} }
@ -214,7 +213,7 @@ std::size_t Client::GetClientNumber(std::string_view host, u16 port, std::size_t
return client; return client;
} }
} }
return MAX_UDP_CLIENTS; return max_udp_clients;
} }
void Client::OnVersion([[maybe_unused]] Response::Version data) { void Client::OnVersion([[maybe_unused]] Response::Version data) {
@ -260,14 +259,33 @@ void Client::OnPadData(Response::PadData data, std::size_t client) {
std::lock_guard guard(clients[client].status.update_mutex); std::lock_guard guard(clients[client].status.update_mutex);
clients[client].status.motion_status = clients[client].motion.GetMotion(); clients[client].status.motion_status = clients[client].motion.GetMotion();
for (std::size_t id = 0; id < data.touch.size(); ++id) { // TODO: add a setting for "click" touch. Click touch refers to a device that differentiates
UpdateTouchInput(data.touch[id], client, id); // between a simple "tap" and a hard press that causes the touch screen to click.
const bool is_active = data.touch_1.is_active != 0;
float x = 0;
float y = 0;
if (is_active && clients[client].status.touch_calibration) {
const u16 min_x = clients[client].status.touch_calibration->min_x;
const u16 max_x = clients[client].status.touch_calibration->max_x;
const u16 min_y = clients[client].status.touch_calibration->min_y;
const u16 max_y = clients[client].status.touch_calibration->max_y;
x = static_cast<float>(std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) -
min_x) /
static_cast<float>(max_x - min_x);
y = static_cast<float>(std::clamp(static_cast<u16>(data.touch_1.y), min_y, max_y) -
min_y) /
static_cast<float>(max_y - min_y);
} }
clients[client].status.touch_status = {x, y, is_active};
if (configuring) { if (configuring) {
const Common::Vec3f gyroscope = clients[client].motion.GetGyroscope(); const Common::Vec3f gyroscope = clients[client].motion.GetGyroscope();
const Common::Vec3f accelerometer = clients[client].motion.GetAcceleration(); const Common::Vec3f accelerometer = clients[client].motion.GetAcceleration();
UpdateYuzuSettings(client, accelerometer, gyroscope); UpdateYuzuSettings(client, accelerometer, gyroscope, is_active);
} }
} }
} }
@ -302,17 +320,21 @@ void Client::Reset() {
} }
void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
const Common::Vec3<float>& gyro) { const Common::Vec3<float>& gyro, bool touch) {
if (gyro.Length() > 0.2f) { if (gyro.Length() > 0.2f) {
LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {})", client, LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {}), touch={}",
gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2]); client, gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2], touch);
} }
UDPPadStatus pad{ UDPPadStatus pad{
.host = clients[client].host, .host = clients[client].host,
.port = clients[client].port, .port = clients[client].port,
.pad_index = clients[client].pad_index, .pad_index = clients[client].pad_index,
}; };
for (std::size_t i = 0; i < 3; ++i) { if (touch) {
pad.touch = PadTouch::Click;
pad_queue.Push(pad);
}
for (size_t i = 0; i < 3; ++i) {
if (gyro[i] > 5.0f || gyro[i] < -5.0f) { if (gyro[i] > 5.0f || gyro[i] < -5.0f) {
pad.motion = static_cast<PadMotion>(i); pad.motion = static_cast<PadMotion>(i);
pad.motion_value = gyro[i]; pad.motion_value = gyro[i];
@ -326,50 +348,6 @@ void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& a
} }
} }
std::optional<std::size_t> Client::GetUnusedFingerID() const {
std::size_t first_free_id = 0;
while (first_free_id < MAX_TOUCH_FINGERS) {
if (!std::get<2>(touch_status[first_free_id])) {
return first_free_id;
} else {
first_free_id++;
}
}
return std::nullopt;
}
void Client::UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id) {
// TODO: Use custom calibration per device
const Common::ParamPackage touch_param(Settings::values.touch_device);
const u16 min_x = static_cast<u16>(touch_param.Get("min_x", 100));
const u16 min_y = static_cast<u16>(touch_param.Get("min_y", 50));
const u16 max_x = static_cast<u16>(touch_param.Get("max_x", 1800));
const u16 max_y = static_cast<u16>(touch_param.Get("max_y", 850));
const std::size_t touch_id = client * 2 + id;
if (touch_pad.is_active) {
if (finger_id[touch_id] == MAX_TOUCH_FINGERS) {
const auto first_free_id = GetUnusedFingerID();
if (!first_free_id) {
// Invalid finger id skip to next input
return;
}
finger_id[touch_id] = *first_free_id;
}
auto& [x, y, pressed] = touch_status[finger_id[touch_id]];
x = static_cast<float>(std::clamp(static_cast<u16>(touch_pad.x), min_x, max_x) - min_x) /
static_cast<float>(max_x - min_x);
y = static_cast<float>(std::clamp(static_cast<u16>(touch_pad.y), min_y, max_y) - min_y) /
static_cast<float>(max_y - min_y);
pressed = true;
return;
}
if (finger_id[touch_id] != MAX_TOUCH_FINGERS) {
touch_status[finger_id[touch_id]] = {};
finger_id[touch_id] = MAX_TOUCH_FINGERS;
}
}
void Client::BeginConfiguration() { void Client::BeginConfiguration() {
pad_queue.Clear(); pad_queue.Clear();
configuring = true; configuring = true;
@ -382,7 +360,7 @@ void Client::EndConfiguration() {
DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) { DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) {
const std::size_t client_number = GetClientNumber(host, port, pad); const std::size_t client_number = GetClientNumber(host, port, pad);
if (client_number == MAX_UDP_CLIENTS) { if (client_number == max_udp_clients) {
return clients[0].status; return clients[0].status;
} }
return clients[client_number].status; return clients[client_number].status;
@ -390,20 +368,12 @@ DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t
const DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) const { const DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) const {
const std::size_t client_number = GetClientNumber(host, port, pad); const std::size_t client_number = GetClientNumber(host, port, pad);
if (client_number == MAX_UDP_CLIENTS) { if (client_number == max_udp_clients) {
return clients[0].status; return clients[0].status;
} }
return clients[client_number].status; return clients[client_number].status;
} }
Input::TouchStatus& Client::GetTouchState() {
return touch_status;
}
const Input::TouchStatus& Client::GetTouchState() const {
return touch_status;
}
Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() { Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() {
return pad_queue; return pad_queue;
} }
@ -456,24 +426,24 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
current_status = Status::Ready; current_status = Status::Ready;
status_callback(current_status); status_callback(current_status);
} }
if (data.touch[0].is_active == 0) { if (data.touch_1.is_active == 0) {
return; return;
} }
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x, LOG_DEBUG(Input, "Current touch: {} {}", data.touch_1.x,
data.touch[0].y); data.touch_1.y);
min_x = std::min(min_x, static_cast<u16>(data.touch[0].x)); min_x = std::min(min_x, static_cast<u16>(data.touch_1.x));
min_y = std::min(min_y, static_cast<u16>(data.touch[0].y)); min_y = std::min(min_y, static_cast<u16>(data.touch_1.y));
if (current_status == Status::Ready) { if (current_status == Status::Ready) {
// First touch - min data (min_x/min_y) // First touch - min data (min_x/min_y)
current_status = Status::Stage1Completed; current_status = Status::Stage1Completed;
status_callback(current_status); status_callback(current_status);
} }
if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD && if (data.touch_1.x - min_x > CALIBRATION_THRESHOLD &&
data.touch[0].y - min_y > CALIBRATION_THRESHOLD) { data.touch_1.y - min_y > CALIBRATION_THRESHOLD) {
// Set the current position as max value and finishes // Set the current position as max value and finishes
// configuration // configuration
max_x = data.touch[0].x; max_x = data.touch_1.x;
max_y = data.touch[0].y; max_y = data.touch_1.y;
current_status = Status::Completed; current_status = Status::Completed;
data_callback(min_x, min_y, max_x, max_y); data_callback(min_x, min_y, max_x, max_y);
status_callback(current_status); status_callback(current_status);

View file

@ -28,7 +28,6 @@ class Socket;
namespace Response { namespace Response {
struct PadData; struct PadData;
struct PortInfo; struct PortInfo;
struct TouchPad;
struct Version; struct Version;
} // namespace Response } // namespace Response
@ -51,6 +50,7 @@ struct UDPPadStatus {
std::string host{"127.0.0.1"}; std::string host{"127.0.0.1"};
u16 port{26760}; u16 port{26760};
std::size_t pad_index{}; std::size_t pad_index{};
PadTouch touch{PadTouch::Undefined};
PadMotion motion{PadMotion::Undefined}; PadMotion motion{PadMotion::Undefined};
f32 motion_value{0.0f}; f32 motion_value{0.0f};
}; };
@ -93,9 +93,6 @@ public:
DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad); DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad);
const DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad) const; const DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad) const;
Input::TouchStatus& GetTouchState();
const Input::TouchStatus& GetTouchState() const;
private: private:
struct ClientData { struct ClientData {
std::string host{"127.0.0.1"}; std::string host{"127.0.0.1"};
@ -125,25 +122,14 @@ private:
void StartCommunication(std::size_t client, const std::string& host, u16 port, void StartCommunication(std::size_t client, const std::string& host, u16 port,
std::size_t pad_index, u32 client_id); std::size_t pad_index, u32 client_id);
void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
const Common::Vec3<float>& gyro); const Common::Vec3<float>& gyro, bool touch);
// Returns an unused finger id, if there is no fingers available std::nullopt will be
// returned
std::optional<std::size_t> GetUnusedFingerID() const;
// Merges and updates all touch inputs into the touch_status array
void UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id);
bool configuring = false; bool configuring = false;
// Allocate clients for 8 udp servers // Allocate clients for 8 udp servers
static constexpr std::size_t MAX_UDP_CLIENTS = 4 * 8; const std::size_t max_udp_clients = 32;
// Each client can have up 2 touch inputs std::array<ClientData, 4 * 8> clients;
static constexpr std::size_t MAX_TOUCH_FINGERS = MAX_UDP_CLIENTS * 2; Common::SPSCQueue<UDPPadStatus> pad_queue;
std::array<ClientData, MAX_UDP_CLIENTS> clients{};
Common::SPSCQueue<UDPPadStatus> pad_queue{};
Input::TouchStatus touch_status{};
std::array<std::size_t, MAX_TOUCH_FINGERS> finger_id{};
}; };
/// An async job allowing configuration of the touchpad calibration. /// An async job allowing configuration of the touchpad calibration.

View file

@ -140,14 +140,6 @@ static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong si
static_assert(std::is_trivially_copyable_v<PortInfo>, static_assert(std::is_trivially_copyable_v<PortInfo>,
"UDP Response PortInfo is not trivially copyable"); "UDP Response PortInfo is not trivially copyable");
struct TouchPad {
u8 is_active{};
u8 id{};
u16_le x{};
u16_le y{};
};
static_assert(sizeof(TouchPad) == 6, "UDP Response TouchPad struct has wrong size ");
#pragma pack(push, 1) #pragma pack(push, 1)
struct PadData { struct PadData {
PortInfo info{}; PortInfo info{};
@ -198,7 +190,12 @@ struct PadData {
u8 button_13{}; u8 button_13{};
} analog_button; } analog_button;
std::array<TouchPad, 2> touch; struct TouchPad {
u8 is_active{};
u8 id{};
u16_le x{};
u16_le y{};
} touch_1, touch_2;
u64_le motion_timestamp; u64_le motion_timestamp;
@ -225,6 +222,7 @@ static_assert(sizeof(Message<PadData>) == MAX_PACKET_SIZE,
static_assert(sizeof(PadData::AnalogButton) == 12, static_assert(sizeof(PadData::AnalogButton) == 12,
"UDP Response AnalogButton struct has wrong size "); "UDP Response AnalogButton struct has wrong size ");
static_assert(sizeof(PadData::TouchPad) == 6, "UDP Response TouchPad struct has wrong size ");
static_assert(sizeof(PadData::Accelerometer) == 12, static_assert(sizeof(PadData::Accelerometer) == 12,
"UDP Response Accelerometer struct has wrong size "); "UDP Response Accelerometer struct has wrong size ");
static_assert(sizeof(PadData::Gyroscope) == 12, "UDP Response Gyroscope struct has wrong size "); static_assert(sizeof(PadData::Gyroscope) == 12, "UDP Response Gyroscope struct has wrong size ");

View file

@ -78,8 +78,8 @@ public:
explicit UDPTouch(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_) explicit UDPTouch(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_)
: ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {} : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
Input::TouchStatus GetStatus() const override { std::tuple<float, float, bool> GetStatus() const override {
return client->GetTouchState(); return client->GetPadState(ip, port, pad).touch_status;
} }
private: private:
@ -107,4 +107,32 @@ std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamP
return std::make_unique<UDPTouch>(std::move(ip), port, pad, client.get()); return std::make_unique<UDPTouch>(std::move(ip), port, pad, client.get());
} }
void UDPTouchFactory::BeginConfiguration() {
polling = true;
client->BeginConfiguration();
}
void UDPTouchFactory::EndConfiguration() {
polling = false;
client->EndConfiguration();
}
Common::ParamPackage UDPTouchFactory::GetNextInput() {
Common::ParamPackage params;
CemuhookUDP::UDPPadStatus pad;
auto& queue = client->GetPadQueue();
while (queue.Pop(pad)) {
if (pad.touch == CemuhookUDP::PadTouch::Undefined) {
continue;
}
params.Set("engine", "cemuhookudp");
params.Set("ip", pad.host);
params.Set("port", static_cast<u16>(pad.port));
params.Set("pad_index", static_cast<u16>(pad.pad_index));
params.Set("touch", static_cast<u16>(pad.touch));
return params;
}
return params;
}
} // namespace InputCommon } // namespace InputCommon

View file

@ -101,25 +101,6 @@ void MemoryManager::TryUnlockPage(PageEntry page_entry, std::size_t size) {
.IsSuccess()); .IsSuccess());
} }
void MemoryManager::UnmapVicFrame(GPUVAddr gpu_addr, std::size_t size) {
if (!size) {
return;
}
const std::optional<VAddr> cpu_addr = GpuToCpuAddress(gpu_addr);
ASSERT(cpu_addr);
system.GPU().Renderer().Rasterizer().InvalidateExceptTextureCache(*cpu_addr, size);
cache_invalidate_queue.push_back({*cpu_addr, size});
UpdateRange(gpu_addr, PageEntry::State::Unmapped, size);
}
void MemoryManager::InvalidateQueuedCaches() {
for (const auto& entry : cache_invalidate_queue) {
rasterizer->InvalidateTextureCache(entry.first, entry.second);
}
cache_invalidate_queue.clear();
}
PageEntry MemoryManager::GetPageEntry(GPUVAddr gpu_addr) const { PageEntry MemoryManager::GetPageEntry(GPUVAddr gpu_addr) const {
return page_table[PageEntryIndex(gpu_addr)]; return page_table[PageEntryIndex(gpu_addr)];
} }

View file

@ -121,14 +121,6 @@ public:
[[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align); [[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align);
void Unmap(GPUVAddr gpu_addr, std::size_t size); void Unmap(GPUVAddr gpu_addr, std::size_t size);
/**
* Some Decoded NVDEC frames require that texture cache does not get invalidated.
* UnmapVicFrame defers the texture cache invalidation until the stream ends
* by invoking InvalidateQueuedCaches to invalidate all frame texture caches.
*/
void UnmapVicFrame(GPUVAddr gpu_addr, std::size_t size);
void InvalidateQueuedCaches();
private: private:
[[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const; [[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const;
void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size); void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size);
@ -158,7 +150,6 @@ private:
VideoCore::RasterizerInterface* rasterizer = nullptr; VideoCore::RasterizerInterface* rasterizer = nullptr;
std::vector<PageEntry> page_table; std::vector<PageEntry> page_table;
std::vector<std::pair<VAddr, std::size_t>> cache_invalidate_queue;
}; };
} // namespace Tegra } // namespace Tegra

View file

@ -64,12 +64,6 @@ public:
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
virtual void FlushRegion(VAddr addr, u64 size) = 0; virtual void FlushRegion(VAddr addr, u64 size) = 0;
/// Notify rasterizer to flush the texture cache to Switch memory
virtual void InvalidateExceptTextureCache(VAddr addr, u64 size) = 0;
/// Notify rasterizer to invalidate the texture cache
virtual void InvalidateTextureCache(VAddr addr, u64 size) = 0;
/// Check if the the specified memory area requires flushing to CPU Memory. /// Check if the the specified memory area requires flushing to CPU Memory.
virtual bool MustFlushRegion(VAddr addr, u64 size) = 0; virtual bool MustFlushRegion(VAddr addr, u64 size) = 0;

View file

@ -721,23 +721,6 @@ void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
query_cache.FlushRegion(addr, size); query_cache.FlushRegion(addr, size);
} }
void RasterizerOpenGL::InvalidateExceptTextureCache(VAddr addr, u64 size) {
if (addr == 0 || size == 0) {
return;
}
shader_cache.InvalidateRegion(addr, size);
buffer_cache.InvalidateRegion(addr, size);
query_cache.InvalidateRegion(addr, size);
}
void RasterizerOpenGL::InvalidateTextureCache(VAddr addr, u64 size) {
if (addr == 0 || size == 0) {
return;
}
auto lock = texture_cache.AcquireLock();
texture_cache.UnmapMemory(addr, size);
}
bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size) { bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size) {
if (!Settings::IsGPULevelHigh()) { if (!Settings::IsGPULevelHigh()) {
return buffer_cache.MustFlushRegion(addr, size); return buffer_cache.MustFlushRegion(addr, size);

View file

@ -74,8 +74,6 @@ public:
void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override;
void FlushAll() override; void FlushAll() override;
void FlushRegion(VAddr addr, u64 size) override; void FlushRegion(VAddr addr, u64 size) override;
void InvalidateExceptTextureCache(VAddr addr, u64 size) override;
void InvalidateTextureCache(VAddr addr, u64 size) override;
bool MustFlushRegion(VAddr addr, u64 size) override; bool MustFlushRegion(VAddr addr, u64 size) override;
void InvalidateRegion(VAddr addr, u64 size) override; void InvalidateRegion(VAddr addr, u64 size) override;
void OnCPUWrite(VAddr addr, u64 size) override; void OnCPUWrite(VAddr addr, u64 size) override;

View file

@ -658,23 +658,6 @@ void RasterizerVulkan::FlushRegion(VAddr addr, u64 size) {
query_cache.FlushRegion(addr, size); query_cache.FlushRegion(addr, size);
} }
void Vulkan::RasterizerVulkan::InvalidateExceptTextureCache(VAddr addr, u64 size) {
if (addr == 0 || size == 0) {
return;
}
pipeline_cache.InvalidateRegion(addr, size);
buffer_cache.InvalidateRegion(addr, size);
query_cache.InvalidateRegion(addr, size);
}
void Vulkan::RasterizerVulkan::InvalidateTextureCache(VAddr addr, u64 size) {
if (addr == 0 || size == 0) {
return;
}
auto lock = texture_cache.AcquireLock();
texture_cache.UnmapMemory(addr, size);
}
bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size) { bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size) {
if (!Settings::IsGPULevelHigh()) { if (!Settings::IsGPULevelHigh()) {
return buffer_cache.MustFlushRegion(addr, size); return buffer_cache.MustFlushRegion(addr, size);

View file

@ -67,8 +67,6 @@ public:
void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override;
void FlushAll() override; void FlushAll() override;
void FlushRegion(VAddr addr, u64 size) override; void FlushRegion(VAddr addr, u64 size) override;
void InvalidateExceptTextureCache(VAddr addr, u64 size) override;
void InvalidateTextureCache(VAddr addr, u64 size) override;
bool MustFlushRegion(VAddr addr, u64 size) override; bool MustFlushRegion(VAddr addr, u64 size) override;
void InvalidateRegion(VAddr addr, u64 size) override; void InvalidateRegion(VAddr addr, u64 size) override;
void OnCPUWrite(VAddr addr, u64 size) override; void OnCPUWrite(VAddr addr, u64 size) override;

View file

@ -71,8 +71,6 @@ add_executable(yuzu
configuration/configure_input_player.cpp configuration/configure_input_player.cpp
configuration/configure_input_player.h configuration/configure_input_player.h
configuration/configure_input_player.ui configuration/configure_input_player.ui
configuration/configure_input_player_widget.cpp
configuration/configure_input_player_widget.h
configuration/configure_input_profile_dialog.cpp configuration/configure_input_profile_dialog.cpp
configuration/configure_input_profile_dialog.h configuration/configure_input_profile_dialog.h
configuration/configure_input_profile_dialog.ui configuration/configure_input_profile_dialog.ui

View file

@ -394,7 +394,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
input_subsystem->GetMouse()->PressButton(x, y, event->button()); input_subsystem->GetMouse()->PressButton(x, y, event->button());
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
this->TouchPressed(x, y, 0); this->TouchPressed(x, y);
} }
emit MouseActivity(); emit MouseActivity();
@ -409,7 +409,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
auto pos = event->pos(); auto pos = event->pos();
const auto [x, y] = ScaleTouch(pos); const auto [x, y] = ScaleTouch(pos);
input_subsystem->GetMouse()->MouseMove(x, y); input_subsystem->GetMouse()->MouseMove(x, y);
this->TouchMoved(x, y, 0); this->TouchMoved(x, y);
emit MouseActivity(); emit MouseActivity();
} }
@ -423,72 +423,36 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
input_subsystem->GetMouse()->ReleaseButton(event->button()); input_subsystem->GetMouse()->ReleaseButton(event->button());
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
this->TouchReleased(0); this->TouchReleased();
} }
} }
void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints(); // TouchBegin always has exactly one touch point, so take the .first()
for (const auto& touch_point : touch_points) { const auto [x, y] = ScaleTouch(event->touchPoints().first().pos());
if (!TouchUpdate(touch_point)) { this->TouchPressed(x, y);
TouchStart(touch_point);
}
}
} }
void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) { void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints(); QPointF pos;
for (const auto& touch_point : touch_points) { int active_points = 0;
if (!TouchUpdate(touch_point)) {
TouchStart(touch_point); // average all active touch points
} for (const auto& tp : event->touchPoints()) {
} if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) {
// Release all inactive points active_points++;
for (std::size_t id = 0; id < touch_ids.size(); ++id) { pos += tp.pos();
if (!TouchExist(touch_ids[id], touch_points)) {
touch_ids[id] = 0;
this->TouchReleased(id + 1);
} }
} }
pos /= active_points;
const auto [x, y] = ScaleTouch(pos);
this->TouchMoved(x, y);
} }
void GRenderWindow::TouchEndEvent() { void GRenderWindow::TouchEndEvent() {
for (std::size_t id = 0; id < touch_ids.size(); ++id) { this->TouchReleased();
if (touch_ids[id] != 0) {
touch_ids[id] = 0;
this->TouchReleased(id + 1);
}
}
}
bool 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());
this->TouchPressed(x, y, id + 1);
return true;
}
}
return false;
}
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());
this->TouchMoved(x, y, id + 1);
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);
});
} }
bool GRenderWindow::event(QEvent* event) { bool GRenderWindow::event(QEvent* event) {

View file

@ -11,7 +11,6 @@
#include <QImage> #include <QImage>
#include <QThread> #include <QThread>
#include <QTouchEvent>
#include <QWidget> #include <QWidget>
#include <QWindow> #include <QWindow>
@ -22,6 +21,7 @@
class GRenderWindow; class GRenderWindow;
class GMainWindow; class GMainWindow;
class QKeyEvent; class QKeyEvent;
class QTouchEvent;
class QStringList; class QStringList;
namespace InputCommon { namespace InputCommon {
@ -191,10 +191,6 @@ private:
void TouchUpdateEvent(const QTouchEvent* event); void TouchUpdateEvent(const QTouchEvent* event);
void TouchEndEvent(); void TouchEndEvent();
bool 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; void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
bool InitializeOpenGL(); bool InitializeOpenGL();
@ -219,8 +215,6 @@ private:
bool first_frame = false; bool first_frame = false;
std::array<std::size_t, 16> touch_ids{};
protected: protected:
void showEvent(QShowEvent* event) override; void showEvent(QShowEvent* event) override;
bool eventFilter(QObject* object, QEvent* event) override; bool eventFilter(QObject* object, QEvent* event) override;

View file

@ -464,7 +464,13 @@ void Config::ReadMouseValues() {
void Config::ReadTouchscreenValues() { void Config::ReadTouchscreenValues() {
Settings::values.touchscreen.enabled = Settings::values.touchscreen.enabled =
ReadSetting(QStringLiteral("touchscreen_enabled"), true).toBool(); ReadSetting(QStringLiteral("touchscreen_enabled"), true).toBool();
Settings::values.touchscreen.device =
ReadSetting(QStringLiteral("touchscreen_device"), QStringLiteral("engine:emu_window"))
.toString()
.toStdString();
Settings::values.touchscreen.finger =
ReadSetting(QStringLiteral("touchscreen_finger"), 0).toUInt();
Settings::values.touchscreen.rotation_angle = Settings::values.touchscreen.rotation_angle =
ReadSetting(QStringLiteral("touchscreen_angle"), 0).toUInt(); ReadSetting(QStringLiteral("touchscreen_angle"), 0).toUInt();
Settings::values.touchscreen.diameter_x = Settings::values.touchscreen.diameter_x =
@ -557,8 +563,7 @@ void Config::ReadMotionTouchValues() {
.toString() .toString()
.toStdString(); .toStdString();
Settings::values.touch_device = Settings::values.touch_device =
ReadSetting(QStringLiteral("touch_device"), ReadSetting(QStringLiteral("touch_device"), QStringLiteral("engine:emu_window"))
QStringLiteral("min_x:100,min_y:50,max_x:1800,max_y:850"))
.toString() .toString()
.toStdString(); .toStdString();
Settings::values.use_touch_from_button = Settings::values.use_touch_from_button =
@ -1082,7 +1087,10 @@ void Config::SaveTouchscreenValues() {
const auto& touchscreen = Settings::values.touchscreen; const auto& touchscreen = Settings::values.touchscreen;
WriteSetting(QStringLiteral("touchscreen_enabled"), touchscreen.enabled, true); WriteSetting(QStringLiteral("touchscreen_enabled"), touchscreen.enabled, true);
WriteSetting(QStringLiteral("touchscreen_device"), QString::fromStdString(touchscreen.device),
QStringLiteral("engine:emu_window"));
WriteSetting(QStringLiteral("touchscreen_finger"), touchscreen.finger, 0);
WriteSetting(QStringLiteral("touchscreen_angle"), touchscreen.rotation_angle, 0); WriteSetting(QStringLiteral("touchscreen_angle"), touchscreen.rotation_angle, 0);
WriteSetting(QStringLiteral("touchscreen_diameter_x"), touchscreen.diameter_x, 15); WriteSetting(QStringLiteral("touchscreen_diameter_x"), touchscreen.diameter_x, 15);
WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15); WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15);

View file

@ -23,7 +23,6 @@
#include "ui_configure_input_player.h" #include "ui_configure_input_player.h"
#include "yuzu/configuration/config.h" #include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_input_player.h" #include "yuzu/configuration/configure_input_player.h"
#include "yuzu/configuration/configure_input_player_widget.h"
#include "yuzu/configuration/configure_vibration.h" #include "yuzu/configuration/configure_vibration.h"
#include "yuzu/configuration/input_profiles.h" #include "yuzu/configuration/input_profiles.h"
#include "yuzu/util/limitable_input_dialog.h" #include "yuzu/util/limitable_input_dialog.h"
@ -255,12 +254,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup}; analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup};
analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange}; analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange};
const auto ConfigureButtonClick = [&](QPushButton* button, std::size_t button_id, const auto ConfigureButtonClick = [&](QPushButton* button, Common::ParamPackage* param,
Common::ParamPackage* param, int default_val, int default_val, InputCommon::Polling::DeviceType type) {
InputCommon::Polling::DeviceType type) {
connect(button, &QPushButton::clicked, [=, this] { connect(button, &QPushButton::clicked, [=, this] {
HandleClick( HandleClick(
button, button_id, button,
[=, this](Common::ParamPackage params) { [=, this](Common::ParamPackage params) {
// Workaround for ZL & ZR for analog triggers like on XBOX // Workaround for ZL & ZR for analog triggers like on XBOX
// controllers. Analog triggers (from controllers like the XBOX // controllers. Analog triggers (from controllers like the XBOX
@ -288,11 +286,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
continue; continue;
} }
ConfigureButtonClick(button_map[button_id], button_id, &buttons_param[button_id], ConfigureButtonClick(button_map[button_id], &buttons_param[button_id],
Config::default_buttons[button_id], Config::default_buttons[button_id],
InputCommon::Polling::DeviceType::Button); InputCommon::Polling::DeviceType::Button);
button->setContextMenuPolicy(Qt::CustomContextMenu); button->setContextMenuPolicy(Qt::CustomContextMenu);
connect(button, &QPushButton::customContextMenuRequested, connect(button, &QPushButton::customContextMenuRequested,
[=, this](const QPoint& menu_location) { [=, this](const QPoint& menu_location) {
QMenu context_menu; QMenu context_menu;
@ -301,7 +300,6 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
button_map[button_id]->setText(tr("[not set]")); button_map[button_id]->setText(tr("[not set]"));
}); });
context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
}); });
} }
@ -311,7 +309,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
continue; continue;
} }
ConfigureButtonClick(motion_map[motion_id], motion_id, &motions_param[motion_id], ConfigureButtonClick(motion_map[motion_id], &motions_param[motion_id],
Config::default_motions[motion_id], Config::default_motions[motion_id],
InputCommon::Polling::DeviceType::Motion); InputCommon::Polling::DeviceType::Motion);
@ -350,7 +348,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
} }
} }
HandleClick( HandleClick(
analog_map_buttons[analog_id][sub_button_id], analog_id, analog_map_buttons[analog_id][sub_button_id],
[=, this](const Common::ParamPackage& params) { [=, this](const Common::ParamPackage& params) {
SetAnalogParam(params, analogs_param[analog_id], SetAnalogParam(params, analogs_param[analog_id],
analog_sub_buttons[sub_button_id]); analog_sub_buttons[sub_button_id]);
@ -360,43 +358,41 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
analog_button->setContextMenuPolicy(Qt::CustomContextMenu); analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
connect( connect(analog_button, &QPushButton::customContextMenuRequested,
analog_button, &QPushButton::customContextMenuRequested, [=, this](const QPoint& menu_location) {
[=, this](const QPoint& menu_location) { QMenu context_menu;
QMenu context_menu; context_menu.addAction(tr("Clear"), [&] {
context_menu.addAction(tr("Clear"), [&] { analogs_param[analog_id].Clear();
analogs_param[analog_id].Clear(); analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]")); });
context_menu.addAction(tr("Invert axis"), [&] {
if (sub_button_id == 2 || sub_button_id == 3) {
const bool invert_value =
analogs_param[analog_id].Get("invert_x", "+") == "-";
const std::string invert_str = invert_value ? "+" : "-";
analogs_param[analog_id].Set("invert_x", invert_str);
}
if (sub_button_id == 0 || sub_button_id == 1) {
const bool invert_value =
analogs_param[analog_id].Get("invert_y", "+") == "-";
const std::string invert_str = invert_value ? "+" : "-";
analogs_param[analog_id].Set("invert_y", invert_str);
}
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM;
++sub_button_id) {
analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
}
});
context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
menu_location));
}); });
context_menu.addAction(tr("Invert axis"), [&] {
if (sub_button_id == 2 || sub_button_id == 3) {
const bool invert_value =
analogs_param[analog_id].Get("invert_x", "+") == "-";
const std::string invert_str = invert_value ? "+" : "-";
analogs_param[analog_id].Set("invert_x", invert_str);
}
if (sub_button_id == 0 || sub_button_id == 1) {
const bool invert_value =
analogs_param[analog_id].Get("invert_y", "+") == "-";
const std::string invert_str = invert_value ? "+" : "-";
analogs_param[analog_id].Set("invert_y", invert_str);
}
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM;
++sub_button_id) {
analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
}
});
context_menu.exec(
analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(menu_location));
ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
});
} }
// Handle clicks for the modifier buttons as well. // Handle clicks for the modifier buttons as well.
connect(analog_map_modifier_button[analog_id], &QPushButton::clicked, [=, this] { connect(analog_map_modifier_button[analog_id], &QPushButton::clicked, [=, this] {
HandleClick( HandleClick(
analog_map_modifier_button[analog_id], analog_id, analog_map_modifier_button[analog_id],
[=, this](const Common::ParamPackage& params) { [=, this](const Common::ParamPackage& params) {
analogs_param[analog_id].Set("modifier", params.Serialize()); analogs_param[analog_id].Set("modifier", params.Serialize());
}, },
@ -420,14 +416,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
[=, this] { [=, this] {
const auto spinbox_value = analog_map_range_spinbox[analog_id]->value(); const auto spinbox_value = analog_map_range_spinbox[analog_id]->value();
analogs_param[analog_id].Set("range", spinbox_value / 100.0f); analogs_param[analog_id].Set("range", spinbox_value / 100.0f);
ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
}); });
connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] { connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] {
const auto slider_value = analog_map_deadzone_slider[analog_id]->value(); const auto slider_value = analog_map_deadzone_slider[analog_id]->value();
analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value)); analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value));
analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
}); });
connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] { connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] {
@ -439,10 +433,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
} }
// Player Connected checkbox // Player Connected checkbox
connect(ui->groupConnectedController, &QGroupBox::toggled, [this](bool checked) { connect(ui->groupConnectedController, &QGroupBox::toggled,
emit Connected(checked); [this](bool checked) { emit Connected(checked); });
ui->controllerFrame->SetConnectedStatus(checked);
});
if (player_index == 0) { if (player_index == 0) {
connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
@ -561,8 +553,6 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
// TODO(wwylele): enable this when we actually emulate it // TODO(wwylele): enable this when we actually emulate it
ui->buttonHome->setEnabled(false); ui->buttonHome->setEnabled(false);
ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
ui->controllerFrame->SetConnectedStatus(ui->groupConnectedController->isChecked());
} }
ConfigureInputPlayer::~ConfigureInputPlayer() = default; ConfigureInputPlayer::~ConfigureInputPlayer() = default;
@ -859,7 +849,6 @@ void ConfigureInputPlayer::UpdateUI() {
modifier_label->setVisible(!is_controller); modifier_label->setVisible(!is_controller);
modifier_slider->setVisible(!is_controller); modifier_slider->setVisible(!is_controller);
range_groupbox->setVisible(is_controller); range_groupbox->setVisible(is_controller);
ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
} }
} }
@ -976,8 +965,8 @@ void ConfigureInputPlayer::UpdateControllerIcon() {
return QString{}; return QString{};
} }
}(); }();
ui->controllerFrame->SetControllerType(
GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())); ui->controllerFrame->setStyleSheet(stylesheet.arg(theme));
} }
void ConfigureInputPlayer::UpdateControllerAvailableButtons() { void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
@ -1114,8 +1103,7 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
} }
void ConfigureInputPlayer::HandleClick( void ConfigureInputPlayer::HandleClick(
QPushButton* button, std::size_t button_id, QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::DeviceType type) { InputCommon::Polling::DeviceType type) {
if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) { if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
button->setText(tr("Shake!")); button->setText(tr("Shake!"));
@ -1159,12 +1147,6 @@ void ConfigureInputPlayer::HandleClick(
input_subsystem->GetMouseTouch()->BeginConfiguration(); input_subsystem->GetMouseTouch()->BeginConfiguration();
} }
if (type == InputCommon::Polling::DeviceType::Button) {
ui->controllerFrame->BeginMappingButton(button_id);
} else if (type == InputCommon::Polling::DeviceType::AnalogPreferred) {
ui->controllerFrame->BeginMappingAnalog(button_id);
}
timeout_timer->start(2500); // Cancel after 2.5 seconds timeout_timer->start(2500); // Cancel after 2.5 seconds
poll_timer->start(50); // Check for new inputs every 50ms poll_timer->start(50); // Check for new inputs every 50ms
} }
@ -1195,7 +1177,6 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params,
UpdateUI(); UpdateUI();
UpdateInputDeviceCombobox(); UpdateInputDeviceCombobox();
ui->controllerFrame->EndMapping();
input_setter = std::nullopt; input_setter = std::nullopt;
} }

View file

@ -106,7 +106,7 @@ private:
void LoadConfiguration(); void LoadConfiguration();
/// Called when the button was pressed. /// Called when the button was pressed.
void HandleClick(QPushButton* button, std::size_t button_id, void HandleClick(QPushButton* button,
std::function<void(const Common::ParamPackage&)> new_input_setter, std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::DeviceType type); InputCommon::Polling::DeviceType type);

View file

@ -1964,39 +1964,39 @@
</item> </item>
</layout> </layout>
</item> </item>
<item> <item>
<widget class="PlayerControlPreview" name="controllerFrame"> <widget class="QFrame" name="controllerFrame">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="font"> <property name="font">
<font> <font>
<weight>75</weight> <weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">image: url(:/controller/pro);</string> <string notr="true">image: url(:/controller/pro);</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_4"> <layout class="QVBoxLayout" name="verticalLayout_4">
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
<property name="topMargin"> <property name="topMargin">
<number>0</number> <number>0</number>
</property> </property>
<property name="rightMargin"> <property name="rightMargin">
<number>0</number> <number>0</number>
</property> </property>
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
</layout> </layout>
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="miscButtons"> <layout class="QHBoxLayout" name="miscButtons">
<property name="spacing"> <property name="spacing">
@ -3087,14 +3087,6 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>PlayerControlPreview</class>
<extends>QFrame</extends>
<header>yuzu/configuration/configure_input_player_widget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources> <resources>
<include location="../../../dist/icons/controller/controller.qrc"/> <include location="../../../dist/icons/controller/controller.qrc"/>
</resources> </resources>

View file

@ -81,11 +81,19 @@ void CalibrationConfigurationDialog::UpdateButtonText(const QString& text) {
cancel_button->setText(text); cancel_button->setText(text);
} }
constexpr std::array<std::pair<const char*, const char*>, 2> TouchProviders = {{
{"emu_window", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Emulator Window")},
{"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")},
}};
ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent, ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,
InputCommon::InputSubsystem* input_subsystem_) InputCommon::InputSubsystem* input_subsystem_)
: QDialog(parent), input_subsystem{input_subsystem_}, : QDialog(parent), input_subsystem{input_subsystem_},
ui(std::make_unique<Ui::ConfigureMotionTouch>()) { ui(std::make_unique<Ui::ConfigureMotionTouch>()) {
ui->setupUi(this); ui->setupUi(this);
for (const auto& [provider, name] : TouchProviders) {
ui->touch_provider->addItem(tr(name), QString::fromUtf8(provider));
}
ui->udp_learn_more->setOpenExternalLinks(true); ui->udp_learn_more->setOpenExternalLinks(true);
ui->udp_learn_more->setText( ui->udp_learn_more->setText(
@ -104,7 +112,10 @@ ConfigureMotionTouch::~ConfigureMotionTouch() = default;
void ConfigureMotionTouch::SetConfiguration() { void ConfigureMotionTouch::SetConfiguration() {
const Common::ParamPackage motion_param(Settings::values.motion_device); const Common::ParamPackage motion_param(Settings::values.motion_device);
const Common::ParamPackage touch_param(Settings::values.touch_device); const Common::ParamPackage touch_param(Settings::values.touch_device);
const std::string touch_engine = touch_param.Get("engine", "emu_window");
ui->touch_provider->setCurrentIndex(
ui->touch_provider->findData(QString::fromStdString(touch_engine)));
ui->touch_from_button_checkbox->setChecked(Settings::values.use_touch_from_button); ui->touch_from_button_checkbox->setChecked(Settings::values.use_touch_from_button);
touch_from_button_maps = Settings::values.touch_from_button_maps; touch_from_button_maps = Settings::values.touch_from_button_maps;
for (const auto& touch_map : touch_from_button_maps) { for (const auto& touch_map : touch_from_button_maps) {
@ -137,21 +148,30 @@ void ConfigureMotionTouch::SetConfiguration() {
} }
void ConfigureMotionTouch::UpdateUiDisplay() { void ConfigureMotionTouch::UpdateUiDisplay() {
const QString touch_engine = ui->touch_provider->currentData().toString();
const QString cemuhook_udp = QStringLiteral("cemuhookudp"); const QString cemuhook_udp = QStringLiteral("cemuhookudp");
ui->motion_sensitivity_label->setVisible(true); ui->motion_sensitivity_label->setVisible(true);
ui->motion_sensitivity->setVisible(true); ui->motion_sensitivity->setVisible(true);
ui->touch_calibration->setVisible(true); if (touch_engine == cemuhook_udp) {
ui->touch_calibration_config->setVisible(true); ui->touch_calibration->setVisible(true);
ui->touch_calibration_label->setVisible(true); ui->touch_calibration_config->setVisible(true);
ui->touch_calibration->setText( ui->touch_calibration_label->setVisible(true);
QStringLiteral("(%1, %2) - (%3, %4)").arg(min_x).arg(min_y).arg(max_x).arg(max_y)); ui->touch_calibration->setText(
QStringLiteral("(%1, %2) - (%3, %4)").arg(min_x).arg(min_y).arg(max_x).arg(max_y));
} else {
ui->touch_calibration->setVisible(false);
ui->touch_calibration_config->setVisible(false);
ui->touch_calibration_label->setVisible(false);
}
ui->udp_config_group_box->setVisible(true); ui->udp_config_group_box->setVisible(true);
} }
void ConfigureMotionTouch::ConnectEvents() { void ConfigureMotionTouch::ConnectEvents() {
connect(ui->touch_provider, qOverload<int>(&QComboBox::currentIndexChanged), this,
[this](int index) { UpdateUiDisplay(); });
connect(ui->udp_test, &QPushButton::clicked, this, &ConfigureMotionTouch::OnCemuhookUDPTest); connect(ui->udp_test, &QPushButton::clicked, this, &ConfigureMotionTouch::OnCemuhookUDPTest);
connect(ui->udp_add, &QPushButton::clicked, this, &ConfigureMotionTouch::OnUDPAddServer); connect(ui->udp_add, &QPushButton::clicked, this, &ConfigureMotionTouch::OnUDPAddServer);
connect(ui->udp_remove, &QPushButton::clicked, this, &ConfigureMotionTouch::OnUDPDeleteServer); connect(ui->udp_remove, &QPushButton::clicked, this, &ConfigureMotionTouch::OnUDPDeleteServer);
@ -307,11 +327,16 @@ void ConfigureMotionTouch::ApplyConfiguration() {
return; return;
} }
std::string touch_engine = ui->touch_provider->currentData().toString().toStdString();
Common::ParamPackage touch_param{}; Common::ParamPackage touch_param{};
touch_param.Set("min_x", min_x); if (touch_engine == "cemuhookudp") {
touch_param.Set("min_y", min_y); touch_param.Set("min_x", min_x);
touch_param.Set("max_x", max_x); touch_param.Set("min_y", min_y);
touch_param.Set("max_y", max_y); touch_param.Set("max_x", max_x);
touch_param.Set("max_y", max_y);
}
touch_param.Set("engine", std::move(touch_engine));
Settings::values.touch_device = touch_param.Serialize(); Settings::values.touch_device = touch_param.Serialize();
Settings::values.use_touch_from_button = ui->touch_from_button_checkbox->isChecked(); Settings::values.use_touch_from_button = ui->touch_from_button_checkbox->isChecked();

View file

@ -65,12 +65,26 @@
<string>Touch</string> <string>Touch</string>
</property> </property>
<layout class="QVBoxLayout"> <layout class="QVBoxLayout">
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="touch_provider_label">
<property name="text">
<string>Touch Provider:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="touch_provider"/>
</item>
</layout>
</item>
<item> <item>
<layout class="QHBoxLayout"> <layout class="QHBoxLayout">
<item> <item>
<widget class="QLabel" name="touch_calibration_label"> <widget class="QLabel" name="touch_calibration_label">
<property name="text"> <property name="text">
<string>UDP Calibration:</string> <string>Calibration:</string>
</property> </property>
</widget> </widget>
</item> </item>

View file

@ -33,18 +33,21 @@ void ConfigureTouchscreenAdvanced::RetranslateUI() {
} }
void ConfigureTouchscreenAdvanced::ApplyConfiguration() { void ConfigureTouchscreenAdvanced::ApplyConfiguration() {
Settings::values.touchscreen.finger = ui->finger_box->value();
Settings::values.touchscreen.diameter_x = ui->diameter_x_box->value(); Settings::values.touchscreen.diameter_x = ui->diameter_x_box->value();
Settings::values.touchscreen.diameter_y = ui->diameter_y_box->value(); Settings::values.touchscreen.diameter_y = ui->diameter_y_box->value();
Settings::values.touchscreen.rotation_angle = ui->angle_box->value(); Settings::values.touchscreen.rotation_angle = ui->angle_box->value();
} }
void ConfigureTouchscreenAdvanced::LoadConfiguration() { void ConfigureTouchscreenAdvanced::LoadConfiguration() {
ui->finger_box->setValue(Settings::values.touchscreen.finger);
ui->diameter_x_box->setValue(Settings::values.touchscreen.diameter_x); ui->diameter_x_box->setValue(Settings::values.touchscreen.diameter_x);
ui->diameter_y_box->setValue(Settings::values.touchscreen.diameter_y); ui->diameter_y_box->setValue(Settings::values.touchscreen.diameter_y);
ui->angle_box->setValue(Settings::values.touchscreen.rotation_angle); ui->angle_box->setValue(Settings::values.touchscreen.rotation_angle);
} }
void ConfigureTouchscreenAdvanced::RestoreDefaults() { void ConfigureTouchscreenAdvanced::RestoreDefaults() {
ui->finger_box->setValue(0);
ui->diameter_x_box->setValue(15); ui->diameter_x_box->setValue(15);
ui->diameter_y_box->setValue(15); ui->diameter_y_box->setValue(15);
ui->angle_box->setValue(0); ui->angle_box->setValue(0);

View file

@ -65,13 +65,20 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="1" column="1"> <item row="2" column="1">
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
<property name="text"> <property name="text">
<string>Touch Diameter Y</string> <string>Touch Diameter Y</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>Finger</string>
</property>
</widget>
</item>
<item row="0" column="3"> <item row="0" column="3">
<spacer name="horizontalSpacer_2"> <spacer name="horizontalSpacer_2">
<property name="orientation"> <property name="orientation">
@ -85,27 +92,37 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="0" column="1"> <item row="1" column="1">
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="label_3">
<property name="text"> <property name="text">
<string>Touch Diameter X</string> <string>Touch Diameter X</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="0" column="2">
<widget class="QSpinBox" name="finger_box">
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="label_5">
<property name="text"> <property name="text">
<string>Rotational Angle</string> <string>Rotational Angle</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="2"> <item row="1" column="2">
<widget class="QSpinBox" name="diameter_x_box"/> <widget class="QSpinBox" name="diameter_x_box"/>
</item> </item>
<item row="1" column="2"> <item row="2" column="2">
<widget class="QSpinBox" name="diameter_y_box"/> <widget class="QSpinBox" name="diameter_y_box"/>
</item> </item>
<item row="2" column="2"> <item row="3" column="2">
<widget class="QSpinBox" name="angle_box"/> <widget class="QSpinBox" name="angle_box"/>
</item> </item>
</layout> </layout>

View file

@ -38,7 +38,7 @@ void DiscordImpl::Update() {
if (Core::System::GetInstance().IsPoweredOn()) if (Core::System::GetInstance().IsPoweredOn())
Core::System::GetInstance().GetAppLoader().ReadTitle(title); Core::System::GetInstance().GetAppLoader().ReadTitle(title);
DiscordRichPresence presence{}; DiscordRichPresence presence{};
presence.largeImageKey = "yuzu_logo_ea"; presence.largeImageKey = "yuzu_logo";
presence.largeImageText = "yuzu is an emulator for the Nintendo Switch"; presence.largeImageText = "yuzu is an emulator for the Nintendo Switch";
if (Core::System::GetInstance().IsPoweredOn()) { if (Core::System::GetInstance().IsPoweredOn()) {
presence.state = title.c_str(); presence.state = title.c_str();

View file

@ -296,6 +296,10 @@ void Config::ReadValues() {
sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true)); sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true));
Settings::values.touchscreen.enabled = Settings::values.touchscreen.enabled =
sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
Settings::values.touchscreen.device =
sdl2_config->Get("ControlsGeneral", "touch_device", "engine:emu_window");
Settings::values.touchscreen.finger =
sdl2_config->GetInteger("ControlsGeneral", "touch_finger", 0);
Settings::values.touchscreen.rotation_angle = Settings::values.touchscreen.rotation_angle =
sdl2_config->GetInteger("ControlsGeneral", "touch_angle", 0); sdl2_config->GetInteger("ControlsGeneral", "touch_angle", 0);
Settings::values.touchscreen.diameter_x = Settings::values.touchscreen.diameter_x =

View file

@ -29,16 +29,16 @@ EmuWindow_SDL2::~EmuWindow_SDL2() {
} }
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0); TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
input_subsystem->GetMouse()->MouseMove(x, y); input_subsystem->GetMouse()->MouseMove(x, y);
} }
void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
if (button == SDL_BUTTON_LEFT) { if (button == SDL_BUTTON_LEFT) {
if (state == SDL_PRESSED) { if (state == SDL_PRESSED) {
TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0); TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
} else { } else {
TouchReleased(0); TouchReleased();
} }
} else if (button == SDL_BUTTON_RIGHT) { } else if (button == SDL_BUTTON_RIGHT) {
if (state == SDL_PRESSED) { if (state == SDL_PRESSED) {
@ -66,16 +66,16 @@ void EmuWindow_SDL2::OnFingerDown(float x, float y) {
// 3DS does // 3DS does
const auto [px, py] = TouchToPixelPos(x, y); const auto [px, py] = TouchToPixelPos(x, y);
TouchPressed(px, py, 0); TouchPressed(px, py);
} }
void EmuWindow_SDL2::OnFingerMotion(float x, float y) { void EmuWindow_SDL2::OnFingerMotion(float x, float y) {
const auto [px, py] = TouchToPixelPos(x, y); const auto [px, py] = TouchToPixelPos(x, y);
TouchMoved(px, py, 0); TouchMoved(px, py);
} }
void EmuWindow_SDL2::OnFingerUp() { void EmuWindow_SDL2::OnFingerUp() {
TouchReleased(0); TouchReleased();
} }
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {