The full pure-C facade used by the app: node/project/timeline/viewer/ undo/task/events/serializer/playback/preview/renderer/gizmo/color/ audio/footage/proxy/encoding/exporter/config/disk/ipc/plugin/worker families, plus undo-group semantics, display renderer handles, NodeFactory accessors, and per-family pure-C engine tests.
813 lines
19 KiB
C++
813 lines
19 KiB
C++
/***
|
|
|
|
Oak - Non-Linear Video Editor
|
|
Copyright (C) 2026 Oak Team
|
|
|
|
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/>.
|
|
|
|
***/
|
|
|
|
#include "oakengine/app.h"
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
|
|
#include <QCoreApplication>
|
|
#include <QGuiApplication>
|
|
#include <QString>
|
|
|
|
#include "coreengine.h"
|
|
#include "node/project.h"
|
|
#include "node/project/footage/footage.h"
|
|
#include "node/project/sequence/sequence.h"
|
|
#include "node/project/serializer/serializedlayoutinfo.h"
|
|
#include "task/task.h"
|
|
#include "undo/undostack.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
olive::Project *impl(OakEngineProject *h)
|
|
{
|
|
return reinterpret_cast<olive::Project *>(h);
|
|
}
|
|
|
|
OakEngineProject *wrap(olive::Project *p)
|
|
{
|
|
return reinterpret_cast<OakEngineProject *>(p);
|
|
}
|
|
|
|
OakEngineSequence *wrap_seq(olive::Sequence *s)
|
|
{
|
|
return reinterpret_cast<OakEngineSequence *>(s);
|
|
}
|
|
|
|
// buf/size convention: returns the would-be length excluding the NUL.
|
|
int string_to_buf(const QString &s, char *buf, int buf_size)
|
|
{
|
|
const QByteArray utf = s.toUtf8();
|
|
if (buf && buf_size > 0) {
|
|
snprintf(buf, size_t(buf_size), "%s", utf.constData());
|
|
}
|
|
return int(utf.size());
|
|
}
|
|
|
|
// Registered callback set (all fields may be null).
|
|
OakEngineAppCallbacks g_callbacks = {};
|
|
|
|
// Whether oakengine_app_start() has run (and oakengine_app_stop() has not).
|
|
bool g_started = false;
|
|
|
|
// The EngineCore the notification signals are currently connected to.
|
|
olive::EngineCore *g_connected_core = nullptr;
|
|
|
|
olive::EngineCore *app_core()
|
|
{
|
|
return olive::EngineCore::instance();
|
|
}
|
|
|
|
// The EngineCore constructor's UndoStack member creates QActions, which need
|
|
// QGuiApplication state (same reason as oakengine_init()).
|
|
void ensure_qcoreapplication()
|
|
{
|
|
if (QCoreApplication::instance()) {
|
|
return;
|
|
}
|
|
|
|
if (qEnvironmentVariableIsEmpty("QT_QPA_PLATFORM")) {
|
|
qputenv("QT_QPA_PLATFORM", "offscreen");
|
|
}
|
|
|
|
static int argc = 1;
|
|
static char app_name[] = "oakengine";
|
|
static char *argv[] = { app_name, nullptr };
|
|
new QGuiApplication(argc, argv);
|
|
|
|
QCoreApplication::setOrganizationName(QStringLiteral("oakvideoeditor.org"));
|
|
QCoreApplication::setApplicationName(QStringLiteral("Oak Video Editor"));
|
|
}
|
|
|
|
// Forward engine signals to the registered C callbacks. Connected once per
|
|
// EngineCore instance; dropped events are fine while no callback is set.
|
|
void connect_notifications(olive::EngineCore *core)
|
|
{
|
|
if (!core || g_connected_core == core) {
|
|
return;
|
|
}
|
|
g_connected_core = core;
|
|
|
|
QObject::connect(core, &olive::EngineCore::status_message_show, core,
|
|
[](const QString &message, int timeout) {
|
|
if (g_callbacks.status_message_show) {
|
|
g_callbacks.status_message_show(
|
|
message.toUtf8().constData(), timeout,
|
|
g_callbacks.userdata);
|
|
}
|
|
});
|
|
QObject::connect(core, &olive::EngineCore::status_message_clear, core,
|
|
[] {
|
|
if (g_callbacks.status_message_clear) {
|
|
g_callbacks.status_message_clear(
|
|
g_callbacks.userdata);
|
|
}
|
|
});
|
|
QObject::connect(core, &olive::EngineCore::cache_full_warning_requested,
|
|
core, [] {
|
|
if (g_callbacks.cache_full_warning) {
|
|
g_callbacks.cache_full_warning(
|
|
g_callbacks.userdata);
|
|
}
|
|
});
|
|
QObject::connect(core, &olive::EngineCore::active_project_changed, core,
|
|
[](olive::Project *p) {
|
|
if (g_callbacks.active_project_changed) {
|
|
g_callbacks.active_project_changed(
|
|
wrap(p), g_callbacks.userdata);
|
|
}
|
|
});
|
|
QObject::connect(core, &olive::EngineCore::tool_changed, core,
|
|
[](const olive::Tool::Item &tool) {
|
|
if (g_callbacks.tool_changed) {
|
|
g_callbacks.tool_changed(int(tool),
|
|
g_callbacks.userdata);
|
|
}
|
|
});
|
|
QObject::connect(core, &olive::EngineCore::addable_object_changed, core,
|
|
[](olive::Tool::AddableObject o) {
|
|
if (g_callbacks.addable_object_changed) {
|
|
g_callbacks.addable_object_changed(
|
|
int(o), g_callbacks.userdata);
|
|
}
|
|
});
|
|
QObject::connect(core, &olive::EngineCore::snapping_changed, core,
|
|
[](const bool &b) {
|
|
if (g_callbacks.snapping_changed) {
|
|
g_callbacks.snapping_changed(b ? 1 : 0,
|
|
g_callbacks.userdata);
|
|
}
|
|
});
|
|
QObject::connect(core, &olive::EngineCore::timecode_display_changed, core,
|
|
[](olive::core::Timecode::Display d) {
|
|
if (g_callbacks.timecode_display_changed) {
|
|
g_callbacks.timecode_display_changed(
|
|
int(d), g_callbacks.userdata);
|
|
}
|
|
});
|
|
QObject::connect(core, &olive::EngineCore::open_recent_list_changed, core,
|
|
[] {
|
|
if (g_callbacks.open_recent_list_changed) {
|
|
g_callbacks.open_recent_list_changed(
|
|
g_callbacks.userdata);
|
|
}
|
|
});
|
|
QObject::connect(core, &olive::EngineCore::color_picker_enabled, core,
|
|
[](bool e) {
|
|
if (g_callbacks.color_picker_enabled) {
|
|
g_callbacks.color_picker_enabled(
|
|
e ? 1 : 0, g_callbacks.userdata);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Translate the C handler callbacks into the std::function handlers
|
|
// EngineCore calls when it needs user interaction.
|
|
void install_handlers(olive::EngineCore *core)
|
|
{
|
|
if (g_callbacks.confirm_image_sequence) {
|
|
core->set_confirm_image_sequence_handler([](const QString &filename) {
|
|
return g_callbacks.confirm_image_sequence(
|
|
filename.toUtf8().constData(),
|
|
g_callbacks.userdata) != 0;
|
|
});
|
|
} else {
|
|
core->set_confirm_image_sequence_handler(nullptr);
|
|
}
|
|
|
|
if (g_callbacks.relink_footage) {
|
|
core->set_relink_handler([](QVector<olive::Footage *> footage) {
|
|
return g_callbacks.relink_footage(
|
|
reinterpret_cast<OakEngineFootage **>(footage.data()),
|
|
int(footage.size()), g_callbacks.userdata) != 0;
|
|
});
|
|
} else {
|
|
core->set_relink_handler(nullptr);
|
|
}
|
|
|
|
if (g_callbacks.save_project) {
|
|
core->set_save_project_handler([](const QString &override_filename) {
|
|
g_callbacks.save_project(override_filename.toUtf8().constData(),
|
|
g_callbacks.userdata);
|
|
});
|
|
} else {
|
|
core->set_save_project_handler(nullptr);
|
|
}
|
|
|
|
if (g_callbacks.close_project) {
|
|
core->set_close_project_handler([] {
|
|
return g_callbacks.close_project(g_callbacks.userdata) != 0;
|
|
});
|
|
} else {
|
|
core->set_close_project_handler(nullptr);
|
|
}
|
|
|
|
if (g_callbacks.load_layout) {
|
|
core->set_load_layout_handler(
|
|
[](const olive::SerializedLayoutInfo &layout) {
|
|
g_callbacks.load_layout(&layout, g_callbacks.userdata);
|
|
});
|
|
} else {
|
|
core->set_load_layout_handler(nullptr);
|
|
}
|
|
|
|
#ifdef USE_OTIO
|
|
if (g_callbacks.otio_import) {
|
|
core->set_otio_import_handler(
|
|
[](const QList<olive::Sequence *> &sequences) {
|
|
QVector<OakEngineSequence *> handles;
|
|
handles.reserve(sequences.size());
|
|
for (olive::Sequence *s : sequences) {
|
|
handles.append(wrap_seq(s));
|
|
}
|
|
return g_callbacks.otio_import(handles.data(),
|
|
int(handles.size()),
|
|
g_callbacks.userdata) != 0;
|
|
});
|
|
} else {
|
|
core->set_otio_import_handler(nullptr);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
} // namespace
|
|
|
|
extern "C"
|
|
{
|
|
|
|
int oakengine_app_create(const OakEngineAppParams *params)
|
|
{
|
|
if (app_core()) {
|
|
return OAKENGINE_E_STATE;
|
|
}
|
|
|
|
ensure_qcoreapplication();
|
|
|
|
olive::EngineCore::CoreParams core_params;
|
|
if (params) {
|
|
switch (params->run_mode) {
|
|
case OAKENGINE_APP_RUN_HEADLESS_EXPORT:
|
|
core_params.set_run_mode(
|
|
olive::EngineCore::CoreParams::k_headless_export);
|
|
break;
|
|
case OAKENGINE_APP_RUN_HEADLESS_PRE_CACHE:
|
|
core_params.set_run_mode(
|
|
olive::EngineCore::CoreParams::k_headless_pre_cache);
|
|
break;
|
|
default:
|
|
core_params.set_run_mode(
|
|
olive::EngineCore::CoreParams::k_run_normal);
|
|
break;
|
|
}
|
|
core_params.set_fullscreen(params->fullscreen != 0);
|
|
if (params->startup_project) {
|
|
core_params.set_startup_project(
|
|
QString::fromUtf8(params->startup_project));
|
|
}
|
|
if (params->startup_language) {
|
|
core_params.set_startup_language(
|
|
QString::fromUtf8(params->startup_language));
|
|
}
|
|
if (params->crash_on_startup) {
|
|
core_params.set_crash_on_startup(true);
|
|
}
|
|
}
|
|
|
|
// Never deleted: backs the process-wide EngineCore singleton (same
|
|
// lifetime rule as the oakengine_init() shell).
|
|
new olive::EngineCore(core_params);
|
|
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_start(void)
|
|
{
|
|
if (!app_core() || g_started) {
|
|
return OAKENGINE_E_STATE;
|
|
}
|
|
|
|
app_core()->start();
|
|
g_started = true;
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_stop(void)
|
|
{
|
|
if (!app_core() || !g_started) {
|
|
return OAKENGINE_E_STATE;
|
|
}
|
|
|
|
app_core()->stop();
|
|
g_started = false;
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_set_callbacks(const OakEngineAppCallbacks *callbacks)
|
|
{
|
|
if (callbacks) {
|
|
g_callbacks = *callbacks;
|
|
} else {
|
|
g_callbacks = OakEngineAppCallbacks{};
|
|
}
|
|
|
|
if (olive::EngineCore *core = app_core()) {
|
|
connect_notifications(core);
|
|
install_handlers(core);
|
|
}
|
|
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_run_mode(void)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
switch (app_core()->core_params().run_mode()) {
|
|
case olive::EngineCore::CoreParams::k_headless_export:
|
|
return OAKENGINE_APP_RUN_HEADLESS_EXPORT;
|
|
case olive::EngineCore::CoreParams::k_headless_pre_cache:
|
|
return OAKENGINE_APP_RUN_HEADLESS_PRE_CACHE;
|
|
default:
|
|
return OAKENGINE_APP_RUN_NORMAL;
|
|
}
|
|
}
|
|
|
|
int oakengine_app_fullscreen(void)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
return app_core()->core_params().fullscreen() ? 1 : 0;
|
|
}
|
|
|
|
int oakengine_app_startup_project(char *buf, int buf_size)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
return string_to_buf(app_core()->core_params().startup_project(), buf,
|
|
buf_size);
|
|
}
|
|
|
|
void *oakengine_app_undo_stack(void)
|
|
{
|
|
if (!app_core()) {
|
|
return nullptr;
|
|
}
|
|
return app_core()->undo_stack();
|
|
}
|
|
|
|
int oakengine_app_tool(void)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
return int(app_core()->tool());
|
|
}
|
|
|
|
int oakengine_app_set_tool(int tool)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
if (tool < 0 || tool >= int(olive::Tool::k_count)) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
app_core()->set_tool(static_cast<olive::Tool::Item>(tool));
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_addable_object(void)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
return int(app_core()->get_selected_addable_object());
|
|
}
|
|
|
|
int oakengine_app_set_addable_object(int object)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
if (object < 0 || object >= int(olive::Tool::k_addable_count)) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
app_core()->set_selected_addable_object(
|
|
static_cast<olive::Tool::AddableObject>(object));
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_selected_transition(char *buf, int buf_size)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
return string_to_buf(app_core()->get_selected_transition(), buf, buf_size);
|
|
}
|
|
|
|
int oakengine_app_set_selected_transition(const char *id)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
app_core()->set_selected_transition_object(
|
|
id ? QString::fromUtf8(id) : QString());
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_snapping(void)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
return app_core()->snapping() ? 1 : 0;
|
|
}
|
|
|
|
int oakengine_app_set_snapping(int enabled)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
app_core()->set_snapping(enabled != 0);
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_timecode_display(void)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
return int(app_core()->get_timecode_display());
|
|
}
|
|
|
|
int oakengine_app_set_timecode_display(int display)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
if (display < 0 ||
|
|
display > int(olive::core::Timecode::k_milliseconds)) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
app_core()->set_timecode_display(
|
|
static_cast<olive::core::Timecode::Display>(display));
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_recent_projects_count(void)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
return int(app_core()->get_recent_projects().size());
|
|
}
|
|
|
|
int oakengine_app_recent_project_at(int index, char *buf, int buf_size)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
const QStringList &recent = app_core()->get_recent_projects();
|
|
if (index < 0 || index >= recent.size()) {
|
|
return OAKENGINE_E_NOT_FOUND;
|
|
}
|
|
return string_to_buf(recent.at(index), buf, buf_size);
|
|
}
|
|
|
|
int oakengine_app_remove_recent_project(int index)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
if (index < 0 || index >= app_core()->get_recent_projects().size()) {
|
|
return OAKENGINE_E_NOT_FOUND;
|
|
}
|
|
|
|
app_core()->remove_recently_opened_project(index);
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_clear_recent_projects(void)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
app_core()->clear_open_recent_list();
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_show_status_message(const char *message, int timeout)
|
|
{
|
|
if (!app_core() || !message) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
app_core()->show_status_bar_message(QString::fromUtf8(message), timeout);
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_clear_status_message(void)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
app_core()->clear_status_bar_message();
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_set_language(const char *locale)
|
|
{
|
|
if (!app_core() || !locale) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
return app_core()->set_language(QString::fromUtf8(locale)) ? 1 : 0;
|
|
}
|
|
|
|
int oakengine_app_set_autorecovery_interval(int minutes)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
app_core()->set_autorecovery_interval(minutes);
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_set_use_proxy_media(int enabled)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
app_core()->set_use_proxy_media(enabled != 0);
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_request_pixel_sampling(int enable)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
app_core()->request_pixel_sampling_in_viewers(enable != 0);
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_set_magic(int enabled)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
app_core()->set_magic(enabled != 0);
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_is_magic_enabled(void)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
return app_core()->is_magic_enabled() ? 1 : 0;
|
|
}
|
|
|
|
int oakengine_app_copy_to_clipboard(const char *text)
|
|
{
|
|
if (!app_core() || !text) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
olive::EngineCore::copy_string_to_clipboard(QString::fromUtf8(text));
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_paste_from_clipboard(char *buf, int buf_size)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
return string_to_buf(olive::EngineCore::paste_string_from_clipboard(), buf,
|
|
buf_size);
|
|
}
|
|
|
|
int oakengine_app_footage_file_dialog_filter(char *buf, int buf_size)
|
|
{
|
|
return string_to_buf(olive::EngineCore::footage_file_dialog_filter(), buf,
|
|
buf_size);
|
|
}
|
|
|
|
int oakengine_app_is_footage_extension_allowed(const char *path)
|
|
{
|
|
if (!path) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
return olive::EngineCore::is_footage_extension_allowed(
|
|
QString::fromUtf8(path)) ?
|
|
1 :
|
|
0;
|
|
}
|
|
|
|
OakEngineSequence *oakengine_app_create_sequence(OakEngineProject *project,
|
|
const char *name_format)
|
|
{
|
|
if (!app_core() || !project) {
|
|
return nullptr;
|
|
}
|
|
|
|
const QString format = name_format ?
|
|
QString::fromUtf8(name_format) :
|
|
QStringLiteral("Sequence %1");
|
|
return wrap_seq(olive::EngineCore::create_new_sequence_for_project(
|
|
format, impl(project)));
|
|
}
|
|
|
|
int oakengine_app_auto_recovery_index_filename(char *buf, int buf_size)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
return string_to_buf(olive::EngineCore::get_auto_recovery_index_filename(),
|
|
buf, buf_size);
|
|
}
|
|
|
|
OakEngineProject *oakengine_app_open_project(void)
|
|
{
|
|
if (!app_core()) {
|
|
return nullptr;
|
|
}
|
|
return wrap(app_core()->open_project());
|
|
}
|
|
|
|
int oakengine_app_create_new_project(void)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
app_core()->create_new_project();
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_add_open_project(OakEngineProject *project,
|
|
int add_to_recents)
|
|
{
|
|
if (!app_core() || !project) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
app_core()->add_open_project(impl(project), add_to_recents != 0);
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_add_open_project_from_task(void *task, int add_to_recents)
|
|
{
|
|
if (!app_core() || !task) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
return app_core()->add_open_project_from_task(
|
|
static_cast<olive::Task *>(task), add_to_recents != 0) ?
|
|
1 :
|
|
0;
|
|
}
|
|
|
|
int oakengine_app_add_recovery_project_from_task(void *task)
|
|
{
|
|
if (!app_core() || !task) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
app_core()->add_recovery_project_from_task(static_cast<olive::Task *>(task));
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_on_project_saved(OakEngineProject *project)
|
|
{
|
|
if (!app_core() || !project) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
app_core()->on_project_saved(impl(project));
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
int oakengine_app_set_active_project(OakEngineProject *project)
|
|
{
|
|
if (!app_core()) {
|
|
return OAKENGINE_E_INVALID;
|
|
}
|
|
|
|
app_core()->set_active_project(impl(project));
|
|
return OAKENGINE_OK;
|
|
}
|
|
|
|
// ---- Individual handler setter convenience wrappers ----
|
|
|
|
int oakengine_app_set_confirm_image_sequence_handler(
|
|
int (*fn)(const char *filename, void *userdata), void *userdata)
|
|
{
|
|
OakEngineAppCallbacks cb = g_callbacks;
|
|
cb.userdata = userdata;
|
|
cb.confirm_image_sequence = fn;
|
|
return oakengine_app_set_callbacks(&cb);
|
|
}
|
|
|
|
int oakengine_app_set_relink_handler(
|
|
int (*fn)(OakEngineFootage **footage, int count, void *userdata),
|
|
void *userdata)
|
|
{
|
|
OakEngineAppCallbacks cb = g_callbacks;
|
|
cb.userdata = userdata;
|
|
cb.relink_footage = fn;
|
|
return oakengine_app_set_callbacks(&cb);
|
|
}
|
|
|
|
int oakengine_app_set_save_project_handler(
|
|
void (*fn)(const char *override_filename, void *userdata), void *userdata)
|
|
{
|
|
OakEngineAppCallbacks cb = g_callbacks;
|
|
cb.userdata = userdata;
|
|
cb.save_project = fn;
|
|
return oakengine_app_set_callbacks(&cb);
|
|
}
|
|
|
|
int oakengine_app_set_close_project_handler(
|
|
int (*fn)(void *userdata), void *userdata)
|
|
{
|
|
OakEngineAppCallbacks cb = g_callbacks;
|
|
cb.userdata = userdata;
|
|
cb.close_project = fn;
|
|
return oakengine_app_set_callbacks(&cb);
|
|
}
|
|
|
|
int oakengine_app_set_load_layout_handler(
|
|
void (*fn)(const void *layout, void *userdata), void *userdata)
|
|
{
|
|
OakEngineAppCallbacks cb = g_callbacks;
|
|
cb.userdata = userdata;
|
|
cb.load_layout = fn;
|
|
return oakengine_app_set_callbacks(&cb);
|
|
}
|
|
|
|
// ---- void*-based convenience overloads ----
|
|
|
|
int oakengine_app_get_auto_recovery_index_filename(char *buf, int buf_size)
|
|
{
|
|
return oakengine_app_auto_recovery_index_filename(buf, buf_size);
|
|
}
|
|
|
|
int oakengine_app_remove_recently_opened_project(int index)
|
|
{
|
|
return oakengine_app_remove_recent_project(index);
|
|
}
|
|
|
|
int oakengine_app_on_project_saved_vp(void *project)
|
|
{
|
|
return oakengine_app_on_project_saved(
|
|
reinterpret_cast<OakEngineProject *>(project));
|
|
}
|
|
|
|
int oakengine_app_set_active_project_vp(void *project)
|
|
{
|
|
return oakengine_app_set_active_project(
|
|
reinterpret_cast<OakEngineProject *>(project));
|
|
}
|
|
|
|
int oakengine_app_add_open_project_vp(void *project, int add_to_recents)
|
|
{
|
|
return oakengine_app_add_open_project(
|
|
reinterpret_cast<OakEngineProject *>(project), add_to_recents);
|
|
}
|
|
|
|
} // extern "C"
|