Files
oak-editor/app/render/ipc/ipcmessage.h
T
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

162 lines
6.0 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/>.
***/
#ifndef OAK_IPC_IPCMESSAGE_H
#define OAK_IPC_IPCMESSAGE_H
#include <cstdint>
#include <QByteArray>
#include <QJsonObject>
#include <QString>
#include <QVector>
class QIODevice;
namespace olive
{
namespace ipc
{
/**
* @brief Control-plane protocol exchanged over stdio between main and render worker.
*
* The wire format is NDJSON: one compact QJsonObject per line, terminated by '\n'. This is
* deliberately human-readable so the channel can be inspected live with `tee`/`cat` and test
* messages can be injected by hand. The stdio channel carries only low-frequency control traffic;
* bulk pixel data travels through the shared-memory FrameSlotPool, and the (potentially large)
* serialized node graph travels via a temporary file referenced by path.
*
* Every message object has a "type" string field. Directionality (M = main, W = worker):
* "handshake" M<->W Negotiate protocol version and announce shared-memory key/geometry.
* "load_graph" M ->W Path to a temporary file holding the serialized node graph.
* "render_frame" M ->W Request a frame: node uuid, time, video params.
* "frame_ready" W ->M A rendered frame is published; carries the output-slot index + ticket.
* "cancel" M ->W Abandon an in-flight ticket by id.
* "graph_update" M ->W (Reserved, Phase 6) Incremental graph mutation, mirrors ProjectCopier.
* "shutdown" M ->W Finish current work and exit cleanly.
* "error" W ->M Worker-side failure report (human-readable "message" field).
*/
namespace msgtype
{
constexpr const char *k_handshake = "handshake";
constexpr const char *k_load_graph = "load_graph";
constexpr const char *k_render_frame = "render_frame";
constexpr const char *k_frame_ready = "frame_ready";
constexpr const char *k_cancel = "cancel";
constexpr const char *k_graph_update = "graph_update";
constexpr const char *k_shutdown = "shutdown";
constexpr const char *k_error = "error";
} // namespace msgtype
/**
* @brief Write one NDJSON message line to `device`.
*
* Serializes `obj` to compact JSON, appends '\n', and writes the whole line in one call. Returns
* true only if the full line was written.
*/
bool write_message(QIODevice *device, const QJsonObject &obj);
/**
* @brief Pull one complete NDJSON line out of `buffer` and parse it.
*
* If `buffer` contains at least one '\n', the leading line is removed, parsed as JSON, and returned
* via `out` (true). If no complete line is buffered yet, leaves `buffer` untouched and returns
* false. Malformed lines are skipped (removed) and reported via `*ok = false` so the reader can log
* and continue rather than wedge. Supports the typical "append bytes as they arrive, then drain
* complete lines" reader loop on a pipe.
*/
bool read_message(QByteArray *buffer, QJsonObject *out, bool *ok = nullptr);
// ---- Typed message builders / parsers -------------------------------------------------------
//
// Thin helpers that construct or read the QJsonObject for each message type, keeping field names in
// one place so main and worker agree. Fields use plain JSON numbers/strings; 64-bit ids are stored
// as JSON numbers (doubles exactly represent integers up to 2^53, ample for our counters).
struct HandshakeMsg {
int protocol_version = 0;
QString shm_key; ///< Worker->main output shared-memory segment key.
QString
input_shm_key; ///< Main->worker input shared-memory segment key (optional).
int input_slots = 0; ///< Number of main->worker input frame slots.
int output_slots = 0; ///< Number of worker->main output frame slots.
qint64 slot_data_bytes = 0; ///< Per-output-slot pixel block size.
qint64 input_slot_data_bytes = 0; ///< Per-input-slot pixel block size.
QJsonObject to_json() const;
static bool from_json(const QJsonObject &o, HandshakeMsg *out);
};
struct RenderFrameMsg {
qint64 ticket_id =
0; ///< Correlates this request with the eventual frame_ready.
QString
node_uuid; ///< Output/viewer node to render, by stable uuid in the loaded graph.
qint64 time_num = 0;
qint64 time_den = 1;
int width = 0; ///< Forced output size (0 = use graph default).
int height = 0;
int format = -1; ///< Forced PixelFormat::Format (-1 = default/INVALID).
int channel_count = 0; ///< 0 = default.
int mode = 0; ///< RenderMode::Mode.
int input_slot =
-1; ///< Optional main->worker decoded input slot for footage nodes.
QVector<int>
input_slots; ///< Optional ordered decoded input slots for footage nodes.
// Output color transform to apply before returning the frame. When empty,
// the worker returns the image in the project's reference space.
bool has_color_transform = false;
bool color_is_display = false;
QString color_output;
QString color_view;
QString color_look;
QJsonObject to_json() const;
static bool from_json(const QJsonObject &o, RenderFrameMsg *out);
};
struct FrameReadyMsg {
qint64 ticket_id = 0;
int output_slot = 0; ///< Index into the worker->main output FrameSlotPool.
QJsonObject to_json() const;
static bool from_json(const QJsonObject &o, FrameReadyMsg *out);
};
struct CancelMsg {
qint64 ticket_id = 0;
QJsonObject to_json() const;
static bool from_json(const QJsonObject &o, CancelMsg *out);
};
struct LoadGraphMsg {
QString path; ///< Temporary file holding the serialized node graph.
QJsonObject to_json() const;
static bool from_json(const QJsonObject &o, LoadGraphMsg *out);
};
} // namespace ipc
} // namespace olive
#endif // OAK_IPC_IPCMESSAGE_H