blob: 325620e3ac73279808e338da7aa3a13ae7127387 [file]
#ifndef _RIVE_SCRIPT_BACKEND_HPP_
#define _RIVE_SCRIPT_BACKEND_HPP_
#include <stdint.h>
#include "rive/hit_result.hpp"
#include "rive/input/focusable.hpp"
#include "rive/math/vec2d.hpp"
#include <string>
#include <unordered_set>
namespace rive
{
class Artboard;
class DataValue;
class ListenerInvocation;
class RawPath;
class Renderer;
class ScriptedContext;
class ScriptedObject;
class ShapePaint;
class ViewModelInstanceValue;
/// The narrow waist between scripted core objects and a script execution
/// backend. Script values are held as opaque int refs minted and released by
/// the backend; ScriptedObject and friends never touch the engine directly.
/// Implementations: ScriptingVM (Luau) and, per the WAMR swap plan,
/// WasmScriptingVM.
class ScriptBackend
{
private:
float m_displayScale = 1.0f;
public:
virtual ~ScriptBackend();
/// ScriptedObject holds a non-owning ScriptBackend*. The backend tracks
/// which objects point at it so teardown can null those pointers before
/// cascading destruction reaches them.
void registerScriptedObject(ScriptedObject* object);
void unregisterScriptedObject(ScriptedObject* object);
virtual bool valid() const = 0;
virtual void releaseRef(int ref) = 0;
/// Device pixels per layout point of the presenting surface; the
/// embedder reports it and layout resize callbacks pass it through so
/// scripts can size render targets in pixels. A changed value
/// re-announces every scripted layout's size, since a display hop
/// changes scale without reflowing anything.
void displayScale(float scale);
float displayScale() const { return m_displayScale; }
/// The editor stores generator refs per context; backends without that
/// remap return the ref unchanged.
virtual int resolveGeneratorRef(int ref) const { return ref; }
/// Runs the script's generator: creates the object's Context, calls
/// generator(context), refs the returned table. Returns the self ref or 0
/// on failure; outContextRef/outContextPtr carry the created Context.
virtual int instantiate(int generatorRef,
ScriptedObject* object,
int* outContextRef,
ScriptedContext** outContextPtr) = 0;
enum class InitResult : uint8_t
{
/// init is optional and absent; the object counts as initialized.
notImplemented,
succeeded,
/// init errored or returned false / requested missing data; the
/// caller tears the instance down.
failed,
};
virtual InitResult callUserInit(ScriptedObject* object,
int selfRef,
int contextRef) = 0;
/// self:advance(elapsedSeconds) -> bool; false when not implemented or on
/// error.
virtual bool callAdvance(ScriptedObject* object,
int selfRef,
float elapsedSeconds) = 0;
virtual void callUpdate(ScriptedObject* object, int selfRef) = 0;
/// self[name](self), for triggers.
virtual void callTrigger(ScriptedObject* object,
int selfRef,
const char* name) = 0;
/// self[name](self, args...) -> number. Returns false when the method is
/// not implemented or errors, leaving outResult untouched so the caller's
/// fallback stands.
virtual bool callNumberMethod(ScriptedObject* object,
int selfRef,
const char* name,
const float* args,
size_t argCount,
float* outResult) = 0;
/// self[name](self) -> bool; false unless the call succeeds and returns
/// an actual boolean true.
virtual bool callBooleanMethod(ScriptedObject* object,
int selfRef,
const char* name) = 0;
/// Path effect protocol: self.update(self, pathData(source),
/// node(shapePaint)) -> pathData appended into outPath. False on error.
virtual bool callPathEffectUpdate(ScriptedObject* object,
int selfRef,
const RawPath& sourcePath,
const ShapePaint* shapePaint,
RawPath* outPath) = 0;
/// A script data value copied out of the engine before its holder can be
/// collected; kind none when the call produced nothing usable.
struct ScriptDataResult
{
enum class Kind : uint8_t
{
none,
number,
string,
boolean,
color,
};
Kind kind = Kind::none;
float number = 0.0f;
std::string string;
bool boolean = false;
int color = 0;
};
/// Data converter protocol: self[method](self, dataValue(input)).
/// Returns false when the method is not implemented, meaning the input
/// passes through unchanged.
virtual bool callDataConvert(ScriptedObject* object,
int selfRef,
const char* method,
DataValue* input,
ScriptDataResult* outResult) = 0;
/// Listener protocol: self.performAction(self, invocation) when defined,
/// else legacy self.perform(self, pointerEvent).
virtual void callListenerPerform(ScriptedObject* object,
int selfRef,
const ListenerInvocation& invocation) = 0;
/// Drawable protocol: self.draw(self, renderer(renderer)); the renderer
/// wrapper is scoped to this call. The caller owns save/transform/restore.
virtual void callDraw(ScriptedObject* object,
int selfRef,
Renderer* renderer) = 0;
/// Drawable protocol: gamepad connected/event/disconnected dispatch by
/// invocation kind; false when the method isn't implemented or the kind
/// carries no gamepad payload.
virtual bool callGamepadEvent(ScriptedObject* object,
int selfRef,
const char* method,
const ListenerInvocation& invocation) = 0;
/// Drawable protocol: self[method](self, pointerEvent(pointerId, local)).
/// False when not implemented; outResult carries the event's hit result.
virtual bool callPointerEvent(ScriptedObject* object,
int selfRef,
const char* method,
int pointerId,
Vec2D localPosition,
HitResult* outResult) = 0;
/// Drawable protocol: self.keyboardEvent(self, invocation) -> stop
/// propagation.
virtual bool callKeyboardEvent(ScriptedObject* object,
int selfRef,
Key key,
KeyModifiers modifiers,
bool isPressed,
bool isRepeat) = 0;
/// Drawable protocol: self.textEvent(self, invocation) -> stop
/// propagation.
virtual bool callTextEvent(ScriptedObject* object,
int selfRef,
const std::string& text) = 0;
/// Layout protocol: self.resize(self, size).
virtual void callLayoutResize(ScriptedObject* object,
int selfRef,
Vec2D size) = 0;
/// Layout protocol: self.measure(self) -> vector. False only when the
/// method is not implemented; outSize is written only when the call
/// returns an actual vector.
virtual bool callLayoutMeasure(ScriptedObject* object,
int selfRef,
Vec2D* outSize) = 0;
virtual void setInputBoolean(int selfRef, const char* name, bool value) = 0;
virtual void setInputNumber(int selfRef, const char* name, float value) = 0;
virtual void setInputUnsigned(int selfRef,
const char* name,
uint32_t value) = 0;
virtual void setInputString(int selfRef,
const char* name,
const char* value) = 0;
virtual void setInputArtboard(int selfRef,
const char* name,
ScriptedObject* object,
Artboard* artboard) = 0;
virtual void setInputViewModel(int selfRef,
const char* name,
ViewModelInstanceValue* value) = 0;
protected:
/// Nulls every registered object's backend pointer, ahead of engine
/// teardown cascades that would otherwise reach a freed backend.
void detachScriptedObjects();
private:
std::unordered_set<ScriptedObject*> m_scriptedObjects;
};
} // namespace rive
#endif