Files
2026-10-24 11:52:16 -03:00

122 lines
6.0 KiB
C

/* Extensiones nativas en C (y base de las de Lua).
*
* ADVERTENCIA DE SEGURIDAD: una extensión nativa es código que se ejecuta
* DENTRO del proceso de Tahgibu con sus mismos permisos. Los permisos
* (capabilities) de abajo controlan qué funciones del host se le ofrecen,
* pero NO pueden impedir que el código nativo lea/escriba memoria o
* archivos por su cuenta. Instala solo extensiones de origen confiable.
*
* Una extensión nativa es una biblioteca compartida (.so/.dll) que exporta:
* const uint32_t tgb_plugin_api_version; (usa TGB_PLUGIN_DECLARE_API_VERSION)
* tgb_status tgb_plugin_init(const tgb_host_api *host, tgb_plugin_handle *self,
* tgb_plugin_info *info);
* void tgb_plugin_shutdown(tgb_plugin_handle *self); (opcional)
*
* Las funciones de lectura/edición del host solo funcionan DENTRO de un
* comando (mientras Tahgibu ejecuta tgb_plugin_command_fn), nunca en init. */
#ifndef TAHGIBU_PLUGIN_H
#define TAHGIBU_PLUGIN_H
#include "tahgibu/document.h"
#ifdef __cplusplus
extern "C" {
#endif
#define TGB_PLUGIN_API_VERSION 1
#if defined(_WIN32)
#define TGB_PLUGIN_EXPORT __declspec(dllexport)
#else
#define TGB_PLUGIN_EXPORT __attribute__((visibility("default")))
#endif
/* Declara la versión de API con la que se compiló la extensión. Sin ella (o con otra) no se carga. */
#define TGB_PLUGIN_DECLARE_API_VERSION TGB_PLUGIN_EXPORT const uint32_t tgb_plugin_api_version = TGB_PLUGIN_API_VERSION
typedef enum tgb_capability {
TGB_CAP_READ_DOCUMENT = 1u << 0, /* leer dimensiones, fotogramas, píxeles */
TGB_CAP_EDIT_DOCUMENT = 1u << 1, /* modificar el documento (siempre con historial) */
TGB_CAP_EVENTS = 1u << 2 /* recibir eventos */
} tgb_capability;
typedef struct tgb_plugin_handle tgb_plugin_handle; /* identidad de la extensión (opaco) */
typedef struct tgb_plugin_info {
const char *name; /* nombre legible */
const char *version; /* p. ej. "1.0.0" */
uint32_t required_caps; /* combinación de tgb_capability */
} tgb_plugin_info;
/* Un comando recibe el documento activo a través del host (no directamente). */
typedef tgb_status (*tgb_plugin_command_fn)(tgb_plugin_handle *self, void *user);
typedef struct tgb_host_api {
uint32_t api_version; /* = TGB_PLUGIN_API_VERSION */
uint32_t struct_size; /* sizeof(tgb_host_api) del host */
uint32_t (*granted_caps)(tgb_plugin_handle *self);
void (*log)(tgb_plugin_handle *self, const char *message);
tgb_status (*register_command)(tgb_plugin_handle *self, const char *name,
tgb_plugin_command_fn fn, void *user);
/* TGB_CAP_EVENTS */
tgb_status (*subscribe)(tgb_plugin_handle *self, tgb_event_fn fn, void *user);
/* TGB_CAP_READ_DOCUMENT */
int (*doc_width)(tgb_plugin_handle *self);
int (*doc_height)(tgb_plugin_handle *self);
size_t (*doc_frame_count)(tgb_plugin_handle *self);
size_t (*doc_layer_count)(tgb_plugin_handle *self);
tgb_color (*get_pixel)(tgb_plugin_handle *self, size_t frame, size_t layer, int x, int y);
/* TGB_CAP_EDIT_DOCUMENT: todo lo editado entre begin y end es una operación deshacible */
tgb_status (*edit_begin)(tgb_plugin_handle *self, size_t frame, size_t layer);
tgb_status (*put_pixel)(tgb_plugin_handle *self, int x, int y, tgb_color color);
tgb_status (*fill_rect)(tgb_plugin_handle *self, tgb_rect r, tgb_color color);
tgb_status (*edit_end)(tgb_plugin_handle *self);
tgb_status (*add_frame)(tgb_plugin_handle *self, size_t index);
} tgb_host_api;
/* ---------------- Gestor de extensiones (lado de la aplicación) ---------------- */
typedef struct tgb_plugin_manager tgb_plugin_manager;
typedef struct tgb_plugin_entry_info {
char id[64];
char kind[8]; /* "native" o "lua" */
char file[256];
char name[64];
char version[32];
bool enabled;
bool loaded;
uint32_t granted_caps;
uint32_t required_caps;
char last_error[160];
} tgb_plugin_entry_info;
/* El registro vive en <config_dir>/extensions.json; los archivos instalados
* en <data_dir>/extensions/. NUNCA se carga nada que no esté en el registro
* y habilitado explícitamente. */
tgb_status tgb_plugin_manager_create(tgb_context *ctx, tgb_plugin_manager **out);
void tgb_plugin_manager_destroy(tgb_plugin_manager *mgr); /* descarga todo */
/* Copia el archivo a la carpeta de extensiones y lo registra DESHABILITADO. */
tgb_status tgb_plugin_install(tgb_plugin_manager *mgr, const char *source_path,
const char *id, size_t *out_index); /* id: [a-z0-9_-], 1..48; .lua = Lua */
/* granted_caps = permisos que el usuario concede. Falla si no cubren los requeridos. */
tgb_status tgb_plugin_enable(tgb_plugin_manager *mgr, size_t index, uint32_t granted_caps);
tgb_status tgb_plugin_disable(tgb_plugin_manager *mgr, size_t index);
tgb_status tgb_plugin_remove(tgb_plugin_manager *mgr, size_t index); /* borra archivo y registro */
size_t tgb_plugin_count(const tgb_plugin_manager *mgr);
tgb_status tgb_plugin_info_at(const tgb_plugin_manager *mgr, size_t index, tgb_plugin_entry_info *out);
/* OJO: los índices de comandos CAMBIAN al habilitar, deshabilitar o eliminar extensiones.
* Una interfaz debe guardar el NOMBRE ("id:comando") y ejecutar con tgb_plugin_run_command_named. */
size_t tgb_plugin_command_count(const tgb_plugin_manager *mgr);
tgb_status tgb_plugin_command_name(const tgb_plugin_manager *mgr, size_t cmd_index,
char *buf, size_t buf_size);
/* Ejecuta un comando sobre un documento como UNA operación deshacible.
* Si el comando falla, se deshace todo lo que hizo (el documento queda como estaba). */
tgb_status tgb_plugin_run_command(tgb_plugin_manager *mgr, size_t cmd_index, tgb_document *doc);
/* Igual, pero identificando el comando por su nombre estable "id:comando". TGB_ERR_NOT_FOUND si no existe. */
tgb_status tgb_plugin_run_command_named(tgb_plugin_manager *mgr, const char *full_name, tgb_document *doc);
#ifdef __cplusplus
}
#endif
#endif