app: migrate all engine access to the C ABI facade (nm U _ZN5olive = 0)

Every app module now reaches liboakengine exclusively through
oakengine_* C calls, EngineEventBridge subscriptions and app-side
handle headers (cliphandle/keyframehandle/nodevaluehandle/oakvaluehelper).
Direct C++ command construction, engine signal connect()s, and engine
type usage in MOC-visible signatures are gone: 557 -> 0 undefined
olive:: symbols in oak-editor.
This commit is contained in:
2026-07-26 22:43:21 +08:00
parent f95590e924
commit 0aa5879f35
256 changed files with 12357 additions and 4044 deletions
+14 -5
View File
@@ -27,6 +27,8 @@
#include <QTimer>
#include "widget/timebased/timebasedwidget.h"
#include "oakengine/events.h"
#include "oakengine/viewer.h"
namespace olive
{
@@ -156,15 +158,20 @@ void TimeBasedView::set_y_scale(const double &y_scale)
void TimeBasedView::set_viewer_node(ViewerOutput *v)
{
if (viewer_) {
disconnect(viewer_, &ViewerOutput::playhead_changed, viewport(),
static_cast<void (QWidget::*)()>(&TimeBasedView::update));
oakengine_event_unsubscribe(viewer_sub_);
viewer_sub_ = 0;
}
viewer_ = v;
if (viewer_) {
connect(viewer_, &ViewerOutput::playhead_changed, viewport(),
static_cast<void (QWidget::*)()>(&TimeBasedView::update));
viewer_sub_ = oakengine_event_subscribe(
reinterpret_cast<OakEngineNode *>(viewer_),
OAKENGINE_EVENT_VIEWER_PLAYHEAD_CHANGED,
[](const oakengine_event *, void *userdata) {
static_cast<TimeBasedView *>(userdata)->viewport()->update();
},
this);
}
}
@@ -247,7 +254,9 @@ bool TimeBasedView::playhead_move(QMouseEvent *event)
mouse_time += movement;
}
viewer_->set_playhead(mouse_time);
oakengine_viewer_set_playhead(
reinterpret_cast<OakEngineNode *>(viewer_),
mouse_time.numerator(), mouse_time.denominator());
}
return true;
+3
View File
@@ -23,6 +23,7 @@
#define OAK_TIMELINEVIEWBASE_H
#include <QGraphicsView>
#include <cstdint>
#include <vector>
#include "core.h"
@@ -146,6 +147,8 @@ private:
double y_scale_;
ViewerOutput *viewer_;
int64_t viewer_sub_ = 0;
};
}
@@ -28,8 +28,13 @@
#include <QToolTip>
#include "common/qtutils.h"
#include "olive/core/util/timecodefunctions.h"
#include "oakengine/node.h"
#include "oakengine/undo.h"
#include "timebasedview.h"
#include "timebasedwidget.h"
#include "widget/keyframeview/keyframehandle.h"
#include "widget/timeruler/markerhandle.h"
#include "widget/timetarget/timetarget.h"
namespace olive
@@ -311,7 +316,13 @@ public:
// Apply movement
for (size_t i = 0; i < selected_.size(); i++) {
selected_.at(i)->set_time(dragging_.at(i) + time_diff);
if constexpr (std::is_same_v<T, NodeKeyframe>) {
key_set_time_live(selected_.at(i),
dragging_.at(i) + time_diff);
} else {
selection_set_time(selected_.at(i),
dragging_.at(i) + time_diff);
}
}
// Show information about this keyframe
@@ -336,19 +347,32 @@ public:
QToolTip::showText(QCursor::pos(), tip);
}
void drag_stop(MultiUndoCommand *command)
void drag_stop(void *command)
{
QToolTip::hideText();
for (size_t i = 0; i < selected_.size(); i++) {
Rational current;
if constexpr (std::is_same_v<T, TimelineMarker>) {
current = selected_.at(i)->time().in();
} else {
current = selected_.at(i)->time();
if constexpr (std::is_same_v<T, NodeKeyframe>) {
int tbn = 0, tbd = 0;
oakengine_node_frame_time_base(
reinterpret_cast<OakEngineNode *>(selected_.at(i)->parent()),
&tbn, &tbd);
const int64_t new_ts = olive::core::Timecode::time_to_timestamp(
dragging_.at(i), olive::Rational(tbn, tbd),
olive::core::Timecode::k_round);
oakengine_undo_command_multi_add_child(
command,
oakengine_keyframe_set_time_command(
reinterpret_cast<OakEngineKeyframe *>(selected_.at(i)),
new_ts));
} else if constexpr (std::is_same_v<T, TimelineMarker>) {
oakengine_undo_command_multi_add_child(
command,
oakengine_marker_set_time_command(
reinterpret_cast<OakEngineMarker *>(selected_.at(i)),
dragging_.at(i).numerator(),
dragging_.at(i).denominator()));
}
command->add_child(
new SetTimeCommand(selected_.at(i), current, dragging_.at(i)));
}
dragging_.clear();
@@ -419,47 +443,7 @@ public:
}
private:
class SetTimeCommand : public UndoCommand {
public:
SetTimeCommand(T *key, const Rational &time)
{
key_ = key;
new_time_ = time;
old_time_ = key_->time();
}
SetTimeCommand(T *key, const Rational &new_time,
const Rational &old_time)
{
key_ = key;
new_time_ = new_time;
old_time_ = old_time;
}
virtual Project *get_relevant_project() const override
{
return Project::get_project_from_object(key_);
}
protected:
virtual void redo() override
{
key_->set_time(new_time_);
}
virtual void undo() override
{
key_->set_time(old_time_);
}
private:
T *key_;
Rational old_time_;
Rational new_time_;
};
TimeBasedView *view_;
TimeBasedView *view_;
using DrawnObject = QPair<T *, QRectF>;
std::vector<DrawnObject> drawn_objects_;
+146 -70
View File
@@ -25,15 +25,20 @@
#include "common/autoscroll.h"
#include "common/range.h"
#include "config/config.h"
#include "common/configwrapper.h"
#include "core.h"
#include "engineeventbridge.h"
#include "common/current.h"
#include "dialog/markerproperties/markerpropertiesdialog.h"
#include "node/project/sequence/sequence.h"
#include "oakengine/timeline.h"
#include "timeline/timelineundoworkarea.h"
#include "oakengine/viewer.h"
#include "oakengine/timeline.h"
#include "oakengine/undo.h"
#include "widget/timeruler/timeruler.h"
#include "widget/timelinewidget/cliphandle.h"
#include "widget/viewer/vieweroutpututils.h"
namespace olive
{
@@ -42,6 +47,7 @@ TimeBasedWidget::TimeBasedWidget(bool ruler_text_visible,
QWidget *parent)
: TimelineScaledWidget(parent)
, viewer_node_(nullptr)
, bridge_(new EngineEventBridge(this))
, auto_max_scrollbar_(false)
, toggle_show_all_(false)
, auto_set_timebase_(true)
@@ -95,33 +101,28 @@ void TimeBasedWidget::connect_viewer_node(ViewerOutput *node)
// Set viewer node
ViewerOutput *old = viewer_node_.data();
viewer_node_ = node;
// Disconnect old bridge subscriptions and connections
disconnect(bridge_, nullptr, this, nullptr);
bridge_->unsubscribe_all();
if (viewer_node_) {
oak_video_params vp;
oakengine_viewer_get_video_params(
reinterpret_cast<OakEngineNode *>(viewer_node_.data()), 0, &vp);
// We still need Current class - keep using it for now
Current::getInstance().setCurrentVideoParams(
viewer_node_->get_video_params());
viewer_output_video_params(viewer_node_));
Current::getInstance().setCurrentAudioParams(
viewer_node_->get_audio_params());
viewer_output_audio_params(viewer_node_));
} else {
Current::getInstance().setCurrentVideoParams(VideoParams());
Current::getInstance().setCurrentVideoParams(empty_video_params());
Current::getInstance().setCurrentAudioParams(AudioParams());
}
if (old) {
// Call potential derivative functions for disconnecting the viewer node
DisconnectNodeEvent(old);
// Disconnect length changed signal
disconnect(old, &ViewerOutput::length_changed, this,
&TimeBasedWidget::update_maximum_scroll);
disconnect(old, &ViewerOutput::removed_from_graph, this,
&TimeBasedWidget::connected_node_removed_from_graph);
disconnect(old, &ViewerOutput::playhead_changed, this,
&TimeBasedWidget::playhead_time_changed);
// Disconnect rate change signals if they were connected
disconnect(old, &ViewerOutput::frame_rate_changed, this,
&TimeBasedWidget::auto_update_timebase);
disconnect(old, &ViewerOutput::sample_rate_changed, this,
&TimeBasedWidget::auto_update_timebase);
// Reset timebase to null
SetTimebase(Rational());
@@ -137,13 +138,28 @@ void TimeBasedWidget::connect_viewer_node(ViewerOutput *node)
ConnectedNodeChangeEvent(viewer_node_.data());
if (viewer_node_) {
// Connect length changed signal
connect(viewer_node_.data(), &ViewerOutput::length_changed, this,
&TimeBasedWidget::update_maximum_scroll);
connect(viewer_node_.data(), &ViewerOutput::removed_from_graph, this,
&TimeBasedWidget::connected_node_removed_from_graph);
connect(viewer_node_.data(), &ViewerOutput::playhead_changed, this,
&TimeBasedWidget::playhead_time_changed);
OakEngineNode *handle =
reinterpret_cast<OakEngineNode *>(viewer_node_.data());
// Subscribe to viewer events via bridge
bridge_->subscribe(handle, OAKENGINE_EVENT_VIEWER_LENGTH_CHANGED);
bridge_->subscribe(handle, OAKENGINE_EVENT_VIEWER_PLAYHEAD_CHANGED);
bridge_->subscribe(handle, OAKENGINE_EVENT_NODE_REMOVED_FROM_GRAPH);
connect(bridge_, &EngineEventBridge::viewer_length_changed, this,
[this](OakEngineNode *, qint64, qint64) {
update_maximum_scroll();
});
connect(bridge_, &EngineEventBridge::viewer_playhead_changed, this,
[this](OakEngineNode *, qint64 num, qint64 den) {
playhead_time_changed(Rational(num, den));
});
// Node removed from graph - use the bridge signal
connect(bridge_, &EngineEventBridge::node_removed_from_graph, this,
[this](OakEngineNode *, OakEngineNode *) {
connected_node_removed_from_graph();
});
// Connect ruler and scrollbar to timeline points
connect_work_area(viewer_node_->get_work_area());
@@ -152,10 +168,16 @@ void TimeBasedWidget::connect_viewer_node(ViewerOutput *node)
// If we're setting the timebase, set it automatically based on the video and audio parameters
if (auto_set_timebase_) {
auto_update_timebase();
connect(viewer_node_.data(), &ViewerOutput::frame_rate_changed, this,
&TimeBasedWidget::auto_update_timebase);
connect(viewer_node_.data(), &ViewerOutput::sample_rate_changed, this,
&TimeBasedWidget::auto_update_timebase);
bridge_->subscribe(handle, OAKENGINE_EVENT_VIEWER_FRAME_RATE_CHANGED);
bridge_->subscribe(handle, OAKENGINE_EVENT_VIEWER_SAMPLE_RATE_CHANGED);
connect(bridge_, &EngineEventBridge::viewer_frame_rate_changed, this,
[this](OakEngineNode *, qint64, qint64) {
auto_update_timebase();
});
connect(bridge_, &EngineEventBridge::viewer_sample_rate_changed, this,
[this](OakEngineNode *, int) {
auto_update_timebase();
});
}
// Call derivatives
@@ -164,7 +186,8 @@ void TimeBasedWidget::connect_viewer_node(ViewerOutput *node)
update_maximum_scroll();
emit connected_node_changed(old, node);
emit connected_node_changed(reinterpret_cast<OakEngineNode *>(old),
reinterpret_cast<OakEngineNode *>(node));
}
void TimeBasedWidget::connect_work_area(TimelineWorkArea *workarea)
@@ -287,13 +310,13 @@ void TimeBasedWidget::auto_update_timebase()
return;
}
Rational video_tb =
viewer_node_->get_video_params().frame_rate_as_time_base();
viewer_output_video_params(viewer_node_).frame_rate_as_time_base();
if (!video_tb.isNull()) {
SetTimebase(video_tb);
} else {
Rational audio_tb =
viewer_node_->get_audio_params().sample_rate_as_time_base();
viewer_output_audio_params(viewer_node_).sample_rate_as_time_base();
if (!audio_tb.isNull()) {
SetTimebase(audio_tb);
@@ -489,7 +512,9 @@ void TimeBasedWidget::go_to_prev_cut()
closest_cut = qMax(closest_cut, this_track_closest_cut);
}
get_connected_node()->set_playhead(closest_cut);
oakengine_viewer_set_playhead(
reinterpret_cast<OakEngineNode *>(get_connected_node()),
closest_cut.numerator(), closest_cut.denominator());
}
void TimeBasedWidget::go_to_next_cut()
@@ -521,14 +546,17 @@ void TimeBasedWidget::go_to_next_cut()
}
if (closest_cut < RATIONAL_MAX) {
get_connected_node()->set_playhead(closest_cut);
oakengine_viewer_set_playhead(
reinterpret_cast<OakEngineNode *>(get_connected_node()),
closest_cut.numerator(), closest_cut.denominator());
}
}
void TimeBasedWidget::go_to_start()
{
if (viewer_node_) {
viewer_node_->set_playhead(0);
oakengine_viewer_set_playhead(
reinterpret_cast<OakEngineNode *>(viewer_node_.data()), 0, 1);
}
}
@@ -542,7 +570,12 @@ void TimeBasedWidget::prev_frame()
// Catch rounding error, assume this time is snapped and just subtract a timebase
proposed_time -= timebase();
}
viewer_node_->set_playhead(qMax(Rational(0), proposed_time));
{
Rational _pt = qMax(Rational(0), proposed_time);
oakengine_viewer_set_playhead(
reinterpret_cast<OakEngineNode *>(viewer_node_.data()),
_pt.numerator(), _pt.denominator());
}
}
}
@@ -556,14 +589,19 @@ void TimeBasedWidget::next_frame()
// Catch rounding error, assume this time is snapped and just add a timebase
proposed_time += timebase();
}
viewer_node_->set_playhead(proposed_time);
oakengine_viewer_set_playhead(
reinterpret_cast<OakEngineNode *>(viewer_node_.data()),
proposed_time.numerator(), proposed_time.denominator());
}
}
void TimeBasedWidget::go_to_end()
{
if (viewer_node_) {
viewer_node_->set_playhead(viewer_node_->get_length());
oakengine_viewer_set_playhead(
reinterpret_cast<OakEngineNode *>(viewer_node_.data()),
viewer_node_->get_length().numerator(),
viewer_node_->get_length().denominator());
}
}
@@ -587,13 +625,13 @@ void TimeBasedWidget::set_point(Timeline::MovementMode m, const Rational &time)
return;
}
MultiUndoCommand *command = new MultiUndoCommand();
void *command = oakengine_undo_command_create_multi();
TimelineWorkArea *points = viewer_node_->get_work_area();
// Enable workarea if it isn't already enabled
if (!points->enabled()) {
command->add_child(new WorkareaSetEnabledCommand(
viewer_node_->project(), points, true));
oakengine_workarea_set_enabled_undoable(
reinterpret_cast<OakEngineWorkarea *>(points), 1, command);
}
// Determine our new range
@@ -603,7 +641,7 @@ void TimeBasedWidget::set_point(Timeline::MovementMode m, const Rational &time)
in_point = time;
if (!points->enabled() || points->out() < in_point) {
out_point = TimelineWorkArea::k_reset_out;
out_point = RATIONAL_MAX;
} else {
out_point = points->out();
}
@@ -611,17 +649,28 @@ void TimeBasedWidget::set_point(Timeline::MovementMode m, const Rational &time)
out_point = time;
if (!points->enabled() || points->in() > out_point) {
in_point = TimelineWorkArea::k_reset_in;
in_point = Rational(0, 1);
} else {
in_point = points->in();
}
}
// Set workarea
command->add_child(
new WorkareaSetRangeCommand(points, TimeRange(in_point, out_point)));
{
int64_t old_in_num, old_in_den, old_out_num, old_out_den;
int old_enabled;
oakengine_workarea_get(
reinterpret_cast<OakEngineWorkarea *>(points),
&old_in_num, &old_in_den, &old_out_num, &old_out_den,
&old_enabled);
oakengine_workarea_set_range_undoable(
reinterpret_cast<OakEngineWorkarea *>(points),
in_point.numerator(), in_point.denominator(),
out_point.numerator(), out_point.denominator(),
old_in_num, old_in_den, old_out_num, old_out_den, command);
}
Core::instance()->undo_stack()->push(command, tr("Set In/Out Point"));
oakengine_undo_push(command, tr("Set In/Out Point").toUtf8().constData());
}
void TimeBasedWidget::reset_point(Timeline::MovementMode m)
@@ -639,13 +688,27 @@ void TimeBasedWidget::reset_point(Timeline::MovementMode m)
TimeRange r = points->range();
if (m == Timeline::k_trim_in) {
r.set_in(TimelineWorkArea::k_reset_in);
r.set_in(Rational(0, 1));
} else {
r.set_out(TimelineWorkArea::k_reset_out);
r.set_out(RATIONAL_MAX);
}
Core::instance()->undo_stack()->push(new WorkareaSetRangeCommand(points, r),
tr("Reset In/Out Points"));
{
auto reset_cmd = oakengine_undo_command_create_multi();
int64_t old_in_num, old_in_den, old_out_num, old_out_den;
int old_enabled;
oakengine_workarea_get(
reinterpret_cast<OakEngineWorkarea *>(points),
&old_in_num, &old_in_den, &old_out_num, &old_out_den,
&old_enabled);
oakengine_workarea_set_range_undoable(
reinterpret_cast<OakEngineWorkarea *>(points),
r.in().numerator(), r.in().denominator(),
r.out().numerator(), r.out().denominator(),
old_in_num, old_in_den, old_out_num, old_out_den, reset_cmd);
oakengine_undo_push(reset_cmd,
tr("Reset In/Out Points").toUtf8().constData());
}
}
void TimeBasedWidget::page_scroll_internal(QScrollBar *bar, int maximum,
@@ -717,10 +780,15 @@ void TimeBasedWidget::clear_in_out_points()
return;
}
Core::instance()->undo_stack()->push(
new WorkareaSetEnabledCommand(get_connected_node()->project(),
get_connected_node()->get_work_area(), false),
tr("Cleared In/Out Points"));
{
auto clear_cmd = oakengine_undo_command_create_multi();
oakengine_workarea_set_enabled_undoable(
reinterpret_cast<OakEngineWorkarea *>(
get_connected_node()->get_work_area()),
0, clear_cmd);
oakengine_undo_push(clear_cmd,
tr("Cleared In/Out Points").toUtf8().constData());
}
}
void TimeBasedWidget::set_marker()
@@ -748,16 +816,20 @@ void TimeBasedWidget::set_marker()
color = OAK_CONFIG("MarkerColor").toInt();
}
TimelineMarker *marker = new TimelineMarker(
color, TimeRange(get_connected_node()->get_playhead(),
get_connected_node()->get_playhead()));
const Rational playhead = get_connected_node()->get_playhead();
OakEngineMarker *marker = oakengine_marker_create(
color, playhead.numerator(), playhead.denominator(),
playhead.numerator(), playhead.denominator(), "");
TimelineMarker *cpp_marker =
reinterpret_cast<TimelineMarker *>(marker);
bool edited_in_dialog = false;
if (OAK_CONFIG("SetNameWithMarker").toBool()) {
MarkerPropertiesDialog mpd({ marker }, timebase(), this);
MarkerPropertiesDialog mpd({ cpp_marker }, timebase(), this);
if (mpd.exec() != QDialog::Accepted) {
delete marker;
oakengine_marker_free(marker);
marker = nullptr;
cpp_marker = nullptr;
} else {
edited_in_dialog = true;
}
@@ -767,19 +839,19 @@ void TimeBasedWidget::set_marker()
if (edited_in_dialog) {
// The dialog pushed undo commands referencing this exact
// marker object, so it must be the one added to the list.
Core::instance()->undo_stack()->push(
new MarkerAddCommand(markers, marker), tr("Added Marker"));
oakengine_marker_list_add_existing(
reinterpret_cast<OakEngineMarkerList *>(markers), marker);
} else {
// Pristine marker: add through the liboakengine C ABI
// facade (one undoable command) and drop the temporary.
oakengine_sequence_marker_add_ex(
reinterpret_cast<OakEngineSequence *>(
get_connected_node()),
Timecode::time_to_timestamp(marker->time().in(),
Timecode::time_to_timestamp(cpp_marker->time().in(),
timebase(),
Timecode::k_round),
"", marker->color());
delete marker;
"", cpp_marker->color());
oakengine_marker_free(marker);
}
}
}
@@ -820,8 +892,10 @@ void TimeBasedWidget::go_to_in()
{
if (get_connected_node()) {
if (get_connected_node()->get_work_area()->enabled()) {
get_connected_node()->set_playhead(
get_connected_node()->get_work_area()->in());
oakengine_viewer_set_playhead(
reinterpret_cast<OakEngineNode *>(get_connected_node()),
get_connected_node()->get_work_area()->in().numerator(),
get_connected_node()->get_work_area()->in().denominator());
} else {
go_to_start();
}
@@ -832,8 +906,10 @@ void TimeBasedWidget::go_to_out()
{
if (get_connected_node()) {
if (get_connected_node()->get_work_area()->enabled()) {
get_connected_node()->set_playhead(
get_connected_node()->get_work_area()->out());
oakengine_viewer_set_playhead(
reinterpret_cast<OakEngineNode *>(get_connected_node()),
get_connected_node()->get_work_area()->out().numerator(),
get_connected_node()->get_work_area()->out().denominator());
} else {
go_to_end();
}
@@ -913,7 +989,7 @@ bool TimeBasedWidget::snap_point(const std::vector<Rational> &start_times,
TimelineMarker *marker = *jt;
TimeRange marker_range =
marker->time() + clip->in() - clip->media_in();
marker->time() + clip->in() - clip_media_in(clip);
qreal marker_in_screen =
time_to_scene(marker_range.in());
+4 -1
View File
@@ -36,6 +36,7 @@
namespace olive
{
class EngineEventBridge;
class TimeRuler;
class TimeBasedWidget : public TimelineScaledWidget {
@@ -149,6 +150,8 @@ protected:
{
}
EngineEventBridge *bridge_ = nullptr;
virtual void ConnectNodeEvent(ViewerOutput *)
{
}
@@ -213,7 +216,7 @@ protected slots:
signals:
void timebase_changed(const Rational &);
void connected_node_changed(ViewerOutput *old, ViewerOutput *now);
void connected_node_changed(OakEngineNode *old, OakEngineNode *now);
protected slots:
virtual void SendCatchUpScrollEvent();
+2 -1
View File
@@ -26,6 +26,7 @@
#include "audio/audiovisualwaveform.h"
#include "oakengine/node.h"
namespace olive
{
@@ -34,7 +35,7 @@ const int TimeScaledObject::k_calculate_dimensions_padding = 10;
TimeScaledObject::TimeScaledObject()
: scale_(1.0)
, min_scale_(0)
, max_scale_(AudioVisualWaveform::k_maximum_sample_rate.to_double())
, max_scale_(oakengine_audio_waveform_max_sample_rate())
{
}