summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/arm/arm_interface.cpp97
-rw-r--r--src/core/arm/arm_interface.h18
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp19
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h8
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp38
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h8
-rw-r--r--src/input_common/helpers/udp_protocol.h9
-rw-r--r--src/video_core/renderer_base.cpp4
-rw-r--r--src/video_core/renderer_base.h3
-rw-r--r--src/yuzu/aboutdialog.ui2
-rw-r--r--src/yuzu/bootmanager.cpp6
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp20
-rw-r--r--src/yuzu/main.cpp40
-rw-r--r--src/yuzu/main.h3
14 files changed, 181 insertions, 94 deletions
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 08bf1201d..2a9390e26 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -14,96 +14,28 @@
#include "core/loader/loader.h"
#include "core/memory.h"
+#include "core/arm/dynarmic/arm_dynarmic_32.h"
+#include "core/arm/dynarmic/arm_dynarmic_64.h"
+
namespace Core {
constexpr u64 SEGMENT_BASE = 0x7100000000ull;
std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
- System& system, const ThreadContext64& ctx) {
- std::vector<BacktraceEntry> out;
- auto& memory = system.Memory();
-
- auto fp = ctx.cpu_registers[29];
- auto lr = ctx.cpu_registers[30];
- while (true) {
- out.push_back({
- .module = "",
- .address = 0,
- .original_address = lr,
- .offset = 0,
- .name = {},
- });
-
- if (fp == 0) {
- break;
- }
-
- lr = memory.Read64(fp + 8) - 4;
- fp = memory.Read64(fp);
- }
-
- std::map<VAddr, std::string> modules;
- auto& loader{system.GetAppLoader()};
- if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
- return {};
- }
-
- std::map<std::string, Symbols::Symbols> symbols;
- for (const auto& module : modules) {
- symbols.insert_or_assign(module.second,
- Symbols::GetSymbols(module.first, system.Memory(),
- system.CurrentProcess()->Is64BitProcess()));
- }
-
- for (auto& entry : out) {
- VAddr base = 0;
- for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) {
- const auto& module{*iter};
- if (entry.original_address >= module.first) {
- entry.module = module.second;
- base = module.first;
- break;
- }
- }
-
- entry.offset = entry.original_address - base;
- entry.address = SEGMENT_BASE + entry.offset;
-
- if (entry.module.empty())
- entry.module = "unknown";
-
- const auto symbol_set = symbols.find(entry.module);
- if (symbol_set != symbols.end()) {
- const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
- if (symbol.has_value()) {
- // TODO(DarkLordZach): Add demangling of symbol names.
- entry.name = *symbol;
- }
- }
- }
-
- return out;
+ Core::System& system, const ARM_Interface::ThreadContext32& ctx) {
+ return ARM_Dynarmic_32::GetBacktraceFromContext(system, ctx);
}
-std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
- std::vector<BacktraceEntry> out;
- auto& memory = system.Memory();
-
- auto fp = GetReg(29);
- auto lr = GetReg(30);
- while (true) {
- out.push_back({"", 0, lr, 0, ""});
- if (!fp) {
- break;
- }
- lr = memory.Read64(fp + 8) - 4;
- fp = memory.Read64(fp);
- }
+std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
+ Core::System& system, const ARM_Interface::ThreadContext64& ctx) {
+ return ARM_Dynarmic_64::GetBacktraceFromContext(system, ctx);
+}
+void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out) {
std::map<VAddr, std::string> modules;
auto& loader{system.GetAppLoader()};
if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
- return {};
+ return;
}
std::map<std::string, Symbols::Symbols> symbols;
@@ -127,8 +59,9 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
entry.offset = entry.original_address - base;
entry.address = SEGMENT_BASE + entry.offset;
- if (entry.module.empty())
+ if (entry.module.empty()) {
entry.module = "unknown";
+ }
const auto symbol_set = symbols.find(entry.module);
if (symbol_set != symbols.end()) {
@@ -139,12 +72,10 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
}
}
}
-
- return out;
}
void ARM_Interface::LogBacktrace() const {
- const VAddr sp = GetReg(13);
+ const VAddr sp = GetSP();
const VAddr pc = GetPC();
LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address",
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index dce2f4195..bcec4b3b8 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -101,6 +101,12 @@ public:
virtual u64 GetPC() const = 0;
/**
+ * Get the current Stack Pointer
+ * @return Returns current SP
+ */
+ virtual u64 GetSP() const = 0;
+
+ /**
* Get an ARM register
* @param index Register index
* @return Returns the value in the register
@@ -183,16 +189,12 @@ public:
};
static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
+ const ThreadContext32& ctx);
+ static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
const ThreadContext64& ctx);
- std::vector<BacktraceEntry> GetBacktrace() const;
+ virtual std::vector<BacktraceEntry> GetBacktrace() const = 0;
- /// fp (= r29) points to the last frame record.
- /// Note that this is the frame record for the *previous* frame, not the current one.
- /// Note we need to subtract 4 from our last read to get the proper address
- /// Frame records are two words long:
- /// fp+0 : pointer to previous frame record
- /// fp+8 : value of lr for frame
void LogBacktrace() const;
protected:
@@ -200,6 +202,8 @@ protected:
System& system;
CPUInterrupts& interrupt_handlers;
bool uses_wall_clock;
+
+ static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
};
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 5de4384db..da5659046 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -268,6 +268,10 @@ u64 ARM_Dynarmic_32::GetPC() const {
return jit.load()->Regs()[15];
}
+u64 ARM_Dynarmic_32::GetSP() const {
+ return jit.load()->Regs()[13];
+}
+
u64 ARM_Dynarmic_32::GetReg(int index) const {
return jit.load()->Regs()[index];
}
@@ -362,4 +366,19 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
jit_cache.emplace(key, std::move(new_jit));
}
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system,
+ u64 sp, u64 lr) {
+ // No way to get accurate stack traces in A32 yet
+ return {};
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext(
+ System& system, const ThreadContext32& ctx) {
+ return GetBacktrace(system, ctx.cpu_registers[13], ctx.cpu_registers[14]);
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const {
+ return GetBacktrace(system, GetReg(13), GetReg(14));
+}
+
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 684937353..1b628f94d 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -35,6 +35,7 @@ public:
void SetPC(u64 pc) override;
u64 GetPC() const override;
+ u64 GetSP() const override;
u64 GetReg(int index) const override;
void SetReg(int index, u64 value) override;
u128 GetVectorReg(int index) const override;
@@ -66,9 +67,16 @@ public:
void PageTableChanged(Common::PageTable& new_page_table,
std::size_t new_address_space_size_in_bits) override;
+ static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
+ const ThreadContext32& ctx);
+
+ std::vector<BacktraceEntry> GetBacktrace() const override;
+
private:
std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
+ static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 sp, u64 lr);
+
using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
using JitCacheType =
std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index ae0b158c2..871d9d10e 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -328,6 +328,10 @@ u64 ARM_Dynarmic_64::GetPC() const {
return jit.load()->GetPC();
}
+u64 ARM_Dynarmic_64::GetSP() const {
+ return jit.load()->GetSP();
+}
+
u64 ARM_Dynarmic_64::GetReg(int index) const {
return jit.load()->GetRegister(index);
}
@@ -430,4 +434,38 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
jit_cache.emplace(key, std::move(new_jit));
}
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system,
+ u64 fp, u64 lr) {
+ std::vector<BacktraceEntry> out;
+ auto& memory = system.Memory();
+
+ // fp (= r29) points to the last frame record.
+ // Note that this is the frame record for the *previous* frame, not the current one.
+ // Note we need to subtract 4 from our last read to get the proper address
+ // Frame records are two words long:
+ // fp+0 : pointer to previous frame record
+ // fp+8 : value of lr for frame
+ while (true) {
+ out.push_back({"", 0, lr, 0, ""});
+ if (!fp) {
+ break;
+ }
+ lr = memory.Read64(fp + 8) - 4;
+ fp = memory.Read64(fp);
+ }
+
+ SymbolicateBacktrace(system, out);
+
+ return out;
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext(
+ System& system, const ThreadContext64& ctx) {
+ return GetBacktrace(system, ctx.cpu_registers[29], ctx.cpu_registers[30]);
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const {
+ return GetBacktrace(system, GetReg(29), GetReg(30));
+}
+
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 86018f196..78773e293 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -33,6 +33,7 @@ public:
void SetPC(u64 pc) override;
u64 GetPC() const override;
+ u64 GetSP() const override;
u64 GetReg(int index) const override;
void SetReg(int index, u64 value) override;
u128 GetVectorReg(int index) const override;
@@ -60,10 +61,17 @@ public:
void PageTableChanged(Common::PageTable& new_page_table,
std::size_t new_address_space_size_in_bits) override;
+ static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
+ const ThreadContext64& ctx);
+
+ std::vector<BacktraceEntry> GetBacktrace() const override;
+
private:
std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
std::size_t address_space_bits) const;
+ static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr);
+
using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
using JitCacheType =
std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>;
diff --git a/src/input_common/helpers/udp_protocol.h b/src/input_common/helpers/udp_protocol.h
index 9c205e944..597f51cd3 100644
--- a/src/input_common/helpers/udp_protocol.h
+++ b/src/input_common/helpers/udp_protocol.h
@@ -8,8 +8,17 @@
#include <optional>
#include <type_traits>
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used
+#endif
+
#include <boost/crc.hpp>
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
#include "common/swap.h"
namespace InputCommon::CemuhookUDP {
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index c9627cffd..9756a81d6 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -26,6 +26,10 @@ void RendererBase::UpdateCurrentFramebufferLayout() {
render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
}
+bool RendererBase::IsScreenshotPending() const {
+ return renderer_settings.screenshot_requested;
+}
+
void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callback,
const Layout::FramebufferLayout& layout) {
if (renderer_settings.screenshot_requested) {
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index c5f974080..30d19b178 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -83,6 +83,9 @@ public:
/// Refreshes the settings common to all renderers
void RefreshBaseSettings();
+ /// Returns true if a screenshot is being processed
+ bool IsScreenshotPending() const;
+
/// Request a screenshot of the next frame
void RequestScreenshot(void* data, std::function<void(bool)> callback,
const Layout::FramebufferLayout& layout);
diff --git a/src/yuzu/aboutdialog.ui b/src/yuzu/aboutdialog.ui
index 27d81cd13..2f7ddc7f3 100644
--- a/src/yuzu/aboutdialog.ui
+++ b/src/yuzu/aboutdialog.ui
@@ -87,7 +87,7 @@
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
-&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv3.0+.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;This software should not be used to play games you have not legally obtained.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 27b0300d2..a1b819ae0 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -933,6 +933,12 @@ void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) {
auto& renderer = system.Renderer();
const f32 res_scale = Settings::values.resolution_info.up_factor;
+ if (renderer.IsScreenshotPending()) {
+ LOG_WARNING(Render,
+ "A screenshot is already requested or in progress, ignoring the request");
+ return;
+ }
+
const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)};
screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
renderer.RequestScreenshot(
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 291e37acf..a0f29d147 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -10,6 +10,7 @@
#include <QMenu>
#include <QMessageBox>
#include <QTimer>
+#include "common/assert.h"
#include "common/param_package.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
@@ -119,6 +120,23 @@ QString GetButtonName(Common::Input::ButtonNames button_name) {
}
}
+QString GetDirectionName(const std::string& direction) {
+ if (direction == "left") {
+ return QObject::tr("Left");
+ }
+ if (direction == "right") {
+ return QObject::tr("Right");
+ }
+ if (direction == "up") {
+ return QObject::tr("Up");
+ }
+ if (direction == "down") {
+ return QObject::tr("Down");
+ }
+ UNIMPLEMENTED_MSG("Unimplemented direction name={}", direction);
+ return QString::fromStdString(direction);
+}
+
void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param,
const std::string& button_name) {
// The poller returned a complete axis, so set all the buttons
@@ -162,7 +180,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
if (common_button_name == Common::Input::ButtonNames::Value) {
if (param.Has("hat")) {
- const QString hat = QString::fromStdString(param.Get("direction", ""));
+ const QString hat = GetDirectionName(param.Get("direction", ""));
return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat);
}
if (param.Has("axis")) {
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 52879a989..5e26aad29 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -152,7 +152,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
}
#endif
-constexpr int default_mouse_timeout = 2500;
+constexpr int default_mouse_hide_timeout = 2500;
+constexpr int default_mouse_center_timeout = 10;
/**
* "Callouts" are one-time instructional messages shown to the user. In the config settings, there
@@ -287,10 +288,13 @@ GMainWindow::GMainWindow()
ui->menubar->setCursor(QCursor());
statusBar()->setCursor(QCursor());
- mouse_hide_timer.setInterval(default_mouse_timeout);
+ mouse_hide_timer.setInterval(default_mouse_hide_timeout);
connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);
connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor);
+ mouse_center_timer.setInterval(default_mouse_center_timeout);
+ connect(&mouse_center_timer, &QTimer::timeout, this, &GMainWindow::CenterMouseCursor);
+
MigrateConfigFiles();
#if defined(HAVE_SDL2) && !defined(_WIN32)
@@ -3301,10 +3305,26 @@ void GMainWindow::ShowMouseCursor() {
}
}
+void GMainWindow::CenterMouseCursor() {
+ if (emu_thread == nullptr || !Settings::values.mouse_panning) {
+ mouse_center_timer.stop();
+ return;
+ }
+ if (!this->isActiveWindow()) {
+ mouse_center_timer.stop();
+ return;
+ }
+ const int center_x = render_window->width() / 2;
+ const int center_y = render_window->height() / 2;
+
+ QCursor::setPos(mapToGlobal({center_x, center_y}));
+}
+
void GMainWindow::OnMouseActivity() {
if (!Settings::values.mouse_panning) {
ShowMouseCursor();
}
+ mouse_center_timer.stop();
}
void GMainWindow::OnCoreError(Core::SystemResultStatus result, std::string details) {
@@ -3577,6 +3597,22 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
AcceptDropEvent(event);
}
+void GMainWindow::leaveEvent(QEvent* event) {
+ if (Settings::values.mouse_panning) {
+ const QRect& rect = geometry();
+ QPoint position = QCursor::pos();
+
+ qint32 x = qBound(rect.left(), position.x(), rect.right());
+ qint32 y = qBound(rect.top(), position.y(), rect.bottom());
+ // Only start the timer if the mouse has left the window bound.
+ // The leave event is also triggered when the window looses focus.
+ if (x != position.x() || y != position.y()) {
+ mouse_center_timer.start();
+ }
+ event->accept();
+ }
+}
+
bool GMainWindow::ConfirmChangeGame() {
if (emu_thread == nullptr)
return true;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index ab95a7518..b399e9b01 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -328,6 +328,7 @@ private:
void UpdateUISettings();
void HideMouseCursor();
void ShowMouseCursor();
+ void CenterMouseCursor();
void OpenURL(const QUrl& url);
void LoadTranslation();
void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
@@ -372,6 +373,7 @@ private:
bool auto_paused = false;
bool auto_muted = false;
QTimer mouse_hide_timer;
+ QTimer mouse_center_timer;
// FS
std::shared_ptr<FileSys::VfsFilesystem> vfs;
@@ -418,4 +420,5 @@ protected:
void dropEvent(QDropEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override;
void dragMoveEvent(QDragMoveEvent* event) override;
+ void leaveEvent(QEvent* event) override;
};