/* 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 /extensions.json; los archivos instalados * en /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