build: split the engine into liboakengine.so; worker drops the UI entirely
Physical split: app/{audio,cli,codec,common,config,node,pluginSupport,
render,task,timeline,undo,tool,shaders} plus coreengine, version and
ui/icons+colorcoding move to a new top-level engine/ tree, built as
liboakengine.so (shared). The render backends (oakgl/oakvulkan) move
with it and link the engine library instead of embedding a static
render-core subset (libolive-rendercore is gone).
- oak-render-worker now links liboakengine instead of the whole
libolive-editor object set: 336MB -> 2.9MB, no Qt Widgets UI
- the editor links liboakengine for the engine and keeps only UI
objects in libolive-editor
- install/packaging: GNUInstallDirs libdir on Linux, bundle copy on
macOS, oakengine.dll staged for NSIS, AppImage validation entry
- fix backend lookup for the new layout: DynamicRenderer searched
../app but backends now live in engine/; a stale pre-split liboakgl
in the build tree got dlopened instead, re-initialized and later
destroyed the interposed engine statics (full-suite segfault at
DialogSequenceParameterTab, found via gdb watchpoint)
This commit is contained in:
@@ -0,0 +1,617 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_COREENGINE_H
|
||||
#define OAK_COREENGINE_H
|
||||
|
||||
#include <olive/core/core.h>
|
||||
#include <QFileInfoList>
|
||||
#include <QList>
|
||||
#include <QTimer>
|
||||
#include <QTranslator>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "node/project/footage/footage.h"
|
||||
#include "node/project.h"
|
||||
#include "node/project/sequence/sequence.h"
|
||||
#include "node/project/serializer/mainwindowlayoutinfo.h"
|
||||
#include "task/task.h"
|
||||
#include "tool/tool.h"
|
||||
#include "undo/undostack.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The UI-independent engine core of the Olive application
|
||||
*
|
||||
* EngineCore holds the global application state that does not depend on the
|
||||
* UI (no widgets, dialogs, panels or windows) and can therefore run headless
|
||||
* (e.g. in the render worker or in tests). The UI layer (olive::Core)
|
||||
* derives from this class and registers handlers / connects signals for
|
||||
* everything that requires user interaction (see the std::function handlers
|
||||
* below, modelled after Config::ErrorHandler).
|
||||
*
|
||||
* This class must only include engine-side headers (olive/core, node/, undo/,
|
||||
* task/, timeline/, codec/, common/, config/, tool/, audio/, render/). It must
|
||||
* never include widget/, dialog/, panel/, window/ or ui/ headers.
|
||||
*/
|
||||
class EngineCore : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
class CoreParams {
|
||||
public:
|
||||
CoreParams();
|
||||
|
||||
enum RunMode { k_run_normal, k_headless_export, k_headless_pre_cache };
|
||||
|
||||
bool fullscreen() const
|
||||
{
|
||||
return run_fullscreen_;
|
||||
}
|
||||
|
||||
void set_fullscreen(bool e)
|
||||
{
|
||||
run_fullscreen_ = e;
|
||||
}
|
||||
|
||||
RunMode run_mode() const
|
||||
{
|
||||
return mode_;
|
||||
}
|
||||
|
||||
void set_run_mode(RunMode m)
|
||||
{
|
||||
mode_ = m;
|
||||
}
|
||||
|
||||
const QString startup_project() const
|
||||
{
|
||||
return startup_project_;
|
||||
}
|
||||
|
||||
void set_startup_project(const QString &p)
|
||||
{
|
||||
startup_project_ = p;
|
||||
}
|
||||
|
||||
const QString &startup_language() const
|
||||
{
|
||||
return startup_language_;
|
||||
}
|
||||
|
||||
void set_startup_language(const QString &s)
|
||||
{
|
||||
startup_language_ = s;
|
||||
}
|
||||
|
||||
bool crash_on_startup() const
|
||||
{
|
||||
return crash_;
|
||||
}
|
||||
|
||||
void set_crash_on_startup(bool e)
|
||||
{
|
||||
crash_ = true;
|
||||
}
|
||||
|
||||
private:
|
||||
RunMode mode_;
|
||||
|
||||
QString startup_project_;
|
||||
|
||||
QString startup_language_;
|
||||
|
||||
bool run_fullscreen_;
|
||||
|
||||
bool crash_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief EngineCore Constructor
|
||||
*/
|
||||
EngineCore(const CoreParams ¶ms);
|
||||
|
||||
/**
|
||||
* @brief EngineCore object accessible from anywhere in the code
|
||||
*
|
||||
* Use this to access engine functions. This assumes the object is only
|
||||
* ever constructed once at the application entry point.
|
||||
*/
|
||||
static EngineCore *instance();
|
||||
|
||||
static QString footage_file_dialog_filter();
|
||||
static QStringList allowed_footage_extensions();
|
||||
static bool is_footage_extension_allowed(const QString &path);
|
||||
|
||||
const CoreParams &core_params() const
|
||||
{
|
||||
return core_params_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start the engine
|
||||
*
|
||||
* Initializes everything that is independent of the UI: config, locale,
|
||||
* meta types, and the global engine managers (NodeFactory, ColorManager,
|
||||
* TaskManager, ConformManager, ProxyManager, RenderManager, FrameManager,
|
||||
* ProjectSerializer). Also loads the recent projects list and starts the
|
||||
* autorecovery timer.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* @brief Stop the engine
|
||||
*
|
||||
* Frees the global engine managers and saves the config. In a UI build,
|
||||
* call this after the UI services have been torn down.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* @brief Retrieve UndoStack object
|
||||
*/
|
||||
UndoStack *undo_stack();
|
||||
|
||||
/**
|
||||
* @brief Get the currently active tool
|
||||
*/
|
||||
const Tool::Item &tool() const;
|
||||
|
||||
/**
|
||||
* @brief Get the currently selected object that the add tool should make (if the add tool is active)
|
||||
*/
|
||||
const Tool::AddableObject &get_selected_addable_object() const;
|
||||
|
||||
/**
|
||||
* @brief Get the currently selected node that the transition tool should make (if the transition tool is active)
|
||||
*/
|
||||
const QString &get_selected_transition() const;
|
||||
|
||||
/**
|
||||
* @brief Get current snapping value
|
||||
*/
|
||||
const bool &snapping() const;
|
||||
|
||||
/**
|
||||
* @brief Returns a list of the most recently opened/saved projects
|
||||
*/
|
||||
const QStringList &get_recent_projects() const;
|
||||
|
||||
/**
|
||||
* @brief Gets current timecode display mode
|
||||
*/
|
||||
Timecode::Display get_timecode_display() const;
|
||||
|
||||
/**
|
||||
* @brief Sets current timecode display mode
|
||||
*/
|
||||
void set_timecode_display(Timecode::Display d);
|
||||
|
||||
/**
|
||||
* @brief Set how frequently an autorecovery should be saved (if the project has changed, see SetProjectModified())
|
||||
*/
|
||||
void set_autorecovery_interval(int minutes);
|
||||
|
||||
static void copy_string_to_clipboard(const QString &s);
|
||||
|
||||
static QString paste_string_from_clipboard();
|
||||
|
||||
/**
|
||||
* @brief Recursively count files in a file/directory list
|
||||
*/
|
||||
static int count_files_in_file_list(const QFileInfoList &filenames);
|
||||
|
||||
/**
|
||||
* @brief Create a new sequence named appropriately for the given project
|
||||
*/
|
||||
static Sequence *create_new_sequence_for_project(const QString &format,
|
||||
Project *project);
|
||||
static Sequence *create_new_sequence_for_project(Project *project)
|
||||
{
|
||||
return create_new_sequence_for_project(tr("Sequence %1"), project);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check each footage object for whether it still exists or has changed
|
||||
*
|
||||
* Missing footage is passed to the relink handler registered by the UI
|
||||
* layer. Without a handler, the project is accepted as-is.
|
||||
*/
|
||||
bool validate_footage_in_loaded_project(Project *project,
|
||||
const QString &project_saved_url);
|
||||
|
||||
/**
|
||||
* @brief Changes the current language
|
||||
*/
|
||||
bool set_language(const QString &locale);
|
||||
|
||||
/**
|
||||
* @brief Show a message in the status bar
|
||||
*
|
||||
* The engine cannot show UI itself, so this only emits
|
||||
* status_message_show(). The UI layer connects the signal to the main
|
||||
* window's status bar.
|
||||
*/
|
||||
void show_status_bar_message(const QString &s, int timeout = 0);
|
||||
|
||||
void clear_status_bar_message();
|
||||
|
||||
bool is_magic_enabled() const
|
||||
{
|
||||
return magic_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handler confirming that a file should be imported as an image sequence
|
||||
*
|
||||
* Registered by the UI layer (e.g. a QMessageBox-based prompt). Without
|
||||
* a handler, the import is accepted.
|
||||
*/
|
||||
using ConfirmImageSequenceHandler =
|
||||
std::function<bool(const QString &filename)>;
|
||||
void set_confirm_image_sequence_handler(ConfirmImageSequenceHandler handler);
|
||||
|
||||
/**
|
||||
* @brief Handler offering to relink footage that could not be validated
|
||||
*
|
||||
* Registered by the UI layer (e.g. a FootageRelinkDialog). Should return
|
||||
* false to reject the project. Without a handler, the project is accepted.
|
||||
*/
|
||||
using FootageRelinkHandler = std::function<bool(QVector<Footage *>)>;
|
||||
void set_relink_handler(FootageRelinkHandler handler);
|
||||
|
||||
/**
|
||||
* @brief Handler performing the actual project save for autorecovery
|
||||
*
|
||||
* Registered by the UI layer, since saving involves UI state (the main
|
||||
* window layout). Without a handler, the actual file write is skipped.
|
||||
*/
|
||||
using SaveProjectHandler =
|
||||
std::function<void(const QString &override_filename)>;
|
||||
void set_save_project_handler(SaveProjectHandler handler);
|
||||
|
||||
/**
|
||||
* @brief Handler closing the currently open project
|
||||
*
|
||||
* Registered by the UI layer, which may prompt the user to save first.
|
||||
* Should return false if the close was cancelled. Without a handler, the
|
||||
* project is closed silently.
|
||||
*/
|
||||
using CloseProjectHandler = std::function<bool()>;
|
||||
void set_close_project_handler(CloseProjectHandler handler);
|
||||
|
||||
/**
|
||||
* @brief Handler applying a loaded main window layout after a project load
|
||||
*/
|
||||
using LoadLayoutHandler =
|
||||
std::function<void(const MainWindowLayoutInfo &layout)>;
|
||||
void set_load_layout_handler(LoadLayoutHandler handler);
|
||||
|
||||
/**
|
||||
* @brief Update engine state after a project was successfully saved
|
||||
*
|
||||
* Pushes the project to the top of the recent list, clears its modified
|
||||
* flag and removes it from the unrecovered list.
|
||||
*/
|
||||
void on_project_saved(Project *p);
|
||||
|
||||
/**
|
||||
* @brief Removes a project from the recently opened list (e.g. if it no longer exists)
|
||||
*/
|
||||
void remove_recently_opened_project(int index);
|
||||
|
||||
#ifdef USE_OTIO
|
||||
/**
|
||||
* @brief Handler showing the OTIO import options dialog
|
||||
*
|
||||
* Registered by the UI layer. Without a handler, the import is accepted.
|
||||
*/
|
||||
using OtioImportHandler =
|
||||
std::function<bool(const QList<Sequence *> &sequences)>;
|
||||
void set_otio_import_handler(OtioImportHandler handler);
|
||||
#endif
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Set the current application-wide tool
|
||||
*
|
||||
* @param tool
|
||||
*/
|
||||
void set_tool(const Tool::Item &tool);
|
||||
|
||||
/**
|
||||
* @brief Set the current snapping setting
|
||||
*/
|
||||
void set_snapping(const bool &b);
|
||||
|
||||
/**
|
||||
* @brief Globally enable or disable decoding from proxy media
|
||||
*
|
||||
* When disabled, all footage decodes from its original media regardless of
|
||||
* each footage's individual proxy setting. The per-footage settings are
|
||||
* preserved and take effect again when this is re-enabled.
|
||||
*/
|
||||
void set_use_proxy_media(bool enabled);
|
||||
|
||||
/**
|
||||
* @brief Set the currently selected object that the add tool should make
|
||||
*/
|
||||
void set_selected_addable_object(const Tool::AddableObject &obj);
|
||||
|
||||
/**
|
||||
* @brief Set the currently selected object that the add tool should make
|
||||
*/
|
||||
void set_selected_transition_object(const QString &obj);
|
||||
|
||||
/**
|
||||
* @brief Clears the list of recently opened/saved projects
|
||||
*/
|
||||
void clear_open_recent_list();
|
||||
|
||||
/**
|
||||
* @brief Creates a new empty project and opens it
|
||||
*
|
||||
* The currently open project is closed through the close handler, so the
|
||||
* UI layer may prompt to save first.
|
||||
*/
|
||||
void create_new_project();
|
||||
|
||||
void request_pixel_sampling_in_viewers(bool e);
|
||||
|
||||
/**
|
||||
* @brief Warn that the disk cache is full (at most once)
|
||||
*
|
||||
* Only emits cache_full_warning_requested(); the UI layer shows the
|
||||
* actual dialog.
|
||||
*/
|
||||
void warn_cache_full();
|
||||
|
||||
void set_magic(bool e)
|
||||
{
|
||||
magic_ = e;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Invokable forwarder for the image sequence confirmation
|
||||
*
|
||||
* Import tasks run on worker threads and call this slot through
|
||||
* QMetaObject::invokeMethod with Qt::BlockingQueuedConnection. It runs
|
||||
* on the main thread and forwards to the registered handler; without a
|
||||
* handler, the import is accepted.
|
||||
*/
|
||||
bool confirm_image_sequence(const QString &filename);
|
||||
|
||||
#ifdef USE_OTIO
|
||||
/**
|
||||
* @brief Invokable forwarder for the OTIO import dialog
|
||||
*
|
||||
* Same threading pattern as confirm_image_sequence().
|
||||
*/
|
||||
bool show_otio_import_dialog(const QList<Sequence *> &sequences);
|
||||
#endif
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal emitted when the tool is changed from somewhere
|
||||
*/
|
||||
void tool_changed(const Tool::Item &tool);
|
||||
|
||||
/**
|
||||
* @brief Signal emitted when addable object changes through SetSelectedAddableObject
|
||||
*/
|
||||
void addable_object_changed(Tool::AddableObject o);
|
||||
|
||||
/**
|
||||
* @brief Signal emitted when the snapping setting is changed
|
||||
*/
|
||||
void snapping_changed(const bool &b);
|
||||
|
||||
/**
|
||||
* @brief Signal emitted when the default timecode display mode changed
|
||||
*/
|
||||
void timecode_display_changed(Timecode::Display d);
|
||||
|
||||
/**
|
||||
* @brief Signal emitted when a change is made to the open recent list
|
||||
*/
|
||||
void open_recent_list_changed();
|
||||
|
||||
/**
|
||||
* @brief Enable mouse color sampling functionality on all viewers
|
||||
*
|
||||
* This can be slow, so we only turn it on when we need it.
|
||||
*/
|
||||
void color_picker_enabled(bool e);
|
||||
|
||||
/**
|
||||
* @brief A viewer with color picked enabled has emitted a color
|
||||
*/
|
||||
void color_picker_color_emitted(const Color &reference, const Color &display);
|
||||
|
||||
/**
|
||||
* @brief Request showing a message in the main window's status bar
|
||||
*/
|
||||
void status_message_show(const QString &message, int timeout);
|
||||
|
||||
/**
|
||||
* @brief Request clearing the main window's status bar
|
||||
*/
|
||||
void status_message_clear();
|
||||
|
||||
/**
|
||||
* @brief Request showing the "disk cache full" warning to the user
|
||||
*/
|
||||
void cache_full_warning_requested();
|
||||
|
||||
/**
|
||||
* @brief Signal emitted when the active (open) project changed
|
||||
*
|
||||
* The UI layer uses this to set the project on the main window.
|
||||
*/
|
||||
void active_project_changed(Project *p);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Adds a project to the "open projects" list
|
||||
*/
|
||||
void add_open_project(olive::Project *p, bool add_to_recents = false);
|
||||
|
||||
bool add_open_project_from_task(Task *task, bool add_to_recents);
|
||||
|
||||
void set_active_project(Project *p);
|
||||
|
||||
/**
|
||||
* @brief Currently open project
|
||||
*
|
||||
* Protected so the UI layer (olive::Core) can read it.
|
||||
*/
|
||||
Project *open_project_;
|
||||
|
||||
static QString get_auto_recovery_index_filename();
|
||||
|
||||
protected slots:
|
||||
void add_recovery_project_from_task(Task *task);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Returns the filename where the recently opened/saved projects should be stored
|
||||
*/
|
||||
static QString get_recent_projects_file_path();
|
||||
|
||||
/**
|
||||
* @brief Called only on startup to set the locale
|
||||
*/
|
||||
void set_startup_locale();
|
||||
|
||||
/**
|
||||
* @brief Adds a filename to the top of the recently opened projects list (or moves it if it already exists)
|
||||
*/
|
||||
void push_recently_opened_project(const QString &s);
|
||||
|
||||
/**
|
||||
* @brief Declare custom types/classes for Qt's signal/slot system
|
||||
*
|
||||
* Qt's signal/slot system requires types to be declared. In the interest of doing this only at startup, we contain
|
||||
* them all in a function here.
|
||||
*/
|
||||
void declare_types_for_qt();
|
||||
|
||||
void save_unrecovered_list();
|
||||
|
||||
void save_recent_projects_list();
|
||||
|
||||
/**
|
||||
* @brief Close the open project without any user prompt
|
||||
*
|
||||
* Fallback for the close handler when no UI layer is present.
|
||||
*/
|
||||
bool close_open_project_without_prompt();
|
||||
|
||||
/**
|
||||
* @brief Currently active tool
|
||||
*/
|
||||
Tool::Item tool_;
|
||||
|
||||
/**
|
||||
* @brief Currently active addable object
|
||||
*/
|
||||
Tool::AddableObject addable_object_;
|
||||
|
||||
/**
|
||||
* @brief Currently selected transition
|
||||
*/
|
||||
QString selected_transition_;
|
||||
|
||||
/**
|
||||
* @brief Current snapping setting
|
||||
*/
|
||||
bool snapping_;
|
||||
|
||||
/**
|
||||
* @brief Internal timer for saving autorecovery files
|
||||
*/
|
||||
QTimer autorecovery_timer_;
|
||||
|
||||
/**
|
||||
* @brief Application-wide undo stack instance_
|
||||
*/
|
||||
UndoStack undo_stack_;
|
||||
|
||||
/**
|
||||
* @brief List of most recently opened/saved projects
|
||||
*/
|
||||
QStringList recent_projects_;
|
||||
|
||||
/**
|
||||
* @brief Parameters set up in main() determining how the program should run
|
||||
*/
|
||||
CoreParams core_params_;
|
||||
|
||||
/**
|
||||
* @brief Static singleton engine instance_
|
||||
*/
|
||||
static EngineCore *instance_;
|
||||
|
||||
/**
|
||||
* @brief Internal translator
|
||||
*/
|
||||
QTranslator *translator_;
|
||||
|
||||
/**
|
||||
* @brief List of projects that are unsaved but have autorecovery projects
|
||||
*/
|
||||
QVector<QUuid> autorecovered_projects_;
|
||||
|
||||
/**
|
||||
* @brief Do something debug related
|
||||
*/
|
||||
bool magic_;
|
||||
|
||||
/**
|
||||
* @brief How many widgets currently need pixel sampling access
|
||||
*/
|
||||
int pixel_sampling_users_;
|
||||
|
||||
bool shown_cache_full_warning_;
|
||||
|
||||
ConfirmImageSequenceHandler confirm_image_sequence_handler_;
|
||||
|
||||
FootageRelinkHandler relink_handler_;
|
||||
|
||||
SaveProjectHandler save_project_handler_;
|
||||
|
||||
CloseProjectHandler close_project_handler_;
|
||||
|
||||
LoadLayoutHandler load_layout_handler_;
|
||||
|
||||
#ifdef USE_OTIO
|
||||
OtioImportHandler otio_import_handler_;
|
||||
#endif
|
||||
|
||||
private slots:
|
||||
void save_autorecovery();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_COREENGINE_H
|
||||
Reference in New Issue
Block a user