diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/audio_core/audio_renderer.cpp | 89 | ||||
-rw-r--r-- | src/audio_core/audio_renderer.h | 49 | ||||
-rw-r--r-- | src/core/file_sys/ips_layer.cpp | 69 | ||||
-rw-r--r-- | src/core/file_sys/ips_layer.h | 11 | ||||
-rw-r--r-- | src/core/hle/kernel/svc.cpp | 25 | ||||
-rw-r--r-- | src/core/telemetry_session.cpp | 12 | ||||
-rw-r--r-- | src/core/telemetry_session.h | 4 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 10 | ||||
-rw-r--r-- | src/yuzu/bootmanager.cpp | 71 | ||||
-rw-r--r-- | src/yuzu/bootmanager.h | 10 | ||||
-rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | 48 | ||||
-rw-r--r-- | src/yuzu_cmd/emu_window/emu_window_sdl2.h | 12 |
12 files changed, 346 insertions, 64 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 6f0ff953a..23e5d3f10 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp @@ -30,7 +30,7 @@ public: return info; } - VoiceInfo& Info() { + VoiceInfo& GetInfo() { return info; } @@ -51,9 +51,30 @@ private: VoiceInfo info{}; }; +class AudioRenderer::EffectState { +public: + const EffectOutStatus& GetOutStatus() const { + return out_status; + } + + const EffectInStatus& GetInfo() const { + return info; + } + + EffectInStatus& GetInfo() { + return info; + } + + void UpdateState(); + +private: + EffectOutStatus out_status{}; + EffectInStatus info{}; +}; AudioRenderer::AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event) - : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) { + : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), + effects(params.effect_count) { audio_out = std::make_unique<AudioCore::AudioOut>(); stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer", @@ -96,11 +117,29 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ memory_pool_count * sizeof(MemoryPoolInfo)); // Copy VoiceInfo structs - std::size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size + - config.voice_resource_size}; + std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size + + config.memory_pools_size + config.voice_resource_size}; for (auto& voice : voices) { - std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo)); - offset += sizeof(VoiceInfo); + std::memcpy(&voice.GetInfo(), input_params.data() + voice_offset, sizeof(VoiceInfo)); + voice_offset += sizeof(VoiceInfo); + } + + std::size_t effect_offset{sizeof(UpdateDataHeader) + config.behavior_size + + config.memory_pools_size + config.voice_resource_size + + config.voices_size}; + for (auto& effect : effects) { + std::memcpy(&effect.GetInfo(), input_params.data() + effect_offset, sizeof(EffectInStatus)); + effect_offset += sizeof(EffectInStatus); + } + + // Update memory pool state + std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); + for (std::size_t index = 0; index < memory_pool.size(); ++index) { + if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) { + memory_pool[index].state = MemoryPoolStates::Attached; + } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) { + memory_pool[index].state = MemoryPoolStates::Detached; + } } // Update voices @@ -114,14 +153,8 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ } } - // Update memory pool state - std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); - for (std::size_t index = 0; index < memory_pool.size(); ++index) { - if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) { - memory_pool[index].state = MemoryPoolStates::Attached; - } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) { - memory_pool[index].state = MemoryPoolStates::Detached; - } + for (auto& effect : effects) { + effect.UpdateState(); } // Release previous buffers and queue next ones for playback @@ -144,6 +177,14 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ voice_out_status_offset += sizeof(VoiceOutStatus); } + std::size_t effect_out_status_offset{ + sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size + + response_data.voice_resource_size}; + for (const auto& effect : effects) { + std::memcpy(output_params.data() + effect_out_status_offset, &effect.GetOutStatus(), + sizeof(EffectOutStatus)); + effect_out_status_offset += sizeof(EffectOutStatus); + } return output_params; } @@ -244,11 +285,29 @@ void AudioRenderer::VoiceState::RefreshBuffer() { break; } - samples = Interpolate(interp_state, std::move(samples), Info().sample_rate, STREAM_SAMPLE_RATE); + samples = + Interpolate(interp_state, std::move(samples), GetInfo().sample_rate, STREAM_SAMPLE_RATE); is_refresh_pending = false; } +void AudioRenderer::EffectState::UpdateState() { + if (info.is_new) { + out_status.state = EffectStatus::New; + } else { + if (info.type == Effect::Aux) { + ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_info) == 0, + "Aux buffers tried to update"); + ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_info) == 0, + "Aux buffers tried to update"); + ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_base) == 0, + "Aux buffers tried to update"); + ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_base) == 0, + "Aux buffers tried to update"); + } + } +} + static constexpr s16 ClampToS16(s32 value) { return static_cast<s16>(std::clamp(value, -32768, 32767)); } diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index dfef89e1d..046417da3 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h @@ -28,6 +28,16 @@ enum class PlayState : u8 { Paused = 2, }; +enum class Effect : u8 { + None = 0, + Aux = 2, +}; + +enum class EffectStatus : u8 { + None = 0, + New = 1, +}; + struct AudioRendererParameter { u32_le sample_rate; u32_le sample_count; @@ -128,6 +138,43 @@ struct VoiceOutStatus { }; static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size"); +struct AuxInfo { + std::array<u8, 24> input_mix_buffers; + std::array<u8, 24> output_mix_buffers; + u32_le mix_buffer_count; + u32_le sample_rate; // Stored in the aux buffer currently + u32_le sampe_count; + u64_le send_buffer_info; + u64_le send_buffer_base; + + u64_le return_buffer_info; + u64_le return_buffer_base; +}; +static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size"); + +struct EffectInStatus { + Effect type; + u8 is_new; + u8 is_enabled; + INSERT_PADDING_BYTES(1); + u32_le mix_id; + u64_le buffer_base; + u64_le buffer_sz; + s32_le priority; + INSERT_PADDING_BYTES(4); + union { + std::array<u8, 0xa0> raw; + AuxInfo aux_info; + }; +}; +static_assert(sizeof(EffectInStatus) == 0xc0, "EffectInStatus is an invalid size"); + +struct EffectOutStatus { + EffectStatus state; + INSERT_PADDING_BYTES(0xf); +}; +static_assert(sizeof(EffectOutStatus) == 0x10, "EffectOutStatus is an invalid size"); + struct UpdateDataHeader { UpdateDataHeader() {} @@ -173,11 +220,13 @@ public: Stream::State GetStreamState() const; private: + class EffectState; class VoiceState; AudioRendererParameter worker_params; Kernel::SharedPtr<Kernel::Event> buffer_event; std::vector<VoiceState> voices; + std::vector<EffectState> effects; std::unique_ptr<AudioOut> audio_out; AudioCore::StreamPtr stream; }; diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index 0cadbc375..6c072d0a3 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp @@ -2,9 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> +#include <cstring> +#include <map> #include <sstream> -#include "common/assert.h" +#include <string> +#include <utility> + #include "common/hex_util.h" +#include "common/logging/log.h" #include "common/swap.h" #include "core/file_sys/ips_layer.h" #include "core/file_sys/vfs_vector.h" @@ -17,22 +23,48 @@ enum class IPSFileType { Error, }; -constexpr std::array<std::pair<const char*, const char*>, 11> ESCAPE_CHARACTER_MAP{ - std::pair{"\\a", "\a"}, {"\\b", "\b"}, {"\\f", "\f"}, {"\\n", "\n"}, - {"\\r", "\r"}, {"\\t", "\t"}, {"\\v", "\v"}, {"\\\\", "\\"}, - {"\\\'", "\'"}, {"\\\"", "\""}, {"\\\?", "\?"}, -}; +constexpr std::array<std::pair<const char*, const char*>, 11> ESCAPE_CHARACTER_MAP{{ + {"\\a", "\a"}, + {"\\b", "\b"}, + {"\\f", "\f"}, + {"\\n", "\n"}, + {"\\r", "\r"}, + {"\\t", "\t"}, + {"\\v", "\v"}, + {"\\\\", "\\"}, + {"\\\'", "\'"}, + {"\\\"", "\""}, + {"\\\?", "\?"}, +}}; static IPSFileType IdentifyMagic(const std::vector<u8>& magic) { - if (magic.size() != 5) + if (magic.size() != 5) { return IPSFileType::Error; - if (magic == std::vector<u8>{'P', 'A', 'T', 'C', 'H'}) + } + + constexpr std::array<u8, 5> patch_magic{{'P', 'A', 'T', 'C', 'H'}}; + if (std::equal(magic.begin(), magic.end(), patch_magic.begin())) { return IPSFileType::IPS; - if (magic == std::vector<u8>{'I', 'P', 'S', '3', '2'}) + } + + constexpr std::array<u8, 5> ips32_magic{{'I', 'P', 'S', '3', '2'}}; + if (std::equal(magic.begin(), magic.end(), ips32_magic.begin())) { return IPSFileType::IPS32; + } + return IPSFileType::Error; } +static bool IsEOF(IPSFileType type, const std::vector<u8>& data) { + constexpr std::array<u8, 3> eof{{'E', 'O', 'F'}}; + if (type == IPSFileType::IPS && std::equal(data.begin(), data.end(), eof.begin())) { + return true; + } + + constexpr std::array<u8, 4> eeof{{'E', 'E', 'O', 'F'}}; + return type == IPSFileType::IPS32 && std::equal(data.begin(), data.end(), eeof.begin()); +} + VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { if (in == nullptr || ips == nullptr) return nullptr; @@ -47,8 +79,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { u64 offset = 5; // After header while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) { offset += temp.size(); - if (type == IPSFileType::IPS32 && temp == std::vector<u8>{'E', 'E', 'O', 'F'} || - type == IPSFileType::IPS && temp == std::vector<u8>{'E', 'O', 'F'}) { + if (IsEOF(type, temp)) { break; } @@ -88,11 +119,20 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { } } - if (temp != std::vector<u8>{'E', 'E', 'O', 'F'} && temp != std::vector<u8>{'E', 'O', 'F'}) + if (!IsEOF(type, temp)) { return nullptr; - return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory()); + } + + return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(), + in->GetContainingDirectory()); } +struct IPSwitchCompiler::IPSwitchPatch { + std::string name; + bool enabled; + std::map<u32, std::vector<u8>> records; +}; + IPSwitchCompiler::IPSwitchCompiler(VirtualFile patch_text_) : patch_text(std::move(patch_text_)) { Parse(); } @@ -291,7 +331,8 @@ VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const { } } - return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory()); + return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(), + in->GetContainingDirectory()); } } // namespace FileSys diff --git a/src/core/file_sys/ips_layer.h b/src/core/file_sys/ips_layer.h index 57da00da8..450b2f71e 100644 --- a/src/core/file_sys/ips_layer.h +++ b/src/core/file_sys/ips_layer.h @@ -4,8 +4,11 @@ #pragma once +#include <array> #include <memory> +#include <vector> +#include "common/common_types.h" #include "core/file_sys/vfs.h" namespace FileSys { @@ -22,17 +25,13 @@ public: VirtualFile Apply(const VirtualFile& in) const; private: + struct IPSwitchPatch; + void ParseFlag(const std::string& flag); void Parse(); bool valid = false; - struct IPSwitchPatch { - std::string name; - bool enabled; - std::map<u32, std::vector<u8>> records; - }; - VirtualFile patch_text; std::vector<IPSwitchPatch> patches; std::array<u8, 0x20> nso_build_id{}; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 6c4af7e47..b488b508d 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -301,13 +301,28 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) { return Mutex::Release(mutex_addr); } +struct BreakReason { + union { + u64 raw; + BitField<31, 1, u64> dont_kill_application; + }; +}; + /// Break program execution static void Break(u64 reason, u64 info1, u64 info2) { - LOG_CRITICAL( - Debug_Emulated, - "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", - reason, info1, info2); - ASSERT(false); + BreakReason break_reason{reason}; + if (break_reason.dont_kill_application) { + LOG_ERROR( + Debug_Emulated, + "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", + reason, info1, info2); + } else { + LOG_CRITICAL( + Debug_Emulated, + "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", + reason, info1, info2); + ASSERT(false); + } } /// Used to output a message on a debug hardware unit - does nothing on a retail unit diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index f29fff1e7..7b04792b5 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -2,12 +2,16 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> + +#include <mbedtls/ctr_drbg.h> +#include <mbedtls/entropy.h> + #include "common/assert.h" #include "common/common_types.h" #include "common/file_util.h" +#include "common/logging/log.h" -#include <mbedtls/ctr_drbg.h> -#include <mbedtls/entropy.h> #include "core/core.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" @@ -28,11 +32,11 @@ static u64 GenerateTelemetryId() { mbedtls_entropy_context entropy; mbedtls_entropy_init(&entropy); mbedtls_ctr_drbg_context ctr_drbg; - std::string personalization = "yuzu Telemetry ID"; + constexpr std::array<char, 18> personalization{{"yuzu Telemetry ID"}}; mbedtls_ctr_drbg_init(&ctr_drbg); ASSERT(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, - reinterpret_cast<const unsigned char*>(personalization.c_str()), + reinterpret_cast<const unsigned char*>(personalization.data()), personalization.size()) == 0); ASSERT(mbedtls_ctr_drbg_random(&ctr_drbg, reinterpret_cast<unsigned char*>(&telemetry_id), sizeof(u64)) == 0); diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h index cec271df0..2a4845797 100644 --- a/src/core/telemetry_session.h +++ b/src/core/telemetry_session.h @@ -5,6 +5,7 @@ #pragma once #include <memory> +#include <string> #include "common/telemetry.h" namespace Core { @@ -30,8 +31,6 @@ public: field_collection.AddField(type, name, std::move(value)); } - static void FinalizeAsyncJob(); - private: Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields @@ -53,7 +52,6 @@ u64 RegenerateTelemetryId(); * Verifies the username and token. * @param username yuzu username to use for authentication. * @param token yuzu token to use for authentication. - * @param func A function that gets exectued when the verification is finished * @returns Future with bool indicating whether the verification succeeded */ bool VerifyLogin(const std::string& username, const std::string& token); diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 7e57de78a..85c668ca1 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -2299,8 +2299,7 @@ private: ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), "NDV is not implemented"); - const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); - const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + const std::string x = regs.GetRegisterAsFloat(instr.gpr8); const bool is_array = instr.tmml.array != 0; auto texture_type = instr.tmml.texture_type.Value(); const std::string sampler = @@ -2311,13 +2310,11 @@ private: std::string coord; switch (texture_type) { case Tegra::Shader::TextureType::Texture1D: { - std::string x = regs.GetRegisterAsFloat(instr.gpr8); coord = "float coords = " + x + ';'; break; } case Tegra::Shader::TextureType::Texture2D: { - std::string x = regs.GetRegisterAsFloat(instr.gpr8); - std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); coord = "vec2 coords = vec2(" + x + ", " + y + ");"; break; } @@ -2327,8 +2324,7 @@ private: UNREACHABLE(); // Fallback to interpreting as a 2D texture for now - std::string x = regs.GetRegisterAsFloat(instr.gpr8); - std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); + const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); coord = "vec2 coords = vec2(" + x + ", " + y + ");"; texture_type = Tegra::Shader::TextureType::Texture2D; } diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 4e4c108ab..e8ab23326 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -110,6 +110,7 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc); setWindowTitle(QString::fromStdString(window_title)); + setAttribute(Qt::WA_AcceptTouchEvents); InputCommon::Init(); InputCommon::StartJoystickEventHandler(); @@ -190,11 +191,17 @@ QByteArray GRenderWindow::saveGeometry() { return geometry; } -qreal GRenderWindow::windowPixelRatio() { +qreal GRenderWindow::windowPixelRatio() const { // windowHandle() might not be accessible until the window is displayed to screen. return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; } +std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const { + const qreal pixel_ratio = windowPixelRatio(); + return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), + static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; +} + void GRenderWindow::closeEvent(QCloseEvent* event) { emit Closed(); QWidget::closeEvent(event); @@ -209,31 +216,81 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { } void GRenderWindow::mousePressEvent(QMouseEvent* event) { + if (event->source() == Qt::MouseEventSynthesizedBySystem) + return; // touch input is handled in TouchBeginEvent + auto pos = event->pos(); if (event->button() == Qt::LeftButton) { - qreal pixelRatio = windowPixelRatio(); - this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio), - static_cast<unsigned>(pos.y() * pixelRatio)); + const auto [x, y] = ScaleTouch(pos); + this->TouchPressed(x, y); } else if (event->button() == Qt::RightButton) { InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); } } void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { + if (event->source() == Qt::MouseEventSynthesizedBySystem) + return; // touch input is handled in TouchUpdateEvent + auto pos = event->pos(); - qreal pixelRatio = windowPixelRatio(); - this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u), - std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u)); + const auto [x, y] = ScaleTouch(pos); + this->TouchMoved(x, y); InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); } void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { + if (event->source() == Qt::MouseEventSynthesizedBySystem) + return; // touch input is handled in TouchEndEvent + if (event->button() == Qt::LeftButton) this->TouchReleased(); else if (event->button() == Qt::RightButton) InputCommon::GetMotionEmu()->EndTilt(); } +void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { + // TouchBegin always has exactly one touch point, so take the .first() + const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); + this->TouchPressed(x, y); +} + +void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) { + QPointF pos; + int active_points = 0; + + // average all active touch points + for (const auto tp : event->touchPoints()) { + if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) { + active_points++; + pos += tp.pos(); + } + } + + pos /= active_points; + + const auto [x, y] = ScaleTouch(pos); + this->TouchMoved(x, y); +} + +void GRenderWindow::TouchEndEvent() { + this->TouchReleased(); +} + +bool GRenderWindow::event(QEvent* event) { + if (event->type() == QEvent::TouchBegin) { + TouchBeginEvent(static_cast<QTouchEvent*>(event)); + return true; + } else if (event->type() == QEvent::TouchUpdate) { + TouchUpdateEvent(static_cast<QTouchEvent*>(event)); + return true; + } else if (event->type() == QEvent::TouchEnd || event->type() == QEvent::TouchCancel) { + TouchEndEvent(); + return true; + } + + return QWidget::event(event); +} + void GRenderWindow::focusOutEvent(QFocusEvent* event) { QWidget::focusOutEvent(event); InputCommon::GetKeyboard()->ReleaseAllKeys(); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index f133bfadf..873985564 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -15,6 +15,7 @@ class QKeyEvent; class QScreen; +class QTouchEvent; class GGLWidgetInternal; class GMainWindow; @@ -119,7 +120,7 @@ public: void restoreGeometry(const QByteArray& geometry); // overridden QByteArray saveGeometry(); // overridden - qreal windowPixelRatio(); + qreal windowPixelRatio() const; void closeEvent(QCloseEvent* event) override; @@ -130,6 +131,8 @@ public: void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; + bool event(QEvent* event) override; + void focusOutEvent(QFocusEvent* event) override; void OnClientAreaResized(unsigned width, unsigned height); @@ -148,6 +151,11 @@ signals: void Closed(); private: + std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const; + void TouchBeginEvent(const QTouchEvent* event); + void TouchUpdateEvent(const QTouchEvent* event); + void TouchEndEvent(); + void OnMinimalClientAreaChangeRequest( const std::pair<unsigned, unsigned>& minimal_size) override; diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 155095095..a9ad92a80 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp @@ -40,6 +40,35 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { } } +std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, float touch_y) const { + int w, h; + SDL_GetWindowSize(render_window, &w, &h); + + touch_x *= w; + touch_y *= h; + + return {static_cast<unsigned>(std::max(std::round(touch_x), 0.0f)), + static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))}; +} + +void EmuWindow_SDL2::OnFingerDown(float x, float y) { + // TODO(NeatNit): keep track of multitouch using the fingerID and a dictionary of some kind + // This isn't critical because the best we can do when we have that is to average them, like the + // 3DS does + + const auto [px, py] = TouchToPixelPos(x, y); + TouchPressed(px, py); +} + +void EmuWindow_SDL2::OnFingerMotion(float x, float y) { + const auto [px, py] = TouchToPixelPos(x, y); + TouchMoved(px, py); +} + +void EmuWindow_SDL2::OnFingerUp() { + TouchReleased(); +} + void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { if (state == SDL_PRESSED) { InputCommon::GetKeyboard()->PressKey(key); @@ -219,11 +248,26 @@ void EmuWindow_SDL2::PollEvents() { OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state); break; case SDL_MOUSEMOTION: - OnMouseMotion(event.motion.x, event.motion.y); + // ignore if it came from touch + if (event.button.which != SDL_TOUCH_MOUSEID) + OnMouseMotion(event.motion.x, event.motion.y); break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: - OnMouseButton(event.button.button, event.button.state, event.button.x, event.button.y); + // ignore if it came from touch + if (event.button.which != SDL_TOUCH_MOUSEID) { + OnMouseButton(event.button.button, event.button.state, event.button.x, + event.button.y); + } + break; + case SDL_FINGERDOWN: + OnFingerDown(event.tfinger.x, event.tfinger.y); + break; + case SDL_FINGERMOTION: + OnFingerMotion(event.tfinger.x, event.tfinger.y); + break; + case SDL_FINGERUP: + OnFingerUp(); break; case SDL_QUIT: is_open = false; diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index d34902109..b0d4116cc 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h @@ -40,6 +40,18 @@ private: /// Called by PollEvents when a mouse button is pressed or released void OnMouseButton(u32 button, u8 state, s32 x, s32 y); + /// Translates pixel position (0..1) to pixel positions + std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const; + + /// Called by PollEvents when a finger starts touching the touchscreen + void OnFingerDown(float x, float y); + + /// Called by PollEvents when a finger moves while touching the touchscreen + void OnFingerMotion(float x, float y); + + /// Called by PollEvents when a finger stops touching the touchscreen + void OnFingerUp(); + /// Called by PollEvents when any event that may cause the window to be resized occurs void OnResize(); |