diff --git a/.gitmodules b/.gitmodules index f9f9206df..41b12b89f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,4 @@ [submodule "ext/KDDockWidgets"] path = ext/KDDockWidgets url = https://github.com/olive-editor/KDDockWidgets.git + branch = main diff --git a/CMakeLists.txt b/CMakeLists.txt index 303939381..4115edd31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,11 @@ cmake_minimum_required(VERSION 3.13 FATAL_ERROR) project(olive-editor VERSION 0.2.0 LANGUAGES CXX) - +if(${CMAKE_BUILD_TYPE} EQUAL Debug) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory -g") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=memory -g") +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} -fsanitize=memory) +endif () option(BUILD_QT6 "Build with Qt 6 over 5 (experimental)" ON) option(BUILD_DOXYGEN "Build Doxygen documentation" OFF) option(BUILD_TESTS "Build unit tests" OFF) @@ -168,7 +172,7 @@ list(APPEND OLIVE_LIBRARIES ) # Link FFmpeg -find_package(FFMPEG 3.0 REQUIRED +find_package(FFMPEG REQUIRED COMPONENTS avutil avcodec diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index a412f7dd5..502587b3d 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -140,7 +140,7 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, break; } - AVFrame *hw_in = f.get(); + AVFramePtr hw_in = f; VideoParams plane_params = vp; plane_params.set_channel_count(1); @@ -148,7 +148,7 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, TexturePtr y_plane = p.renderer->CreateTexture( plane_params, hw_in->data[0], hw_in->linesize[0] / px_size); - + y_plane->handleFrame(hw_in); switch (f->format) { case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUV422P: @@ -170,8 +170,11 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, TexturePtr u_plane = p.renderer->CreateTexture( plane_params, hw_in->data[1], hw_in->linesize[1] / px_size); + u_plane->handleFrame(hw_in); + TexturePtr v_plane = p.renderer->CreateTexture( plane_params, hw_in->data[2], hw_in->linesize[2] / px_size); + v_plane->handleFrame(hw_in); ShaderJob job; job.Insert(QStringLiteral("y_channel"), @@ -207,6 +210,7 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, case AV_PIX_FMT_RGBA: case AV_PIX_FMT_RGBA64LE: // RGBA can be uploaded directly to the texture + tex->handleFrame(f); tex->Upload(f->data[0], f->linesize[0] / vp.GetBytesPerPixel()); break; } @@ -282,14 +286,17 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(const RetrieveVideoParams &p) AVCOL_RANGE_MPEG; // Perform any CPU processing required - f = PreProcessFrame(f, p); + AVFramePtr ptr = PreProcessFrame(f, p); + f=std::move(ptr); if (!f) { // Error occurred while software scaling return nullptr; } // Finally, perform any GPU processing required - return ProcessFrameIntoTexture(f, p, original); + TexturePtr texture = ProcessFrameIntoTexture(f, p, original); + + return texture; } return nullptr; @@ -799,7 +806,7 @@ AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f, dest->format = f->format; dest->color_range = f->color_range; dest->colorspace = f->colorspace; - + dest->hw_frames_ctx = nullptr; if (p.divider > 1) { dest->width = VideoParams::GetScaledDimension(dest->width, p.divider); dest->height = VideoParams::GetScaledDimension(dest->height, p.divider); @@ -852,8 +859,8 @@ AVFramePtr FFmpegDecoder::PreProcessFrame(AVFramePtr f, dest->color_range == AVCOL_RANGE_JPEG ? 1 : 0, 0, 0x10000, 0x10000); } - r = sws_scale(sws_ctx_, f->data, f->linesize, 0, f->height, dest->data, - dest->linesize); + r = sws_scale_frame(sws_ctx_, dest.get(), f.get()); + if (r < 0) { FFmpegError(r); return nullptr; diff --git a/app/common/ffmpegutils.h b/app/common/ffmpegutils.h index 1d5e7da29..a667fb925 100644 --- a/app/common/ffmpegutils.h +++ b/app/common/ffmpegutils.h @@ -87,7 +87,7 @@ public: using AVFramePtr = std::shared_ptr; inline AVFramePtr CreateAVFramePtr(AVFrame *f) { - return std::shared_ptr(f, [](AVFrame *g) { av_frame_free(&g); }); + return std::shared_ptr(f, [](AVFrame *g) { av_frame_free(&g); }); } inline AVFramePtr CreateAVFramePtr() { diff --git a/app/core.cpp b/app/core.cpp index 412c9972e..b6d2dd5b0 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -776,6 +776,7 @@ void Core::StartGUI(bool full_screen) connect(qApp, &QApplication::focusChanged, PanelManager::instance(), &PanelManager::FocusChanged); + KDDockWidgets::initFrontend(KDDockWidgets::FrontendType::QtWidgets); // Set KDDockWidgets flags auto &config = KDDockWidgets::Config::self(); auto flags = config.flags(); diff --git a/app/main.cpp b/app/main.cpp index 6e351735c..2545f9a95 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -145,6 +145,7 @@ int decompress_project(const QString &project) int main(int argc, char *argv[]) { + // Set up debug handler qInstallMessageHandler(olive::DebugHandler); diff --git a/app/panel/panel.cpp b/app/panel/panel.cpp index b1262624f..601ccc7ca 100644 --- a/app/panel/panel.cpp +++ b/app/panel/panel.cpp @@ -27,23 +27,26 @@ #include #include #include +#include "Window_p.h" #include "panel/panelmanager.h" +#include + namespace olive { -#define super KDDockWidgets::DockWidget +#define super KDDockWidgets::QtWidgets::DockWidget PanelWidget::PanelWidget(const QString &object_name) : super(object_name) , border_visible_(false) , signal_instead_of_close_(false) { - setFocusPolicy(Qt::ClickFocus); + View::setFocusPolicy(Qt::ClickFocus); connect(this, &PanelWidget::shown, this, - static_cast(&PanelWidget::setFocus)); + reinterpret_cast(&PanelWidget::setFocus)); PanelManager::instance()->RegisterPanel(this); } @@ -125,6 +128,15 @@ void PanelWidget::changeEvent(QEvent *e) if (e->type() == QEvent::LanguageChange) { Retranslate(); } + + if (e->type() == QEvent::WindowStateChange) { + if (isVisible() && !isMinimized()) { + emit shown(Qt::OtherFocusReason); + } + else { + emit hidden(); + } + } super::changeEvent(e); } diff --git a/app/panel/panel.h b/app/panel/panel.h index 56777fd11..787a4981b 100644 --- a/app/panel/panel.h +++ b/app/panel/panel.h @@ -21,18 +21,23 @@ #ifndef PANEL_WIDGET_H #define PANEL_WIDGET_H +#include "KDDockWidgets/src/core/Window_p.h" +#include "KDDockWidgets/src/qtwidgets/views/TabBar.h" + #include #include #include "common/define.h" +#include + namespace olive { /** * @brief A widget that is always dockable within the MainWindow. */ -class PanelWidget : public KDDockWidgets::DockWidget { +class PanelWidget : public KDDockWidgets::QtWidgets::DockWidget { Q_OBJECT public: /** @@ -279,7 +284,8 @@ public: signals: void CloseRequested(); - + void shown(Qt::FocusReason reason); + void hidden(); protected: /** * @brief paintEvent @@ -323,7 +329,7 @@ protected slots: * String to set the subtitle to */ void SetSubtitle(const QString &t); - +protected slots: private: /** * @brief Internal function that sets the QDockWidget's window title whenever the title/subtitle change. @@ -339,6 +345,10 @@ private: bool border_visible_; bool signal_instead_of_close_; + + QMetaObject::Connection m_tabBarConnection; + QMetaObject::Connection m_windowConnection; + bool m_lastVisibleState = false; }; } diff --git a/app/panel/project/project.cpp b/app/panel/project/project.cpp index f2f011e10..b2525f3b7 100644 --- a/app/panel/project/project.cpp +++ b/app/panel/project/project.cpp @@ -180,7 +180,7 @@ void ProjectPanel::ItemDoubleClickSlot(Node *item) PanelManager::instance()->MostRecentlyFocused(); panel->ConnectViewerNode(static_cast(item)); panel->raise(); - panel->setFocus(); + panel->setFocus(Qt::FocusReason::MouseFocusReason); } else if (dynamic_cast(item)) { // Open this sequence in the Timeline Core::instance()->main_window()->OpenSequence( diff --git a/app/render/texture.h b/app/render/texture.h index 350ffe9e3..d167343fb 100644 --- a/app/render/texture.h +++ b/app/render/texture.h @@ -21,6 +21,8 @@ #ifndef RENDERTEXTURE_H #define RENDERTEXTURE_H +#include "common/ffmpegutils.h" + #include #include @@ -150,7 +152,10 @@ public: { return job_; } - + void handleFrame(AVFramePtr ptr) + { + frame_=ptr; + } private: Renderer *renderer_; @@ -159,6 +164,8 @@ private: QVariant id_; AcceleratedJob *job_; + + AVFramePtr frame_; }; } diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index cc17e6993..52f11887e 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -1203,8 +1203,11 @@ void ViewerWidget::UpdateMinimumScale() // Avoids divide by zero SetMinimumScale(0); } else { - SetMinimumScale(static_cast(ruler()->width()) / - GetConnectedNode()->GetLength().toDouble()); + double min_scale = static_cast(ruler()->width()) / + GetConnectedNode()->GetLength().toDouble(); + // Ensure min_scale doesn't exceed max_scale to prevent crash + min_scale = qMin(min_scale, GetMaximumScale()); + SetMinimumScale(min_scale); } } diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index d556ebf8b..b020f98e1 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -29,15 +29,17 @@ #include #endif +#include "KDDockWidgets/src/qtwidgets/Window_p.h" #include "dialog/about/about.h" #include "mainmenu.h" #include "mainstatusbar.h" +#include "KDDockWidgets/src/LayoutSaver.h" #include "timeline/timelineundoworkarea.h" namespace olive { -#define super KDDockWidgets::MainWindow +#define super KDDockWidgets::QtWidgets::MainWindow MainWindow::MainWindow(QWidget *parent) : super(QStringLiteral("OliveMain"), KDDockWidgets::MainWindowOption_None, @@ -95,7 +97,7 @@ MainWindow::MainWindow(QWidget *parent) // emit the "shown" signal before emitting the "hidden" signals, resulting // in Core thinking there are -1 pixel samplers open. To mitigate that, // we force "shown" to emit ourselves here. - emit pixel_sampler_panel_->shown(); + emit pixel_sampler_panel_->shown(Qt::OtherFocusReason); // Make node-related connections connect(node_panel_, &NodePanel::NodeSelectionChangedWithContexts, @@ -382,7 +384,7 @@ void MainWindow::ToggleMaximizedPanel() premaximized_state_.clear(); currently_focused_panel->raise(); - currently_focused_panel->setFocus(); + currently_focused_panel->setFocus(Qt::ActiveWindowFocusReason); PanelManager::instance()->SetSuppressChangedSignal(false); } @@ -430,7 +432,7 @@ void MainWindow::SetProject(Project *p) project_panel_->set_project(p); if (project_) { - project_panel_->setFocus(); + project_panel_->setFocus(Qt::OtherFocusReason); } } diff --git a/app/window/mainwindow/mainwindow.h b/app/window/mainwindow/mainwindow.h index ac925c5c5..c56b80f17 100644 --- a/app/window/mainwindow/mainwindow.h +++ b/app/window/mainwindow/mainwindow.h @@ -53,7 +53,7 @@ namespace olive /** * @brief Olive's main window responsible for docking widgets and the main menu bar. */ -class MainWindow : public KDDockWidgets::MainWindow { +class MainWindow : public KDDockWidgets::QtWidgets::MainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); diff --git a/ext/KDDockWidgets b/ext/KDDockWidgets index f24a04252..d24ba267d 160000 --- a/ext/KDDockWidgets +++ b/ext/KDDockWidgets @@ -1 +1 @@ -Subproject commit f24a04252ba319ef323769e766b3ba5252c15c4c +Subproject commit d24ba267da1a8e7e1f34d69db5e07336d68f6886 diff --git a/ext/core b/ext/core index 25e4152bb..a3628c464 160000 --- a/ext/core +++ b/ext/core @@ -1 +1 @@ -Subproject commit 25e4152bb70a0f4e30b9249e4f4b31e7936dc84a +Subproject commit a3628c46417f373596455a2bb724bf20a8edf9bc