#!/usr/bin/env bash # new-platform — scaffold a new el-native platform bridge # # Usage: ./new-platform # # Example: # ./new-platform myplatform # # Creates el_myplatform.c with all 33 required __* functions stubbed out, # the ElWidget slot table, and the dlsym callback dispatcher. # # After running, follow the printed instructions to wire the bridge into # el_native_target.h and el_seed.c. # # See PLATFORM_BRIDGE_SPEC.md for the full bridge contract. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # ── Argument validation ─────────────────────────────────────────────────────── if [[ $# -lt 1 ]]; then echo "Usage: $0 " >&2 echo "" >&2 echo " lowercase identifier, e.g. myrtos, wayland, qt6" >&2 echo "" >&2 echo "This creates el_.c in the current directory." >&2 exit 1 fi PLATFORM_LOWER="${1,,}" # force lowercase PLATFORM_UPPER="${PLATFORM_LOWER^^}" # force uppercase # Validate: letters, digits, underscores only if [[ ! "${PLATFORM_LOWER}" =~ ^[a-z][a-z0-9_]*$ ]]; then echo "Error: platform name must start with a letter and contain only a-z, 0-9, _" >&2 exit 1 fi OUTPUT_FILE="${SCRIPT_DIR}/el_${PLATFORM_LOWER}.c" if [[ -f "${OUTPUT_FILE}" ]]; then echo "Error: ${OUTPUT_FILE} already exists." >&2 echo " Remove it first if you want to regenerate it." >&2 exit 1 fi # ── Generate the bridge file ────────────────────────────────────────────────── cat > "${OUTPUT_FILE}" << BRIDGE_FILE /* * el_${PLATFORM_LOWER}.c — el-native platform bridge for ${PLATFORM_UPPER}. * * Generated by new-platform. Replace the TODO stubs with real implementations. * See PLATFORM_BRIDGE_SPEC.md for the full contract. * * ── Slot system ────────────────────────────────────────────────────────────── * Every widget (window, button, label, container, image) is stored in a static * array of ElWidget structs. The el program holds an int64_t "handle" which is * a direct index into that array. Rules: * - Slot 0 is NEVER valid. Scans start at index 1. * - Handle -1 means "invalid" / "create failed". * - Maximum 4096 concurrent widgets. * - el_widget_get() returns NULL for 0, negative, out-of-range, or FREE slots. * * ── Callback ABI ───────────────────────────────────────────────────────────── * When a platform event fires (click, text change), call el_${PLATFORM_LOWER}_invoke_cb: * * el_${PLATFORM_LOWER}_invoke_cb(w->cb_click, slot_index, ""); * * The El callback signature: * fn handler(handle: Int, data: String) -> Void * compiles to: * void handler(int64_t handle, int64_t data) * where data is a const char* cast to int64_t (el String representation). * * ── Thread safety ──────────────────────────────────────────────────────────── * ALL platform UI calls must run on the main/UI thread. If your platform * delivers events on background threads (e.g., from a network callback that * updates a label), marshal to the main thread before calling any widget op. * * ── Compile ────────────────────────────────────────────────────────────────── * cc -DEL_TARGET_${PLATFORM_UPPER} \\ * \$(pkg-config --cflags ) \\ * -c el_${PLATFORM_LOWER}.c -o el_${PLATFORM_LOWER}.o * * ── Link ───────────────────────────────────────────────────────────────────── * cc el_${PLATFORM_LOWER}.o el_seed.o el_runtime.o -o myapp \\ * \$(pkg-config --libs ) -ldl -lpthread */ #ifdef EL_TARGET_${PLATFORM_UPPER} /* ── TODO: add platform-specific includes here ────────────────────────────── * Examples: * #include // GTK4 * #include // SDL2 * #include "lvgl/lvgl.h" // LVGL * #include // Win32 */ #include #include #include #include #include /* dlsym — replace with GetProcAddress on Windows */ /* ── Widget table ─────────────────────────────────────────────────────────── */ #define EL_${PLATFORM_UPPER}_MAX_WIDGETS 4096 typedef enum { EL_WIDGET_FREE = 0, EL_WIDGET_WINDOW = 1, EL_WIDGET_VSTACK = 2, EL_WIDGET_HSTACK = 3, EL_WIDGET_ZSTACK = 4, EL_WIDGET_SCROLL = 5, EL_WIDGET_LABEL = 6, EL_WIDGET_BUTTON = 7, EL_WIDGET_TEXTFIELD = 8, EL_WIDGET_TEXTAREA = 9, EL_WIDGET_IMAGE = 10, } ElWidgetKind; typedef struct { ElWidgetKind kind; /* TODO: add your platform's native widget reference here. * Examples: * GtkWidget* widget; // GTK4 * SDL_Rect rect; // SDL2 (no object, just geometry) * lv_obj_t* obj; // LVGL * HWND hwnd; // Win32 * void* native; // generic pointer */ void* native; /* platform widget reference — replace as needed */ /* Text content (cached for platforms that don't provide a get-text API) */ char* text; /* Foreground / background color (RGBA, 0.0-1.0) */ float fg_r, fg_g, fg_b, fg_a; float bg_r, bg_g, bg_b, bg_a; /* Geometry */ int width; /* 0 = not set */ int height; /* 0 = not set */ int flex; /* 0 = hug content, >0 = expand */ /* Padding (top, right, bottom, left) */ int pad_top, pad_right, pad_bottom, pad_left; /* Corner radius */ int corner_radius; /* State */ int disabled; /* 0 = enabled, 1 = disabled */ int hidden; /* 0 = visible, 1 = hidden */ /* Event callbacks — El function name resolved at event time via dlsym */ char* cb_click; /* on_click / on_submit */ char* cb_change; /* on_change */ } ElWidget; static ElWidget _el_widgets[EL_${PLATFORM_UPPER}_MAX_WIDGETS]; /* ── Slot management ──────────────────────────────────────────────────────── */ static int64_t el_widget_alloc(ElWidgetKind kind, void* native) { for (int i = 1; i < EL_${PLATFORM_UPPER}_MAX_WIDGETS; i++) { if (_el_widgets[i].kind == EL_WIDGET_FREE) { memset(&_el_widgets[i], 0, sizeof(ElWidget)); _el_widgets[i].kind = kind; _el_widgets[i].native = native; return (int64_t)i; } } fprintf(stderr, "el_${PLATFORM_LOWER}: widget table full (max %d)\n", EL_${PLATFORM_UPPER}_MAX_WIDGETS); return -1; } static ElWidget* el_widget_get(int64_t handle) { if (handle <= 0 || handle >= EL_${PLATFORM_UPPER}_MAX_WIDGETS) return NULL; if (_el_widgets[handle].kind == EL_WIDGET_FREE) return NULL; return &_el_widgets[handle]; } static void el_widget_free(int64_t handle) { ElWidget* w = el_widget_get(handle); if (!w) return; /* TODO: release w->native (toolkit-specific) */ free(w->text); free(w->cb_click); free(w->cb_change); memset(w, 0, sizeof(ElWidget)); /* sets kind = EL_WIDGET_FREE (0) */ } /* ── Callback dispatcher ─────────────────────────────────────────────────── */ /* * Invoke an El function by symbol name. The El function must have the * compiled signature: void fn(int64_t handle, int64_t data) * where data is a const char* cast to int64_t (el String representation). * * On platforms without dlsym (e.g., Windows), replace with: * GetProcAddress(GetModuleHandle(NULL), fn_name) * On embedded targets without dynamic linking, maintain a manual symbol table. */ typedef void (*ElCb2)(int64_t handle, int64_t data); static void el_${PLATFORM_LOWER}_invoke_cb(const char* fn_name, int64_t handle, const char* data) { if (!fn_name || !*fn_name) return; void* sym = dlsym(RTLD_DEFAULT, fn_name); if (!sym) { fprintf(stderr, "el_${PLATFORM_LOWER}: callback symbol not found: %s\n", fn_name); return; } ElCb2 fn = (ElCb2)sym; fn(handle, (int64_t)(uintptr_t)(data ? data : "")); } /* ── Lifecycle ────────────────────────────────────────────────────────────── */ /* * el_${PLATFORM_LOWER}_init — initialize the platform toolkit. * Must be idempotent (safe to call more than once). * Called once from __native_init before any widget creation. */ void el_${PLATFORM_LOWER}_init(void) { static int done = 0; if (done) return; done = 1; /* TODO: initialize your platform toolkit here. * Examples: * gtk_init(NULL, NULL); // GTK4 * SDL_Init(SDL_INIT_VIDEO); // SDL2 * lv_init(); // LVGL */ } /* * el_${PLATFORM_LOWER}_run_loop — start the platform event loop. * On most platforms this NEVER returns. Exceptions: Android (no-op). * Must be called from the main thread. */ void el_${PLATFORM_LOWER}_run_loop(void) { /* TODO: start the platform event/render loop. * Examples: * gtk_main(); // GTK4 * while (1) { SDL_PollEvent(...); render(); SDL_Delay(16); } // SDL2 * while (1) { lv_task_handler(); usleep(5000); } // LVGL */ } /* ── Window ───────────────────────────────────────────────────────────────── */ int64_t el_${PLATFORM_LOWER}_window_create(const char* title, int w, int h, int mw, int mh) { /* TODO: create a top-level window. * title may be NULL — treat as "". * mw/mh are minimum dimensions (0 = no minimum). * Return slot handle on success, -1 on failure. */ (void)title; (void)w; (void)h; (void)mw; (void)mh; return -1; /* TODO: implement */ } void el_${PLATFORM_LOWER}_window_show(int64_t handle) { ElWidget* w = el_widget_get(handle); if (!w) return; /* TODO: make the window visible. */ } void el_${PLATFORM_LOWER}_window_set_title(int64_t handle, const char* title) { ElWidget* w = el_widget_get(handle); if (!w) return; /* TODO: update the window title. title may be NULL — treat as "". */ (void)title; } /* ── Layout containers ────────────────────────────────────────────────────── */ int64_t el_${PLATFORM_LOWER}_vstack_create(int spacing) { /* TODO: create a vertical linear container with the given spacing (px). */ (void)spacing; return -1; /* TODO: implement */ } int64_t el_${PLATFORM_LOWER}_hstack_create(int spacing) { /* TODO: create a horizontal linear container with the given spacing (px). */ (void)spacing; return -1; /* TODO: implement */ } int64_t el_${PLATFORM_LOWER}_zstack_create(void) { /* TODO: create a z-axis overlay container (children overlap). */ return -1; /* TODO: implement */ } int64_t el_${PLATFORM_LOWER}_scroll_create(void) { /* TODO: create a scrollable container (vertical scroll, first child = content). */ return -1; /* TODO: implement */ } /* ── Leaf widgets ─────────────────────────────────────────────────────────── */ int64_t el_${PLATFORM_LOWER}_label_create(const char* text) { /* TODO: create a non-editable text label. text may be NULL — treat as "". */ (void)text; return -1; /* TODO: implement */ } int64_t el_${PLATFORM_LOWER}_button_create(const char* label) { /* TODO: create a clickable button with the given label text. * Wire up the platform event handler so on_click callbacks fire later. */ (void)label; return -1; /* TODO: implement */ } int64_t el_${PLATFORM_LOWER}_text_field_create(const char* placeholder) { /* TODO: create a single-line text input. placeholder may be NULL. */ (void)placeholder; return -1; /* TODO: implement */ } int64_t el_${PLATFORM_LOWER}_text_area_create(const char* placeholder) { /* TODO: create a multi-line text input (scrollable). placeholder may be NULL. */ (void)placeholder; return -1; /* TODO: implement */ } int64_t el_${PLATFORM_LOWER}_image_create(const char* path_or_name) { /* TODO: create an image widget. * Try path_or_name as a filesystem path first, then as a named resource. * If neither resolves, create an empty image widget (do not crash). */ (void)path_or_name; return -1; /* TODO: implement */ } /* ── Widget property setters ─────────────────────────────────────────────── */ void el_${PLATFORM_LOWER}_widget_set_text(int64_t handle, const char* text) { ElWidget* w = el_widget_get(handle); if (!w) return; /* TODO: update text on label, button, text field, text area, or window title. * Dispatch on w->kind. text may be NULL — treat as "". * Cache in w->text if the platform has no get-text API. */ free(w->text); w->text = strdup(text ? text : ""); } const char* el_${PLATFORM_LOWER}_widget_get_text(int64_t handle) { ElWidget* w = el_widget_get(handle); if (!w) return ""; /* TODO: return the current text of the widget. * If the platform provides a get-text API, use it. * Otherwise return w->text (populated by set_text). * NEVER return NULL — return "" on failure. */ return w->text ? w->text : ""; } void el_${PLATFORM_LOWER}_widget_set_color(int64_t handle, float r, float g, float b, float a) { ElWidget* w = el_widget_get(handle); if (!w) return; w->fg_r = r; w->fg_g = g; w->fg_b = b; w->fg_a = a; /* TODO: apply foreground (text) color to the platform widget. */ } void el_${PLATFORM_LOWER}_widget_set_bg_color(int64_t handle, float r, float g, float b, float a) { ElWidget* w = el_widget_get(handle); if (!w) return; w->bg_r = r; w->bg_g = g; w->bg_b = b; w->bg_a = a; /* TODO: apply background color to the platform widget. */ } void el_${PLATFORM_LOWER}_widget_set_font(int64_t handle, const char* family, int size, int bold) { ElWidget* w = el_widget_get(handle); if (!w) return; /* TODO: set font on text-bearing widgets (label, button, text field, text area). * family may be "system" — use the platform default font in that case. * Fall back to system font if family is not found. */ (void)family; (void)size; (void)bold; } void el_${PLATFORM_LOWER}_widget_set_padding(int64_t handle, int top, int right, int bottom, int left) { ElWidget* w = el_widget_get(handle); if (!w) return; w->pad_top = top; w->pad_right = right; w->pad_bottom = bottom; w->pad_left = left; /* TODO: apply padding/insets to the platform container or text widget. */ } void el_${PLATFORM_LOWER}_widget_set_width(int64_t handle, int width) { ElWidget* w = el_widget_get(handle); if (!w) return; w->width = width; /* TODO: apply a fixed-width constraint to the platform widget. */ } void el_${PLATFORM_LOWER}_widget_set_height(int64_t handle, int height) { ElWidget* w = el_widget_get(handle); if (!w) return; w->height = height; /* TODO: apply a fixed-height constraint to the platform widget. */ } void el_${PLATFORM_LOWER}_widget_set_flex(int64_t handle, int flex) { ElWidget* w = el_widget_get(handle); if (!w) return; w->flex = flex; /* TODO: set the flex/expand factor. * flex > 0 → widget expands to fill available space. * flex == 0 → widget hugs content size. * Maps to: content hugging priority (AppKit), hexpand/vexpand (GTK4), * layout_weight (Android), stretch factor (SDL2 manual layout). */ } void el_${PLATFORM_LOWER}_widget_set_corner_radius(int64_t handle, int radius) { ElWidget* w = el_widget_get(handle); if (!w) return; w->corner_radius = radius; /* TODO: apply rounded corners (requires GPU layer / backing surface on most platforms). */ } void el_${PLATFORM_LOWER}_widget_set_disabled(int64_t handle, int disabled) { ElWidget* w = el_widget_get(handle); if (!w) return; w->disabled = disabled; /* TODO: enable or disable the widget (buttons, text fields). No-op for containers/labels. */ } void el_${PLATFORM_LOWER}_widget_set_hidden(int64_t handle, int hidden) { ElWidget* w = el_widget_get(handle); if (!w) return; w->hidden = hidden; /* TODO: show or hide the widget. * hidden=1 → invisible but still occupies layout space (visibility:hidden semantics). * For windows: minimize or hide. */ } /* ── Tree management ─────────────────────────────────────────────────────── */ void el_${PLATFORM_LOWER}_widget_add_child(int64_t parent, int64_t child) { ElWidget* pw = el_widget_get(parent); ElWidget* cw = el_widget_get(child); if (!pw || !cw) return; /* TODO: attach child to parent. Dispatch on pw->kind: * EL_WIDGET_WINDOW → add to root content view/container * EL_WIDGET_VSTACK → add as vertical child * EL_WIDGET_HSTACK → add as horizontal child * EL_WIDGET_ZSTACK → add as overlapping subview * EL_WIDGET_SCROLL → set as the scrollable content view (first child only) * default → add as plain subview * Do NOT add a window widget as a child. */ (void)pw; (void)cw; } void el_${PLATFORM_LOWER}_widget_remove_child(int64_t parent, int64_t child) { ElWidget* pw = el_widget_get(parent); ElWidget* cw = el_widget_get(child); if (!pw || !cw) return; /* TODO: detach child from parent. The child slot remains allocated (not destroyed). */ (void)pw; (void)cw; } void el_${PLATFORM_LOWER}_widget_destroy(int64_t handle) { ElWidget* w = el_widget_get(handle); if (!w) return; /* TODO: remove the widget from its parent/superview. * For windows: close the window. * Free any platform delegate/target objects stored in side tables. * Then free the slot: */ el_widget_free(handle); } /* ── Event registration ───────────────────────────────────────────────────── */ void el_${PLATFORM_LOWER}_widget_on_click(int64_t handle, const char* fn_name) { ElWidget* w = el_widget_get(handle); if (!w) return; free(w->cb_click); w->cb_click = (fn_name && *fn_name) ? strdup(fn_name) : NULL; } void el_${PLATFORM_LOWER}_widget_on_change(int64_t handle, const char* fn_name) { ElWidget* w = el_widget_get(handle); if (!w) return; free(w->cb_change); w->cb_change = (fn_name && *fn_name) ? strdup(fn_name) : NULL; } void el_${PLATFORM_LOWER}_widget_on_submit(int64_t handle, const char* fn_name) { /* Submit (Enter key in text field) reuses the cb_click slot, matching AppKit convention. */ el_${PLATFORM_LOWER}_widget_on_click(handle, fn_name); } /* ── Example event handler (adapt to your platform's callback mechanism) ─── */ /* * When a button is clicked in your platform event loop, call: * * ElWidget* w = el_widget_get(slot_index); * if (w && w->cb_click) { * el_${PLATFORM_LOWER}_invoke_cb(w->cb_click, slot_index, ""); * } * * When a text field changes: * * ElWidget* w = el_widget_get(slot_index); * if (w && w->cb_change) { * el_${PLATFORM_LOWER}_invoke_cb(w->cb_change, slot_index, current_text); * } */ #endif /* EL_TARGET_${PLATFORM_UPPER} */ BRIDGE_FILE chmod 644 "${OUTPUT_FILE}" # ── Print next-steps instructions ──────────────────────────────────────────── UPPER="${PLATFORM_UPPER}" LOWER="${PLATFORM_LOWER}" cat << INSTRUCTIONS Created: el_${LOWER}.c Next steps: ──────────────────────────────────────────────────────────────────────────── 1. Add to el_native_target.h (inside a new #ifdef EL_TARGET_${UPPER} block): #ifdef EL_TARGET_${UPPER} void __native_init(void); void __native_run_loop(void); el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height); void __window_show(el_val_t handle); void __window_set_title(el_val_t handle, el_val_t title); el_val_t __vstack_create(el_val_t spacing); el_val_t __hstack_create(el_val_t spacing); el_val_t __zstack_create(void); el_val_t __scroll_create(void); el_val_t __label_create(el_val_t text); el_val_t __button_create(el_val_t label); el_val_t __text_field_create(el_val_t placeholder); el_val_t __text_area_create(el_val_t placeholder); el_val_t __image_create(el_val_t path_or_name); void __widget_set_text(el_val_t handle, el_val_t text); el_val_t __widget_get_text(el_val_t handle); void __widget_set_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a); void __widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a); void __widget_set_font(el_val_t handle, el_val_t family, el_val_t size, el_val_t bold); void __widget_set_padding(el_val_t handle, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left); void __widget_set_width(el_val_t handle, el_val_t width); void __widget_set_height(el_val_t handle, el_val_t height); void __widget_set_flex(el_val_t handle, el_val_t flex); void __widget_set_corner_radius(el_val_t handle, el_val_t radius); void __widget_set_disabled(el_val_t handle, el_val_t disabled); void __widget_set_hidden(el_val_t handle, el_val_t hidden); void __widget_add_child(el_val_t parent, el_val_t child); void __widget_remove_child(el_val_t parent, el_val_t child); void __widget_destroy(el_val_t handle); void __widget_on_click(el_val_t handle, el_val_t fn_name); void __widget_on_change(el_val_t handle, el_val_t fn_name); void __widget_on_submit(el_val_t handle, el_val_t fn_name); el_val_t __manifest_read(el_val_t path); #endif /* EL_TARGET_${UPPER} */ ──────────────────────────────────────────────────────────────────────────── 2. Add to el_seed.c (copy the EL_TARGET_MACOS block as a template and substitute el_appkit_ → el_${LOWER}_): #ifdef EL_TARGET_${UPPER} /* Forward declarations — implemented in el_${LOWER}.c */ extern void el_${LOWER}_init(void); extern void el_${LOWER}_run_loop(void); extern int64_t el_${LOWER}_window_create(const char* title, int w, int h, int mw, int mh); extern void el_${LOWER}_window_show(int64_t handle); extern void el_${LOWER}_window_set_title(int64_t handle, const char* title); extern int64_t el_${LOWER}_vstack_create(int spacing); extern int64_t el_${LOWER}_hstack_create(int spacing); extern int64_t el_${LOWER}_zstack_create(void); extern int64_t el_${LOWER}_scroll_create(void); extern int64_t el_${LOWER}_label_create(const char* text); extern int64_t el_${LOWER}_button_create(const char* label); extern int64_t el_${LOWER}_text_field_create(const char* placeholder); extern int64_t el_${LOWER}_text_area_create(const char* placeholder); extern int64_t el_${LOWER}_image_create(const char* path_or_name); extern void el_${LOWER}_widget_set_text(int64_t handle, const char* text); extern const char* el_${LOWER}_widget_get_text(int64_t handle); extern void el_${LOWER}_widget_set_color(int64_t h, float r, float g, float b, float a); extern void el_${LOWER}_widget_set_bg_color(int64_t h, float r, float g, float b, float a); extern void el_${LOWER}_widget_set_font(int64_t h, const char* family, int size, int bold); extern void el_${LOWER}_widget_set_padding(int64_t h, int top, int right, int bottom, int left); extern void el_${LOWER}_widget_set_width(int64_t h, int width); extern void el_${LOWER}_widget_set_height(int64_t h, int height); extern void el_${LOWER}_widget_set_flex(int64_t h, int flex); extern void el_${LOWER}_widget_set_corner_radius(int64_t h, int radius); extern void el_${LOWER}_widget_set_disabled(int64_t h, int disabled); extern void el_${LOWER}_widget_set_hidden(int64_t h, int hidden); extern void el_${LOWER}_widget_add_child(int64_t parent, int64_t child); extern void el_${LOWER}_widget_remove_child(int64_t parent, int64_t child); extern void el_${LOWER}_widget_destroy(int64_t handle); extern void el_${LOWER}_widget_on_click(int64_t h, const char* fn_name); extern void el_${LOWER}_widget_on_change(int64_t h, const char* fn_name); extern void el_${LOWER}_widget_on_submit(int64_t h, const char* fn_name); void __native_init(void) { el_${LOWER}_init(); } void __native_run_loop(void) { el_${LOWER}_run_loop(); } el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height) { return (el_val_t)el_${LOWER}_window_create( EL_CSTR(title), (int)(int64_t)width, (int)(int64_t)height, (int)(int64_t)min_width, (int)(int64_t)min_height); } void __window_show(el_val_t h) { el_${LOWER}_window_show((int64_t)h); } void __window_set_title(el_val_t h, el_val_t t) { el_${LOWER}_window_set_title((int64_t)h, EL_CSTR(t)); } el_val_t __vstack_create(el_val_t s) { return (el_val_t)el_${LOWER}_vstack_create((int)(int64_t)s); } el_val_t __hstack_create(el_val_t s) { return (el_val_t)el_${LOWER}_hstack_create((int)(int64_t)s); } el_val_t __zstack_create(void) { return (el_val_t)el_${LOWER}_zstack_create(); } el_val_t __scroll_create(void) { return (el_val_t)el_${LOWER}_scroll_create(); } el_val_t __label_create(el_val_t t) { return (el_val_t)el_${LOWER}_label_create(EL_CSTR(t)); } el_val_t __button_create(el_val_t l) { return (el_val_t)el_${LOWER}_button_create(EL_CSTR(l)); } el_val_t __text_field_create(el_val_t p) { return (el_val_t)el_${LOWER}_text_field_create(EL_CSTR(p)); } el_val_t __text_area_create(el_val_t p) { return (el_val_t)el_${LOWER}_text_area_create(EL_CSTR(p)); } el_val_t __image_create(el_val_t p) { return (el_val_t)el_${LOWER}_image_create(EL_CSTR(p)); } void __widget_set_text(el_val_t h, el_val_t t) { el_${LOWER}_widget_set_text((int64_t)h, EL_CSTR(t)); } el_val_t __widget_get_text(el_val_t h) { const char* s = el_${LOWER}_widget_get_text((int64_t)h); return EL_STR(s ? s : ""); } void __widget_set_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) { el_${LOWER}_widget_set_color((int64_t)h, (float)el_to_float(r), (float)el_to_float(g), (float)el_to_float(b), (float)el_to_float(a)); } void __widget_set_bg_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) { el_${LOWER}_widget_set_bg_color((int64_t)h, (float)el_to_float(r), (float)el_to_float(g), (float)el_to_float(b), (float)el_to_float(a)); } void __widget_set_font(el_val_t h, el_val_t family, el_val_t size, el_val_t bold) { el_${LOWER}_widget_set_font((int64_t)h, EL_CSTR(family), (int)(int64_t)size, (int)(int64_t)bold); } void __widget_set_padding(el_val_t h, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left) { el_${LOWER}_widget_set_padding((int64_t)h, (int)(int64_t)top, (int)(int64_t)right, (int)(int64_t)bottom, (int)(int64_t)left); } void __widget_set_width(el_val_t h, el_val_t w) { el_${LOWER}_widget_set_width((int64_t)h, (int)(int64_t)w); } void __widget_set_height(el_val_t h, el_val_t ht) { el_${LOWER}_widget_set_height((int64_t)h, (int)(int64_t)ht); } void __widget_set_flex(el_val_t h, el_val_t f) { el_${LOWER}_widget_set_flex((int64_t)h, (int)(int64_t)f); } void __widget_set_corner_radius(el_val_t h, el_val_t r) { el_${LOWER}_widget_set_corner_radius((int64_t)h, (int)(int64_t)r); } void __widget_set_disabled(el_val_t h, el_val_t d) { el_${LOWER}_widget_set_disabled((int64_t)h, (int)(int64_t)d); } void __widget_set_hidden(el_val_t h, el_val_t hid) { el_${LOWER}_widget_set_hidden((int64_t)h, (int)(int64_t)hid); } void __widget_add_child(el_val_t p, el_val_t c) { el_${LOWER}_widget_add_child((int64_t)p, (int64_t)c); } void __widget_remove_child(el_val_t p, el_val_t c) { el_${LOWER}_widget_remove_child((int64_t)p, (int64_t)c); } void __widget_destroy(el_val_t h) { el_${LOWER}_widget_destroy((int64_t)h); } void __widget_on_click(el_val_t h, el_val_t fn) { el_${LOWER}_widget_on_click((int64_t)h, EL_CSTR(fn)); } void __widget_on_change(el_val_t h, el_val_t fn) { el_${LOWER}_widget_on_change((int64_t)h, EL_CSTR(fn)); } void __widget_on_submit(el_val_t h, el_val_t fn) { el_${LOWER}_widget_on_submit((int64_t)h, EL_CSTR(fn)); } #endif /* EL_TARGET_${UPPER} */ ──────────────────────────────────────────────────────────────────────────── 3. Implement each TODO in el_${LOWER}.c. 4. Compile: cc -DEL_TARGET_${UPPER} -Wall \\ \$(pkg-config --cflags 2>/dev/null) \\ -c el_${LOWER}.c -o el_${LOWER}.o 5. Link: cc el_${LOWER}.o el_seed.o el_runtime.o -o myapp \\ \$(pkg-config --libs 2>/dev/null) \\ -ldl -lpthread See PLATFORM_BRIDGE_SPEC.md for the full bridge contract and gotchas. INSTRUCTIONS