Commit Graph
100 Commits
Author SHA1 Message Date
Mike-Solar d57c812fa6 ci: fix three platform-specific build failures
- Linux: Qt6 metatype static_assert requires complete types; declare
  OakEngineBlock with Q_DECLARE_OPAQUE_POINTER in the public header
  (outside extern "C")
- Windows: gtest discovery runs the freshly linked test exe at build
  time and it cannot find the DLLs (0xc0000135); copy oakcore/oakengine/
  ffmpeg_bridge DLLs next to the test binaries on WIN32
- macOS: Imath header mixing between otio-install's stale bundled Imath
  and Homebrew's Imath broke ImathBox.h (V2h undeclared); bump the OTIO
  cache to v2 and drop bundled Imath headers so the system one is the
  only set in play
2026-08-02 16:24:16 +08:00
Mike-Solar 5f8232a594 ci: fix macOS/Linux configure (oakgl) and Windows MSYS2 git
- OAK_ENABLE_DYNAMIC_RENDER_BACKEND now defaults ON; it was previously
  undefined (OFF) unless passed, so CI configure failed on the
  unconditional oakgl target references in app/worker/tests
- add git to the MSYS2 package list in ci.yml and cd.yml; the OTIO
  build step runs git clone in the msys2 shell, where git is not
  installed by default
2026-08-02 15:32:07 +08:00
Mike-Solar 22438e0596 docs: plan for eliminating EngineEventBridge (architecture + good first issues) 2026-08-02 15:22:24 +08:00
Mike-Solar 6f931e720a fix: black screen during playback
renderer_generated_frame_for_queue pushed a raw void* QVariant into the
display queue, but on_paint only unwraps OakSharedBufferPtr; the unwrap
failed and the widget painted blank. Paused display worked because
set_display_image wraps with oak_make_shared_frame. Wrap the queued
frames the same way. Also adds env-gated OAK_DEBUG_PLAYBACK
diagnostics.
2026-08-02 14:56:41 +08:00
Mike-Solar a59c33715f fix: node graph edge display, teardown crashes, and event/audio lifetime bugs
- capi: oakengine_node_output_connection_at/_at_ex returned the source
  node as the connection destination; the actual destination is
  conn.second.node(). Out-edge enumeration was useless, so the node
  view could only draw in-edges and randomly lost whichever edges
  needed the out-edge path (random per context build order).
  Regression test in oakengine_node_test
- project teardown: Project::clear() pre-notifies node removal while
  nodes are fully constructed (observers used to crash on
  half-destroyed nodes); childEvent suppresses the removal dance while
  clearing; Node::disconnect_all/disconnect_edge get a silent mode for
  teardown so no invalidation/events touch dying members
  (is_being_cleared); ClipBlock marker disconnect guarded against
  dead viewer/markers; ProjectCopier and PreviewAutoCacher drop
  project references on Project::destroyed instead of disconnecting
  dead objects at shutdown
- preview: add oakengine_preview_request_get_audio_sample_count; the
  viewer queried sample count by passing nullptr to get_audio_samples
  which rejects it, so all playback audio was silently dropped
- app: fix unterminated input-id memcpy in ResolveGroupInput
  (nodeparamviewitem, widgetbridge) that corrupted every parameter id
- app: unsubscribe raw C-API event subscriptions in destructors of
  NodeParamViewKeyframeControl, NodeParamViewConnectedLabel and
  ExportDialog; playhead events used to fire into dead widgets
  (crash when dragging the playhead)
- tests: preview request roundtrip (video frame + audio range) and
  free-while-active teardown coverage; env-gated OAK_DEBUG_EDGES /
  OAK_DEBUG_INVALID_INPUT diagnostics
- docs: investigation notes in docs/zh/
2026-08-02 14:29:49 +08:00
Mike-Solar 30853cbcfd fix: teardown UAF in Track block cache and headless test/render environment
- Track: new InputDisconnectedEvent trims blocks_/block_array_indexes_
  when a block edge is removed outside the Track's own operations
  (block deletion, undo commands detaching a whole track). Previously
  blocks_ kept dangling pointers and Project teardown crashed in
  track_length() (ASan heap-use-after-free in oakengine_timeline_edit /
  oakengine_sync). The persistent array map is intentionally not
  rewritten so undo of remove_track can re-attach the clips; replace_block
  guards the handler with ignore_block_disconnect_
- filefunctions: honor OAK_CONFIG_DIR to redirect the configuration
  root; QStandardPaths ignores XDG_* on macOS, so the engine tests read
  the real user config (a stale 0.1.x file) and failed frame-rate
  assertions. All engine tests now set OAK_CONFIG_DIR to their tmpdir
- init test: chdir to the fixture directory before loading
  project_with_footage.ove so the engine's moved-project footage
  relocation does not rewrite the stored relative filename
- renderworkerpool: do not let the render worker inherit
  QT_QPA_PLATFORM=offscreen from a headless host process; the worker
  needs a real platform GL context and exited immediately otherwise,
  failing oak_cli_transcode
2026-08-01 20:35:13 +08:00
Mike-Solar 8399d04b44 build: raise gtest discovery timeout to 60s
Freshly linked binaries can exceed the 5s default on first run
(dyld cold cache + Qt/OCIO init).
2026-08-01 19:14:31 +08:00
Mike-Solar a4dfc62f0f fix: memory-safety and playback regressions found via ASan
- nodeparamview: add visited set to get_distance_between_nodes, fixing
  unbounded recursion (stack overflow) when the node graph has a cycle
- viewerdisplay: give texture_ a consistent owner via assign_texture();
  borrowed queue textures are now retained, created ones freed, fixing
  a dangling pointer that corrupted the heap and crashed in the GL driver
- playbackcache: resignal_requests() iterates a copy, handlers may
  clear_request_range() while iterating (ASan container-overflow)
- preview C API: preview request ticket lambdas captured the request
  state raw; after oakengine_preview_request_free the ticket outlived
  the request and the finished callback wrote into freed memory
  (heap-use-after-free). The finished flag is now a shared_ptr captured
  weakly by the callbacks
- playback: oak_playback_frame regains a timestamp (num/den) filled
  from olive::Frame; the viewer queue append no longer uses Rational()
  for every frame, which made append_timewise drop all but the first
  frame and froze the picture during playback
- mainwindow: open_node_in_viewer refuses sequence nodes; sequences
  already have the Sequence Viewer, and saved layouts could otherwise
  resurrect a redundant floating Viewer bound to the sequence
2026-08-01 19:14:31 +08:00
Mike-Solar 66d761b4b7 R8: finish app/ pure C ABI migration (P3-P9) and make OTIO required
- app/ no longer includes engine C++ headers nor holds engine C++ types:
  engine access goes through the oakengine C ABI plus C++ wrappers
  (oakutil/oaknode.h, oakutil/oakvideo.h) and app-local mirror types
  (tooltypes, trackreferencehandle, timelinecommonapp, keyframetypes,
  subtitleapp, serializedlayoutinfoapp, nodevaluehandle, sliderdisplaytypeapp)
- engine: new C ABI functions for block/track/clip/transition navigation
  and predicates, links, caches, waveform/playback, disk folder,
  sequence_track_list, node_free, footage_is_valid, block_get_track,
  get_brush; loadotio/saveotio ported to the current engine API
- OTIO is now a required dependency: CI and CD build it on every
  platform, FindOpenTimelineIO fixed for OTIO 0.16/0.19 (the old deps
  include requirement silently disabled OTIO everywhere), runtime
  libraries are bundled into packages and copied next to macOS binaries
  (oak_copy_otio_runtime)
- fix ProjectViewModel drag&drop mime read/write size mismatch (segfault)
- unify color label naming (k_olive -> "Oak") in the app-side mirror
- docs: OTIO required, FFmpeg minimum corrected to 6.0 (en/zh)
- gtest suite: 1925 passed, 0 failed
2026-07-31 22:46:52 +08:00
Mike-Solar 18aed979a2 build: fix macOS build issues blocking app compilation
- engine: liboakengine.dylib intentionally leaves olive::k_app_version
  to the final executable (olive-version-obj); whitelist that symbol
  with -U on Apple, where undefined symbols in dylibs are an error
  (ELF allows them by default).
- app/common/configwrapper.h: only declare the int64_t/uint64_t
  conversion/assignment overloads on Linux LP64, where they differ
  from qint64/quint64; elsewhere both are long long and the extra
  overloads are redeclarations.
2026-07-27 17:14:56 +08:00
Mike-Solar c0dcd33de0 R8 phase 1: extract app/engine shared utilities into shared/include/oakutil
Move the pure-header utilities shared by app/ and engine/ out of
engine/common/ into a new shared/include/oakutil/ layer (define, lerp,
decibel, digit, range, crashpadutils, autoscroll, qtutils, filefunctions
declarations, and a trimmed xmlutils exposing a CancelAtom-free void*
overload). engine/common/ keeps forwarding headers so internal include
paths are unchanged; app/ now includes oakutil/* directly.

engine/node/project.h gains an explicit NodeGroup forward declaration
previously obtained transitively through the old xmlutils.h.
2026-07-27 17:14:56 +08:00
Mike-Solar 17888a2dc1 change: remove UB in core.h 2026-07-27 08:26:13 +08:00
Mike-Solar a84e75ef47 change: UI and migrate to gtest 2026-07-27 05:27:32 +08:00
Mike-Solar dd5508e571 docs: archive completed C ABI campaign docs under plans/completed/
Move the finished migration campaign docs (handoffs v3-v6, roadmap,
R5 guides, R6 cleanup, R7 pure-ABI) from docs/zh/ into
docs/zh/plans/completed/ with an archive README; fix all
cross-references; refresh plans/README.md index (active plans now
marked unlocked).
2026-07-27 01:52:52 +08:00
Mike-Solar d7c0970d91 docs: mark R7 complete, unlock plans/riir module split 2026-07-27 00:55:12 +08:00
Mike-Solar eb634b53ef R7: pure C ABI display boundary + engine visibility closure
R7-A (Qwen 3.8 Max): oakengine/display.h rewritten to the POD contract
from r7-pure-abi-plan.md - oak_video_params everywhere, opaque
texture/frame handles with retain/free protocol (engine-heap control
blocks), OakSharedBuffer refcounted wrapper for the QVariant playback
path. All TexturePtr/FramePtr gone from app (47 sites).

R7-B (Qwen 3.8 Max): liboakengine.so exports 3486 -> 19 C++ symbols
(version script oakengine.ver: oakengine_* plus the documented
oakgl/oakvulkan dlopen plugin ABI). oakengine-obj OBJECT library feeds
both the shared lib and the test binaries (-rdynamic so dlopen'd
backends resolve engine objects).

Fix (Kimi K3): producer/consumer type mismatch - viewerdisplay
unpacks OakSharedBufferPtr but viewer.cpp pushed raw void* handles,
so no frame ever reached the display widget (all 5 vulkan viewer
tests timed out with 'never received a texture'). Producers now wrap
with oak_make_shared_frame / oak_make_shared_texture(retain).

Verified: build 0 errors, ctest 45/45, nm U _ZN5olive = 0 in
oak-editor/oak-render-worker/oak-cli, 19 exported C++ symbols in
liboakengine.so (all documented plugin ABI).
2026-07-27 00:54:46 +08:00
Mike-Solar 9b25772267 docs: module-split execution manuals for RIIR stage 1 (plans/riir/)
13 files: 00 overview, 01 two-layer adapter spec (init/free/func +
opaque handles + same-name adapter classes, frozen naming/ownership/
error/event rules), 02 dependency matrix + split order M1-M9 (from a
490-file include scan), 03 Google Test spec (per-C-API coverage,
roundtrip tests, leak counters), M1-M9 per-module manuals with frozen
C APIs (common/undo/node/timeline/codec/audio/render/task/plugin) and
the facade assembly-layer verdict.
2026-07-27 00:06:33 +08:00
Mike-Solar 6ea653e6dc Merge branch 'c-abi-migration': complete C ABI facade migration
The app/worker/cli now reach liboakengine exclusively through the
oakengine_* pure-C ABI (557 -> 0 undefined olive:: symbols in
oak-editor, 0 in oak-render-worker). Includes the full facade,
app-side migration, EngineEventBridge event mechanism, undo-group
semantics, and the campaign documentation (roadmap through R7 plan).

Verified: full build 0 errors, ctest 45/45 (oak_cli_transcode
intermittent SEGFAULT is pre-existing flaky).
2026-07-26 22:44:12 +08:00
Mike-Solar 96defacacb chore: recovery backups, temp files and misc campaign artifacts
.bak LocalHistory recovery backups, .tmp0/.tmp1, .gitignore update,
reasonix.toml, convert_videoparams.py. Kept as-is from the campaign
branch; cleanup can happen separately on main.
2026-07-26 22:43:42 +08:00
Mike-Solar 5b68bbeec8 cli/worker/tests: facade-only linkage and updated test suites
oak-cli and oak-render-worker use the C ABI exclusively (0 olive::
symbols). Google Test suites updated for the migrated APIs, new engine
facade test coverage, CLI transcode verification.
2026-07-26 22:43:32 +08:00
Mike-Solar 0aa5879f35 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.
2026-07-26 22:43:21 +08:00
Mike-Solar f95590e924 engine: internals supporting the facade migration
UndoStack push_pre_executed, FolderAddChild-based true folder moves,
NodeViewDeleteCommand delete-then-reconnect support, event emission
points for the facade event bus, and related internal adjustments.
2026-07-26 22:43:09 +08:00
Mike-Solar e9f173916f engine: complete C ABI facade (liboakengine oakengine_* surface)
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.
2026-07-26 22:43:00 +08:00
Mike-Solar c486c853ff docs: C ABI migration campaign plans, handoffs and roadmap
Complete documentation set for the facade migration (B1-R6) and beyond:
facade-migration-roadmap, handoffs v3-v6, R5 guides, R6 cleanup plan,
R7 pure-ABI plan, long-term plans (riir/ai-agent/gtest/ui-redesign),
updated Chinese README draft, UI design mockups, and the Google Test +
struct-typedef rules in CONTRIBUTING.
2026-07-26 22:42:44 +08:00
Mike-Solar fcf717f6a7 docs: correct the icon plan - move the registry to the app, not to core 2026-07-20 19:08:09 +08:00
Mike-Solar 3e37f7d702 docs: lay out the phase-4 coupling-reduction campaign in the roadmap 2026-07-20 19:03:41 +08:00
Mike-Solar 772029adc3 docs: record the composite-scenario facade verdicts in the roadmap 2026-07-20 18:59:08 +08:00
Mike-Solar 1384ad1e95 app: migrate the viewer panel to the facade playback engine 2026-07-20 18:55:04 +08:00
Mike-Solar d63131cb12 engine: add the playback family to the facade 2026-07-20 17:46:49 +08:00
Mike-Solar f5acf31b43 docs: refine the viewer migration mapping with frame-path reuse and facade refinements 2026-07-20 16:45:09 +08:00
Mike-Solar 9872c4f48f docs: record the icon cluster and core-boundary findings in the roadmap risks 2026-07-20 16:37:52 +08:00
Mike-Solar cc6105ddd0 docs: add a measurable engine-coupling metric to the roadmap acceptance list 2026-07-20 16:32:24 +08:00
Mike-Solar 8f3ae107b4 docs: detail the viewer panel migration mapping in the facade roadmap 2026-07-20 16:27:45 +08:00
Mike-Solar 245f204c81 engine: timeline panel leftover commands migrate to the facade (round 3)
- new primitives: clip_toggle_enabled (per-block flip), clip_set_linked,
  sequence_add_default_transition (config-driven, sequence timebase),
  node_set_label_many, node_set_color_label, plus observation getters
- toggle-links, default transitions, enable toggles, color labels, and
  block renaming now go through the facade; the stray empty undo entry
  from block renaming is gone along the way
- nest/multicam/waveform-sync stay as documented composites: they mix
  redo_now intermediate state, graph surgery, and app-side computation
  that a single primitive cannot express faithfully
- the timeline panel's command execution paths are now fully migrated
2026-07-20 16:17:37 +08:00
Mike-Solar 37aa859cd0 engine: keyframe properties dialog migrates; keyframeviewundo deleted
- new primitives: keyframes_set_time_many (conflict-safe batch time
  move), keyframes_set_value_many (captured or explicit old values),
  keyframes_set_bezier_many (double precision), and
  keyframe_set_bezier_point (single handle with NaN-capture fallback)
- dialog and curveview drag finalization go through the facade; the
  tests exercise the same global undo stack via oakengine_project_undo
- keyframeview/keyframeviewundo.{h,cpp} removed with zero remaining
  references
2026-07-20 15:41:36 +08:00
Mike-Solar 8311128f4c engine: node parameter panel migrates to the facade
- new facade API: set_input_at_time (element addressing, track=-1 for
  all components at once), set_input_string_at_time, frame_time_base,
  array_insert_at/remove_at, disconnect_ex (element-aware), and
  keyframes_set_type_many (first cross-track keyframe op, addressed by
  (time,track) pairs)
- the widget bridge's commit funnel, color path, array ops, label
  disconnect, and keyframe set-type actions in keyframeview/curvewidget
  now go through the facade; keyframeviewundo.h loses two consumers
- deliberate leftovers with rationale: keyframecontrol's multi-track
  composite ops (documented track-0-only limitation of the keyframe
  family), keyframeproperties dialog (needs a set_time primitive),
  curveview's drag UX, and NodeInputDragger (already engine-side)
2026-07-20 14:42:00 +08:00
Mike-Solar 0fe37dba3c engine: timeline panel batch/composite commands migrate to the facade (part 2)
- new primitives: ripple_delete_in_to_out (ripple or gap fill plus
  work-area state, one undo command), trim_clips_to (batch edge trim
  returning a count), delete_empty_tracks (type-filtered batch), and
  marker_remove_many (sparse marker deletion by timestamp array)
- delete-in-to-out, edit-to, delete-all-empty-tracks, and sequence
  viewer marker deletion now go through the facade; empty operations
  no longer push empty undo entries
- footage viewer marker deletion keeps its app path deliberately
  (facade marker handles are Sequences, not generic viewers); the
  tentative subtitle track and pointer drag chain stay as documented
  leftovers
2026-07-20 13:37:10 +08:00
Mike-Solar 2aa7eec016 engine: timeline panel core edit commands migrate to the facade (part 1)
- new batch primitives: split_clips (link-preserving, single undo
  command), delete_clips (gap replace + optional ripple with explicit
  region support), ripple_delete_range, marker_add_ex with color
- razor/split-at-playhead, clip delete, ripple-to-point, track delete,
  and the non-dialog marker path now issue facade commands instead of
  the app's own undo command classes
- batch operations deliberately produce one undo command per user
  action (deleting twenty clips is one entry, not twenty); selection
  and transition removal stay UI-side as documented leftovers
2026-07-20 13:17:07 +08:00
Mike-Solar 0a25d43218 engine: sequence parameters dialog migrates to the facade
- new facade API: video params ex (dimensions/rate/par/interlacing/
  preview format/divider), audio params, preview divider, and
  undoable-flagged setters mirroring the dialog's dual undo/no-undo
  modes; label setting gains node_set_label_ex
- the dialog's own SequenceParamCommand is gone; accept now issues
  facade calls (per-field commands, unchanged fields skipped)
- preset system stays UI-side by design: its flat XML schema never
  touches engine objects
- the auto-cache checkbox maps to the engine's existing stub (no undo
  noise)
2026-07-20 12:42:54 +08:00
Mike-Solar 456060ef3e engine: footage properties and project explorer migrate to the facade
- new facade API: video stream overrides (colorspace/range/interlacing/
  premultiply), pixel aspect, image-sequence params, stream enable,
  source start time, and colorspace candidates - all undoable
- project explorer proxy actions now run through FacadeProxyTask and
  the facade media-management functions (ProxyManager references in
  projectexplorer.cpp drop from 5 call sites to a comment)
- footage properties dialog reads/writes through the facade; its two
  app-side undo command classes are gone
- handle-model fix: the footage handle is a heap state object, not a
  plain pointer cast - oakengine_footage_borrow() wraps app-held
  Footage nodes correctly (nine UB reinterpret_casts caught by the
  DialogFootageProperties tests)
2026-07-20 12:16:21 +08:00
Mike-Solar 62f18125cb export: dialog execution path now goes through the facade
- oakengine_export_render_ex covers the dialog's entire option surface
  (formats, codecs, pix fmts, audio params, ranges incl. still frame,
  subtitles, scaling, threads, custom OCIO color transform names,
  per-codec key/value options) - zero feature reduction
- the dialog's Start now creates a FacadeExportTask that drives
  oakengine_export_render_ex instead of constructing ExportTask in the
  UI; progress flows through the facade callback and cancel through
  oakengine_export_cancel (OAKENGINE_E_CANCELLED preserves the
  keep-dialog-open semantics)
- two real fixes: audio sample format 0 no longer means an
  AAC-unsupported u8_p (default is f32_p), and image-sequence exports
  use the engine's real [#####] placeholder instead of a made-up -%04d
2026-07-20 11:06:46 +08:00
Mike-Solar 1ea6527b80 engine: prewarm conforms before facade export
Drives ConformManager for every audio-streaming footage with the same
AudioParams the export will use, before starting ExportTask - matching
the app's preview prewarm semantics. Root-cause analysis showed the
export's own wait-for-conform path already worked; this remains as a
first-export speedup and a belt-and-braces guarantee
2026-07-20 09:55:27 +08:00
Mike-Solar bb17c59c0f engine: add the preview family to the C ABI facade
- clip-level loop mode (off/loop/clamp), undoable
- per-channel audio levels as one-frame RMS at a timestamp
- waveform min/max buckets over a footage range, rendered on demand
- two real fixes uncovered by this family: conform completion signals
  were starved by msleep-only waits (audio renders always came back
  empty), and incomplete tickets from conform-pending renders are now
  retried until the conform is ready
2026-07-20 09:28:47 +08:00
Mike-Solar 882ce8268c engine: add media management (relink/proxy) to the footage facade
- relink through Footage::set_filename with the engine's natural
  clear+reprobe cascade; find_offline_footage recovers offline media
  by exact filename match under a search directory
- synchronous proxy generate (event-loop driven like the export
  family), state query, enable/disable, path, and delete, mirroring
  the app's proxy dialog assembly
- documented as non-undoable, matching the app's existing semantics
2026-07-20 08:53:43 +08:00
Mike-Solar 71b1243ad8 engine: add sequence structure to the timeline facade
- undoable track removal (content restored on undo) and true-move
  track reordering assembled from the edge commands (the engine has
  no move-track API)
- track height/mute/lock getters and setters, documented as
  non-undoable to match the engine's current semantics
- undoable marker add/remove/rename; duplicate timestamps are
  rejected with E_STATE instead of hitting the engine's debug assert
2026-07-20 08:42:03 +08:00
Mike-Solar eaa301b755 engine: add the keyframe family to the node facade
- keyframed state, count, read (time in sequence timebase + mapped
  values), add/remove, easing read/write (linear/bezier/hold with
  control points), and clear - all undoable with full undo/redo
  assertions
- easing set commands are minimal capi-local UndoCommands matching
  the app-layer semantics (engine has none of its own); same-time
  duplicates are rejected with E_STATE instead of hitting the engine's
  debug assert
2026-07-20 08:31:24 +08:00
Mike-Solar e82011b0ee engine: add the node graph family to the C ABI facade
- project node enumeration, type/name/label metadata, input
  introspection (id, mapped value type, connection state)
- typed parameter read/write for the eight common NodeValue kinds
  (int/float/bool/rational/color/vec2-4/combo/string), undoable via
  NodeParamSetSplitStandardValueCommand - the split-track value path,
  chosen after proving the standard-value command stores whole
  variants into a single component track
- graph operations (add/remove/connect/disconnect) reusing the
  engine's undo commands, all covered by undo/redo assertions
2026-07-20 08:16:43 +08:00
Mike-Solar db355c28b9 docs: facade migration roadmap for the remaining UI migration work 2026-07-20 07:53:16 +08:00
Mike-Solar 1ab7fa49a0 engine: timeline edit primitives round 2 (split/ripple-delete/trim/move)
All four are undoable and reuse the timeline/ command classes verbatim
(BlockSplitCommand, TrackRippleRemoveAreaCommand, BlockTrimCommand,
ReplaceBlockWithGap+PlaceBlock), with undo/redo assertions covering
split halves, media-in alignment on trim, ripple shift amounts, and
full restoration on undo
2026-07-20 07:52:07 +08:00
Mike-Solar b9b5ad09f7 engine: add the export family to the C ABI facade; mp4 transcode
- oakengine_export_render drives ExportTask synchronously (offline
  render + encode) with a progress callback, codec probing, and a
  thread-local error channel; exporter.h keeps clear of the visibility
  macro header
- oak-cli transcode now defaults to mp4 (H.264/AAC) with --format ppm
  keeping the raw output path
- two real concurrency bugs found by the facade's own test: ExportTask
  deadlocks when start()ed synchronously (queued conform handshake
  needs an event loop), and the progress callback must be captured by
  value because it fires on the task thread
2026-07-20 07:34:21 +08:00
Mike-Solar c1ee784600 engine: timeline edit primitives and oak-cli transcode
- oakengine_sequence_add_track and add_footage_clip are the facade's
  first editing primitives: undoable track creation and clip placement
  with full range validation, clip enumeration, and gap filtering
- oak-cli transcode closes the loop: media file -> import -> clip ->
  render, producing scaled PPM frames and a WAV from just the C ABI
- engine fix uncovered by transcode: the render worker used an invalid
  empty AudioParams for IPC render frames, crashing any sequence that
  contains audio; it now derives them from the rendered node itself
- sequence_new hardens its defaults against missing audio config keys
2026-07-20 06:33:14 +08:00
Mike-Solar 1a7029fd8f engine: add the footage family to the C ABI facade; oak-cli probe
- oakengine_footage_probe inspects media without a project (decoder
  probe): stream counts, per-stream video info (dimensions, rate,
  duration, color tags, interlacing), audio info, duration, decoder
  name, source start time; thread_local last_error for the NULL-handle
  failure paths
- oakengine_project_import_footage adds probed footage to a project
  through the same undoable command path as the app
- oak-cli probe prints decoder/duration/per-stream details and runs as
  a ctest everywhere (no GL)
- dual ownership documented: probe handles are owned, imported footage
  is borrowed from its project
2026-07-20 06:03:16 +08:00
Mike-Solar 6118260e01 engine: expose sequence video dimensions in the timeline facade
- oakengine_sequence_get_video_params (width/height/pixel aspect) with
  assertions for the default 1920x1080 square-pixel sequence and the
  invalid-handle path
- oak-cli now renders at the sequence's real dimensions instead of the
  hardcoded 1920x1080
2026-07-20 05:51:53 +08:00
Mike-Solar 009af0ff3f cli: add oak-cli, the first pure C ABI consumer of liboakengine
- oak-cli info prints project/sequence/footage details; oak-cli render
  writes PPM frames and a PCM WAV through the facade only - no engine
  C++ headers, no Qt headers, links just liboakengine
- exit code 2 marks rendering-unavailable so ctest can skip cleanly on
  machines without a GL render backend; info test runs everywhere
- facade fix uncovered by the CLI: project load now absolutizes the
  path, so relative footage paths can't be mistaken for a moved
  project; the fixture project now carries probed footage wired into
  its sequence so it renders real content
- packaged like the other binaries (Linux bin install, macOS bundle,
  Windows DLL copies); DESTDIR-verified
2026-07-20 05:48:07 +08:00
Mike-Solar b26ffb7d64 engine: add the renderer family to the C ABI facade
- oakengine_renderer_create/set_mode/last_error, render_frame (sync,
  60s timeout, CPU frames via the existing worker pool),
  render_audio (planar float), cancel; frames and audio buffers are
  owned handles with borrowed data pointers
- output colorspace names map to OCIO display transforms, with
  graceful fallback to reference-space output
- oakengine_renderer_test: parameter validation and error paths need
  no GL and always run; render assertions gate on
  DynamicRenderer backend availability and SKIP cleanly otherwise
- fix a facade bug found by its own test: wait_for_ticket leaked a
  connected lambda capturing a stack reference, which a subsequent
  cancelled ticket could fire into reused stack memory
2026-07-20 05:26:42 +08:00
Mike-Solar 5db7aac058 engine: add init/project/timeline families to the C ABI facade
- oakengine_init/shutdown with HEADLESS/RENDER flags: headless boots
  Config, NodeFactory, ColorManager, task/conform/proxy/frame/disk
  managers and the serializer (plus an offscreen QGuiApplication that
  Qt requires for QAction); RENDER adds RenderManager. Idempotent and
  upgradable, no UI anywhere
- oakengine_project_* (17): create/load/save, modified state, name,
  footage enumeration with online check, undo/redo, sequence access
- oakengine_sequence_* (13): name, length (seconds and rational),
  frame rate, per-type track counts, playhead (timestamp and seconds),
  work area, markers; sequences are borrowed handles owned by their
  project
- pure-C oakengine_init_test covers init idempotency, save/load
  round-trip through a real fixture project, footage online checks,
  timeline parameters, and NULL/bounds safety - no GL required
2026-07-20 04:54:45 +08:00
Mike-Solar 37845302f9 engine: begin the liboakengine C ABI facade with the IPC subsystem
- oakengine/export.h establishes the OAKENGINE_API visibility macros;
  include/oakengine/ipc.h is the first pure-C surface (41 functions:
  shm, frame slot pool, and the worker IPC messages as POD<->JSON
  build/parse), implemented in engine/src/capi/
- the IPC implementations move to engine/src/oliveimpl (namespace
  olive::engine::internal::ipc); engine/render/ipc/*.h are rebuilt as
  same-name/same-API wrapper classes forwarding across the C boundary
- FrameSlotMeta is shared with the C header verbatim so the app/worker
  wire format (v1) is bit-identical; static_asserts pin sizeof and
  field offsets
- spscringbuffer.h moves to include/oakengine/ as an inline-only
  header (no symbols, not ABI)
- new pure-C test oakengine_ipc_test (make_oakengine_test, no GL)
  covers shm, frame pool, message round-trips and the layout asserts;
  full gtest suite stays green (1986 tests)
2026-07-20 04:12:58 +08:00
Mike-Solar 28c4426236 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)
2026-07-20 03:23:28 +08:00
Mike-Solar 026ff94b5e refactor: invert the last engine-to-UI dependencies
- NodeFactory's menu creation moves to UI-side widget/menu/factorymenu
  (the factory only exposes its node library read-only now)
- DiskManager's cache-settings dialog is created through a registered
  std::function handler (registered by Core at startup)
- OlivePluginInstance creates progress UIs through a
  PluginProgressReporter interface (Null fallback headless) and queries
  the active viewer through a provider callback, both registered by Core
- factory.h, diskmanager and pluginSupport no longer reference any
  widget//dialog//panel//window headers or classes
2026-07-20 02:05:01 +08:00
Mike-Solar bff06e00e5 refactor: split Core into EngineCore (engine) and Core (UI)
EngineCore (new app/coreengine.{h,cpp}) owns every engine-safe part of
the old Core singleton: CoreParams, lifecycle of the engine managers,
UndoStack, tool/snapping/timecode state, locale, autorecovery, recent
projects, footage filters, clipboard, project registry, type
declarations, and the proxy toggle. UI dependencies are inverted
through hooks instead: status-bar/cache-full signals and std::function
handlers for image-sequence confirmation, footage relink, OTIO import,
project save/close and layout load (same pattern as
Config::ErrorHandler).

Core (app/) now derives from EngineCore and keeps only UI behavior:
the main window, dialogs, panel heuristics, import/export flows and
project lifecycle presentation. Its public API is unchanged (all
inherited), and Core::instance() covariantly static_casts the engine
singleton. The render worker constructs EngineCore directly, making it
the first binary that no longer needs the UI side of Core.

~25 engine call sites move from core.h to coreengine.h; a dozen more
drop a vestigial core.h include (gaining direct includes for symbols
they were borrowing transitively). Full gtest suite green (1986 tests,
0 failures).
2026-07-20 01:38:42 +08:00
Mike-Solar 5109dd2995 refactor: move the ratio input dialog out of engine common/ into dialog/
get_float_ratio_from_user() is pure UI (QInputDialog/QMessageBox);
common/ stays dialog-free.
2026-07-20 01:04:57 +08:00
Mike-Solar 69cd8d3a75 refactor: move msg_box out of engine qtutils into the dialog layer
The engine-side QtUtils no longer references QMessageBox; the two
dialog call sites use the identical olive::msg_box() inline helper.
2026-07-20 01:01:18 +08:00
Mike-Solar 03d4087124 refactor: cut engine-to-UI include violations ahead of the liboakengine split
- slider DisplayType enums sink to node/sliderdisplaytype.h (canonical
  engine home); FloatSlider/RationalSlider alias them for compatibility
- DropWithoutSequenceBehavior enum sinks to common/dropworkflowbehavior.h
- Config errors now go through a registered ErrorHandler hook instead of
  QMessageBox with a MainWindow parent; the style default no longer
  depends on the UI style manager
- MainWindowLayoutInfo moves to node/project/serializer/ and its panel
  dependency is reduced to a plain std::map alias (PanelLayoutInfo),
  breaking the engine -> PanelWidget -> KDDockWidgets chain
- ProjectImportErrorDialog moves from task/ to dialog/projectimport/
- remove confirmed-redundant UI includes and give project.h/import.h/
  project.cpp the direct includes they were borrowing transitively
- project.h includes folder/sequence headers directly (it used both
  types in its own API all along)
2026-07-20 00:52:12 +08:00
Mike-Solar 98f2f3e224 build: install liboakcore from the default target set
core was added with EXCLUDE_FROM_ALL, which also excluded its install
rules, so liboakcore.so never made it into packages. It now installs to
the platform libdir (/usr/lib on this machine, verified via DESTDIR).
2026-07-19 23:52:59 +08:00
Mike-Solar 1128694257 core: restrict liboakcore exports to the C ABI via version script
Even statically linked runtime libraries must not leak symbols: the
linker version script now whitelists oakcore_* only. nm reports 188
oakcore_* exports and zero anything else.
2026-07-19 23:51:15 +08:00
Mike-Solar 4fc8b80d7e core: turn olivecore into liboakcore.so with a pure C ABI
liboakcore is now a shared library that exposes only a C ABI:
- every value class (Rational, TimeRange, Color, Bezier, AudioParams,
  SampleBuffer) and the free-function groups (StringUtils, fraction
  utils, Timecode) is wrapped in an opaque-handle C API under
  core/include/olive/core/oakcore/ (init/copy/free + self-first
  functions), implemented in core/src/capi/
- consumers keep the original C++ API unchanged through same-name
  wrapper classes that hold the handle and forward across the C
  boundary; original implementations moved to core/src/oliveimpl
  (namespace olive::core::internal) and are hidden from export
- TimeRangeList/TimeRangeListFrameIterator are reimplemented inline
  over the wrapper (iterators/containers don't cross C ABI)
- generic Value container stays internal (unused by consumers) and is
  no longer part of the public umbrella header
- hidden visibility + OAKCORE_BUILD export macro; nm shows zero
  olive::* symbols exported
- install into the platform's standard libdir (GNUInstallDirs);
  Windows DLLs next to the executables, macOS into the app bundle
- TimelineWorkArea::in/out/length now return by value: the wrapped
  TimeRange getters return values, and forwarding them through const
  references dangled (found via RenderWorkerFootageTest crash)
- tests: 9 new pure C ABI test executables (oakcore_*_test) covering
  every public C function; 4 stale legacy core tests removed (they
  targeted a long-renamed API and were never built due to a malformed
  option() that also kept OLIVECORE_BUILD_TESTS off)
- CI/CD: oakcore.dll staged for NSIS, liboakcore.so added to the
  AppImage validation list, build-tree DLL copies on Windows
2026-07-19 23:49:40 +08:00
Mike-Solar 52625f2b98 proxy: restore pdivider (de)serialization dropped in the commit split
The load/save of the per-footage custom divider attribute was lost when
the proxy changes were re-applied during the three-way commit split;
ProxyManager.FootagePersistsCustomProxyParams caught it.
2026-07-19 23:49:14 +08:00
Mike-Solar 7832cb30b3 build: move render worker out of app/ into top-level worker/
- oak-render-worker now builds from worker/ (own CMakeLists.txt) as a
  peer of app/; RenderWorkerPool resolves the new build-tree location
- deduplicated the Linux install() rules for the worker
- worker-spawning tests resolve build/worker instead of build/app
- cd.yml: Windows staging copies the worker from its new output path
2026-07-19 22:12:29 +08:00
Mike-Solar 76f5c2a65b color: input colorspace auto-detection, HDR export tags, LGG/white balance nodes, more LUT formats, waveform parade
- media color primaries/transfer tags now flow from the FFmpeg probe
  through VideoParams into Footage::get_colorspace_to_use(); precedence
  is user override > media tags > project default
- export nclc tags derive from the output colorspace (PQ/HLG/BT.2020,
  P3, sRGB, Rec.601, Rec.709) instead of hardcoded BT.709
- new OCIO Color Grading (Log) node (lift/gamma/gain) and White Balance
  node (kelvin temperature + tint, HDR-safe)
- LUT whitelist extended to 9 OCIO-supported formats
- waveform scope gains an RGB parade mode (GPU and software paths)
- tests updated for the new colorspace precedence
2026-07-19 21:45:51 +08:00
Mike-Solar a7ddc0f114 audio: master-clock playback timing, output clock compensation, buffer config, interpolated speed
- playback timer uses the audio output device as its master clock: the
  PortAudio callback counts consumed frames (including underrun
  zero-fill) so video cannot drift away from what is heard; wall clock
  remains as fallback when no clocked output is running
- output clock compensates for device output latency; new Preferences >
  Audio buffer size setting (0 = auto)
- SampleBuffer::speed() now uses linear interpolation instead of
  nearest-neighbor sampling
- regression tests: audio-clock driven timer (fwd/rev/speed), wall
  clock fallback, interpolation correctness
2026-07-19 21:44:34 +08:00
Mike-Solar 0c02ff0d77 proxy: isolate proxies from export renders and improve proxy workflow
- FootageJob::should_use_proxy() centralizes the proxy decision; worker
  pre-decode now honors the render mode so exports always decode the
  original media (previously every frame was pre-decoded from proxies)
- global Tools > Use Proxy Media toggle with footage invalidation
- ffmpeg -progress parsing for real percentage feedback while generating
- divider mode (1/2, 1/4, 1/8 of source resolution) with UI, proxy
  filename tags and per-footage persistence (pdivider)
- Media Offline warning slat rendered for missing footage
- regression tests: export isolation, relink invalidation, offline slat,
  progress parsing, divider arguments
2026-07-19 21:43:58 +08:00
Mike-Solar bb40b4923e style: unify identifier naming per updated conventions
Automated with clang-tidy readability-identifier-naming (config added to
.clang-tidy) plus scripted passes, per the updated rules now documented
in CONTRIBUTING.md:

- types (class/struct/enum/alias/template params): PascalCase
- functions, variables, members: snake_case (incl. rational -> Rational)
- private/protected members: trailing underscore; static member
  variables likewise (instance_, available_themes_)
- constants and enum values: snake_case (kLinear -> k_linear,
  F32P -> f32p); ALL_CAPS reserved for macros
- macros: OAK_ prefix (OLIVE_ADD_TEST/OLIVE_ASSERT/OLIVE_CONFIG ->
  OAK_ADD_TEST/OAK_ASSERT/OAK_CONFIG, GL_PREAMBLE -> OAK_GL_PREAMBLE,
  include guards -> OAK_*)
- file names: all lowercase (Current/Plugin/OliveHost/OliveClip/
  OlivePluginInstance -> current/plugin/olivehost/oliveclip/
  oliveplugininstance)
- getters share the member name sans underscore, setters set_foo()
- Qt and third-party (OpenFX) virtual overrides and framework callbacks
  keep their original names (exempt in .clang-tidy)

Manual follow-ups required where automation could not reach:
- string-based QMetaObject/SIGNAL/SLOT references updated to renamed
  methods (AddTask, CreatedFile, DeleteSpecificFile, moveSelectionUp, ...)
- macro bodies referencing renamed methods (OLIVE_CONFIG,
  NODE_DEFAULT_DESTRUCTOR, MANAGEDDISPLAYWIDGET_*)
- self-shadowing locals renamed where signals/methods became same-named
  (size_changed, worker_count, selected_items, import param, filters)
- third_party OFX member/namespace usages restored (OFX::Host::*,
  _created, _clipPrefsDirty, createInstance, clearPersistentMessage)
- STL protocol aliases restored (const_iterator) with .clang-tidy
  ignore rules; qHash overloads restored

Full build and test suite pass: ctest 4/4, ~1960 gtest cases green.
2026-07-19 16:10:54 +08:00
Mike-Solar cb1718a103 test: replace fake and duplicate tests with real assertions
Fake tests rewritten to assert real behavior:
- audio_smoke: conversion tests now actually Convert() samples and verify
  output; waveform length/summary assertions tightened to exact values
- plugin_format_conversion: RowBytes/U8ToU16/LoadImageFile now call real
  production code (VideoParams::GetBytesPerPixel, sws scaler, OIIO decode
  of tests/img.png with known pixel values)
- core_color: HSV round trip now verifies fromHsv(toHsv(c)) == c instead
  of comparing toHsv against its own accessors
- core_bezier/node_inputimmediate: expected values replaced with
  independently derived constants instead of re-running the code under test
- common_commandlineparser/common_debug/common_jobtime: capture
  stdout/stderr/qDebug and assert actual output content
- proxy_manager: ProxyFinished test now drives a real proxy job instead of
  emitting the signal itself; proxy_dialog/panel/proxy/preferences/timeruler
  tests assert real widget state
- viewer_smoke/preview_autocacher/render_misc: zero-assertion tests given
  observable-state assertions or removed where nothing is observable

Duplicates removed:
- plugin_smoke_test.cpp: 18 tests duplicated from plugin_paraminstance /
  plugin_support_* / plugin_renderer_readback (751 -> 180 lines)
- module_smoke HumanStrings tests covered precisely by ui_humanstrings_test
- render_misc duplicate kDefaultInterpolation constant check

Removed by policy (skip allowed, never disabled):
- all DISABLED_ prefixes: re-enabled as real offscreen tests or deleted
- ffmpeg_decoder_hw: hardcoded personal path replaced with
  OAK_TEST_HW_DECODE_FILE env var, GTEST_SKIP when unset

Also:
- config_test: restore Config defaults after run (cross-test pollution)
- render_worker_footage: drop /tmp debug-output scaffolding
- previewaudiodevice construction test asserts the real bugfix
2026-07-19 13:58:31 +08:00
Mike-Solar b0aa683499 fix: dangling OpenGL context crash and uninitialized audio frame size
- OpenGLRenderer: hold the viewer-owned QOpenGLContext in a QPointer so
  DestroyInternal() safely skips it when the context has already been
  destroyed by Qt's shared-context lifecycle. Fixes a SIGSEGV when the
  full gtest suite ran MainWindow.ConstructsOffscreenWithPanelsAndMenus
  after earlier viewer tests.
- PreviewAudioDevice: add SetParams() deriving bytes_per_frame from the
  audio format (bytes per sample * channel count) instead of staying 0.
2026-07-19 13:58:06 +08:00
Mike-Solar 842bc7632b update: Update build.md 2026-07-18 20:57:10 +08:00
Mike-Solar 64a250391c about: thank Enzo GD in the About and Welcome dialogs
Enzo GD, administrator of the Olive Facebook user group, gave this
project generous promotional support in its early days. The shared body
text now carries a special-thanks line in both the About dialog and the
first-run Welcome dialog; zh_CN translation included.
2026-07-18 20:13:58 +08:00
Mike-Solar fd93de42ca Merge remote-tracking branch 'origin/main' 2026-07-18 18:27:46 +08:00
Mike-Solar ca68005fc2 ci: switch to GitHub-hosted runners and add daily cache-warming build
- Runners: ubuntu-24.04, macos-15, windows-latest (free tier) instead
  of WarpBuild 8x/12x instances
- openfx-misc parallelism adapts to nproc (GitHub runners have fewer
  vCPUs than the WarpBuild instances)
- Schedule a daily 03:17 UTC run so ccache entries never hit GitHub's
  7-day cache expiry and system dependencies stay current
2026-07-18 14:29:14 +08:00
Mike-Solar 4eb0ef6fc6 change: remove dymatic backend. 2026-07-18 14:20:35 +08:00
Mike-Solar 7206ae8c9f ci: switch all caches to actions/cache (drop WarpBuilds/cache)
WarpBuild's cache service is billed; GitHub's cache is free (10 GB
repo limit with LRU eviction, ample for ccache entries). All three
cache sites — ccache for the matrix and dynamic-backend jobs, plus the
macOS OpenTimelineIO build — now use actions/cache@v4, which also
removes the Windows-specific fallback step.
2026-07-18 14:05:33 +08:00
Mike-Solar f6fc12bb9d ci: use actions/cache for ccache on Windows runners
WarpBuilds/cache does not support Windows; keep it for Linux/macOS and
restore the same keys from GitHub's cache service on Windows instead.
2026-07-18 14:03:58 +08:00
Mike-Solar 988e0e5b44 ci: fix ccache dir path — runner.temp is not a valid expression context
Point CCACHE_DIR and the cache action at a fixed workspace directory
instead; checkout cleans untracked files before the restore, so the
ordering is safe.
2026-07-18 14:02:42 +08:00
Mike-Solar bfce4d916a ci: add ccache compile caching via WarpBuilds/cache, shrink runners to 8x
- Restore ccache with WarpBuilds/cache@v1 on all three OS and the
  dynamic-backend job (per-OS keys with branch fallback), and wire it
  up through CMAKE_C/CXX_COMPILER_LAUNCHER; builds print ccache -s so
  hit rates are visible in CI logs
- Cache the macOS OpenTimelineIO build (keyed on v0.16.0) and skip the
  rebuild on cache hit
- Install ccache via apt/brew/MSYS2 as needed; CCACHE_MAXSIZE=1G
- Runner sizes 16x/32x -> 8x (macOS 12x kept); openfx-misc make -j8 to
  match
2026-07-18 13:57:15 +08:00
Mike-Solar 900120d9ba update: Update README.md 2026-07-17 22:51:46 +08:00
Mike-Solar 1cad5a8fdb update: Update README.md 2026-07-17 22:20:02 +08:00
Mike-Solar e18724129b tests: make ViewerSmokeTimer.DifferentTimebases robust to CI jitter
The test assumed msleep(100) measures close to 100 ms (expecting ~2
frames at 24fps vs ~6 at 60fps). On the loaded macOS CI runner the
24fps leg overslept ~2.5x, producing 6 frames at both rates. Measure
the actual interval per leg and compare against the expected frame
count with a 1-frame tolerance, and use highly distinct timebases
(1fps vs 240fps) for the rate-ordering check.
2026-07-17 22:08:03 +08:00
Mike-Solar dff1f2955d cd: Remove vulkan-headers which is replaced by libvulkan-dev. 2026-07-17 21:51:59 +08:00
Mike-Solar ae908441c8 packaging: ship the Vulkan backend on all platforms, standard lib layout
- Linux CD builds no longer disable Vulkan: the AppImage, deb, rpm and
  Arch packages now build and ship liboakvulkan (the AppImage deploys
  it via linuxdeploy --library so libvulkan is bundled too, and the
  verify step checks both backend libraries); deb/rpm dependencies gain
  libvulkan1/vulkan-loader; the Arch PKGBUILD gains vulkan-headers
- macOS CD installs vulkan-loader and exports VULKAN_SDK so
  find_package(Vulkan) locates the Homebrew loader (previously the
  Vulkan backend silently never built there)
- ffmpeg_bridge now installs to the standard lib directory with
  /../lib RPATH instead of the non-standard ffmpeg_bridge/bin
  layout (verified: editor, worker, oakgl and oakvulkan all resolve it)
- build guides (EN/ZH) document the macOS Vulkan backend dependencies
  and the VULKAN_SDK variable
2026-07-17 21:41:34 +08:00
Mike-Solar 6229381426 version: derive from git tag/commit hash; drop audio playback debug logs
Version is no longer hardcoded: at configure time CMake uses the tag
name when HEAD is exactly on a tag (leading "v" stripped), otherwise
the first 8 hex digits of the commit hash. When git is unavailable
(e.g. source tarball) it falls back to the contents of version.txt,
which is now the single place to bump the release version.

Also remove the temporary qDebug() flood in the audio playback path
(ViewerWidget::QueueNextAudioBuffer / ReceivedAudioBufferForPlayback,
AudioManager::PushToOutput).
2026-07-17 21:10:13 +08:00
Mike-Solar 016341ea7c fix: honor DirectoryIsValid create flag; unbreak Windows CI tests
DirectoryIsValid() ignored try_to_create_if_not_exists and always
called mkpath(). On the Windows CI runner (which may create dirs at
the drive root) PathWidget validation both created bogus directories
and never flagged invalid paths; on Linux the mkpath just failed.

- filefunctions: only mkpath when try_to_create_if_not_exists is set
- TaskProjectLoadTest: Project::set_filename() stores native
  separators on Windows, normalize before comparing paths
- DialogProjectProperties: use a nonexistent file inside a temp dir
  instead of a fixed /definitely/... path that prior tests may have
  partially created on a writable drive
2026-07-17 20:35:25 +08:00
Mike-Solar c734ecdba1 tests: make unknown-icon check robust across Qt builds
QIcon::addFile() differs between Qt versions in whether entries for
nonexistent files keep the icon non-null (Ubuntu CI failure). Assert
on what matters: no usable pixmap or size is produced.
2026-07-17 18:28:38 +08:00
Mike-Solar ff95ffa216 fix: initialize default OCIO config on first use
ColorManager::Init() (run by every Project construction) dereferenced
GetDefaultConfig() unconditionally. Any Project created before
SetUpDefaultConfig() crashed inside OCIO getCanonicalName on a null
config — the Windows CI SEGFAULT, where the suite order runs a
Project-creating test first. Reproduced locally by running
MainWindowLayoutInfo.AccessorsStoreAndRetrieve as the first suite.
2026-07-17 18:28:38 +08:00
Mike-Solar 9ca2b0e716 tests: skip MainWindow construction test where OpenGL is unavailable
The test forces GraphicsBackend=opengl and builds a full MainWindow
with QOpenGLWidget-based viewer panels. On headless platforms whose
QPA cannot provide a GL context (Ubuntu/Windows CI on offscreen), the
app's GL calls crash on a non-current context (SEGFAULT in CI on
both). Probe with QOpenGLContext+QOffscreenSurface and GTEST_SKIP when
GL is unusable; the test still runs on platforms with real GL.
2026-07-17 17:38:15 +08:00
Mike-Solar bbf3b2ca38 tests: include <cstring> for std::memset in oiio utils test
Only compiled on macOS via transitive includes; breaks on Linux/MSVC.
2026-07-17 17:26:52 +08:00
Mike-Solar 004e942cf9 fix: remaining issues recorded during test coverage work
- RenderManager: GPU-side members (context_, decoder_cache_,
  shader_cache_, auto_cacher_, worker_pool_, decoder_clear_timer_) were
  left uninitialized when the configured graphics backend is unknown
  (e.g. dummy); ViewerWidget then dereferenced garbage and crashed.
  Initialize them at declaration
- CurveView::SelectKeyframesOfInput ignored its reference parameter and
  selected keyframes of every connected track; select only the
  requested track's keyframes
- SeekableWidget::SeekToScenePoint dereferenced GetViewerNode()
  unconditionally; skip the playhead update when no viewer is connected
- LoadOTIOTask: unknown root schema leaked the freshly allocated
  project_ (delete + reset; OTIO is not enabled in local builds so this
  file is compile-verified by inspection only)

Locked by new tests: RenderManagerDummyBackend,
TimeRuler.SeekToScenePointWithoutViewerIsNoOp,
CurveViewTest.SelectKeyframesOfInputSelectsOnlyRequestedTrack
2026-07-17 17:20:50 +08:00
Mike-Solar 7223900b6d fix: initialize EncodingParams enum members
format_, video_codec_, audio_codec_, subtitle_sidecar_fmt_ and
subtitles_codec_ were left indeterminate by the constructor; reading
them before the corresponding Enable* call was UB. Initialize to the
kFormatCount/kCodecCount invalid sentinels.
2026-07-17 17:04:39 +08:00
Mike-Solar ba55123b13 tests: coverage for panels, main window, ratio dialog (44 cases)
- all 19 panel classes: construction, titles, context/signal wiring,
  save/load data round-trips
- MainWindowLayoutInfo XML round-trip, MainStatusBar, offscreen
  MainWindow construction with standard panels and menus
- RatioDialog parsing (decimal and : / ; separators), validation
2026-07-17 16:53:13 +08:00
Mike-Solar c812f6ea28 fix: panel/window bugs surfaced by new gtest coverage
- MainWindowLayoutInfo: toXml() iterated open_sequences_ for the
  <viewers> section and fromXml() never parsed it, so open footage
  viewers were lost on layout save/load
- PanelWidget never set QObject::objectName, so
  PanelManager::GetPanelWithName() always returned nullptr and every
  caller (layout restore, NodeView param panel lookup) was dead code
- PanelManager::DestroyInstance() left instance_ dangling (UAF on any
  later RegisterPanel), unlike the other singletons
2026-07-17 16:53:13 +08:00
Mike-Solar 0dae545cba tests: coverage for large widgets (61 cases)
- curve/keyframe views: connections, selection, undo commands
- time ruler / playback controls: time<->scene math, buttons, seek
- project explorer: view model hierarchy, MIME drag&drop, rename undo,
  folder heuristics, toolbar signals
- node table/tree/param views, task view, history, multicam play queue,
  timeline selections, node view scene
2026-07-17 15:48:03 +08:00
Mike-Solar b4db6ebc88 fix: define TimeRuler::SetCenteredText (declared but never defined)
Any caller would hit a link error; there were none, so the bug was
latent until the new widget tests called it.
2026-07-17 15:48:03 +08:00