712 lines
32 KiB
Bash
Executable File
712 lines
32 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# new-platform — scaffold a new el-native platform bridge
|
|
#
|
|
# Usage: ./new-platform <platform-name>
|
|
#
|
|
# 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 <platform-name>" >&2
|
|
echo "" >&2
|
|
echo " <platform-name> lowercase identifier, e.g. myrtos, wayland, qt6" >&2
|
|
echo "" >&2
|
|
echo "This creates el_<platform-name>.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 <your-toolkit>) \\
|
|
* -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 <your-toolkit>) -ldl -lpthread
|
|
*/
|
|
|
|
#ifdef EL_TARGET_${PLATFORM_UPPER}
|
|
|
|
/* ── TODO: add platform-specific includes here ──────────────────────────────
|
|
* Examples:
|
|
* #include <gtk/gtk.h> // GTK4
|
|
* #include <SDL2/SDL.h> // SDL2
|
|
* #include "lvgl/lvgl.h" // LVGL
|
|
* #include <windows.h> // Win32
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <dlfcn.h> /* 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 <your-toolkit> 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 <your-toolkit> 2>/dev/null) \\
|
|
-ldl -lpthread
|
|
|
|
See PLATFORM_BRIDGE_SPEC.md for the full bridge contract and gotchas.
|
|
INSTRUCTIONS
|