Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ src/shadow-gaussian-lut.h: scripts/gen-shadow-lut.py
libtwin.a_files-$(CONFIG_LOGGING) += src/log.c
libtwin.a_files-$(CONFIG_CURSOR) += src/cursor.c
libtwin.a_files-y += src/memstats.c
libtwin.a_files-$(CONFIG_MEM_TLSF) += src/mem-tlsf.c

# Rendering backends
# Screen compositing operations (always needed for screen buffer management)
Expand Down
25 changes: 25 additions & 0 deletions configs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,31 @@ config MEMORY_STATS
malloc/free call. Useful for profiling memory consumption
on constrained devices.

config MEM_TLSF
bool "Use TLSF allocator for heap"
default n
help
Replace system malloc with the TLSF (Two-Level Segregated Fit)
allocator backed by a fixed-size memory pool. Provides O(1)
allocation and deallocation with bounded fragmentation, suitable
for real-time embedded targets without a system heap.

When disabled, the system malloc/free is used directly.

config MEM_TLSF_POOL_SIZE
int "TLSF pool size in bytes"
default 65536
range 4096 8388608
depends on MEM_TLSF
help
Size of the static memory pool backing the TLSF allocator.
Must be large enough for all runtime allocations including
pixmap buffers. A 640x480 ARGB32 screen pixmap alone needs
~1.2 MB; the SDL demo with multiple windows peaks at ~8 MB.
Embedded targets with small displays need far less (e.g.
320x240 RGB565 = ~150 KB framebuffer + widget overhead).
Requests exceeding pool capacity return NULL.

comment "Logging is disabled"
depends on !LOGGING

Expand Down
39 changes: 27 additions & 12 deletions include/twin.h
Original file line number Diff line number Diff line change
Expand Up @@ -644,26 +644,32 @@ typedef enum _twin_shape {
TwinShapeEllipse /**< Elliptical shape */
} twin_shape_t;

/**
* Optional widget attributes, lazily allocated on first use.
* Widgets that never register a callback or request focus pay zero
* RAM cost for these fields. Allocated on first callback/focus use.
*/
typedef struct _twin_widget_ext {
twin_widget_proc_t callback; /**< Application callback (optional) */
void *callback_data; /**< Callback user data */
bool want_focus; /**< Focus request flag */
} twin_widget_ext_t;

struct _twin_widget {
/* Widget hierarchy */
twin_window_t *window; /**< Parent window */
twin_widget_t *next; /**< Next sibling widget */
twin_box_t *parent; /**< Parent container */

/* Event handling:
* - handler: Framework event handler (processes paint, configure, etc.)
* - callback: Application callback (optional, receives button clicks, etc.)
*/
twin_widget_proc_t handler; /**< Widget event handler (framework) */
twin_widget_proc_t callback; /**< Application callback (optional) */
void *callback_data; /**< Callback user data */
twin_rect_t extents; /**< Current geometry */
twin_widget_t *copy_geom; /**< Geometry source widget */
/* Event handling */
twin_widget_proc_t handler; /**< Widget event handler (framework) */
twin_widget_ext_t *ext; /**< Optional attributes (lazy, may be NULL) */
twin_rect_t extents; /**< Current geometry */
twin_widget_t *copy_geom; /**< Geometry source widget (optional) */

/* Widget state */
bool paint; /**< Needs painting */
bool layout; /**< Needs layout */
bool want_focus; /**< Focus request flag */
bool paint; /**< Needs painting */
bool layout; /**< Needs layout */

/* Widget appearance */
twin_argb32_t background; /**< Background color */
Expand Down Expand Up @@ -753,6 +759,15 @@ void twin_widget_set_callback(twin_widget_t *widget,
twin_widget_proc_t callback,
void *data);

/**
* Set whether a widget should take focus on pointer interaction.
* @widget : Widget to configure
* @focusable : true to route key/UCS4 events to this widget after click
*
* This is primarily used by custom widgets that handle keyboard input.
*/
void twin_widget_set_focusable(twin_widget_t *widget, bool focusable);

/**
* Create default mouse cursor pixmap
* @hx : Output hotspot X coordinate
Expand Down
3 changes: 3 additions & 0 deletions src/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ extern twin_backend_t g_twin_backend;
*/
twin_context_t *twin_create(int width, int height)
{
/* Initialize the memory pool before any allocations occur. */
twin_mem_pool_init();

/* Runtime check for missing backend */
if (!g_twin_backend.init) {
log_error("Backend not registered - no init function");
Expand Down
15 changes: 8 additions & 7 deletions src/box.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ static twin_dispatch_result_t _twin_box_query_geometry(twin_box_t *box)
for (twin_widget_t *child = box->children; child; child = child->next) {
if (child->layout) {
ev.kind = TwinEventQueryGeometry;
child->handler(child, &ev, child->callback_data);
child->handler(child, &ev, _twin_widget_callback_data(child));
}
if (box->dir == TwinBoxHorz) {
preferred.width += child->preferred.width;
Expand Down Expand Up @@ -129,7 +129,7 @@ static twin_dispatch_result_t _twin_box_configure(twin_box_t *box)
extents.bottom != child->extents.bottom) {
ev.kind = TwinEventConfigure;
ev.u.configure.extents = extents;
child->handler(child, &ev, child->callback_data);
child->handler(child, &ev, _twin_widget_callback_data(child));
}
}
return TwinDispatchContinue;
Expand Down Expand Up @@ -167,7 +167,7 @@ twin_dispatch_result_t _twin_box_dispatch(twin_widget_t *widget,

/* Send destroy event to child */
ev.kind = TwinEventDestroy;
child->handler(child, &ev, child->callback_data);
child->handler(child, &ev, _twin_widget_callback_data(child));
}
break;
case TwinEventQueryGeometry:
Expand All @@ -178,7 +178,7 @@ twin_dispatch_result_t _twin_box_dispatch(twin_widget_t *widget,
twin_window_show(widget->window);
box->button_down =
_twin_box_xy_to_widget(box, event->u.pointer.x, event->u.pointer.y);
if (box->button_down && box->button_down->want_focus)
if (box->button_down && _twin_widget_want_focus(box->button_down))
box->focus = box->button_down;
fallthrough;
case TwinEventButtonUp:
Expand All @@ -188,15 +188,16 @@ twin_dispatch_result_t _twin_box_dispatch(twin_widget_t *widget,
ev = *event;
ev.u.pointer.x -= child->extents.left;
ev.u.pointer.y -= child->extents.top;
return child->handler(child, &ev, child->callback_data);
return child->handler(child, &ev,
_twin_widget_callback_data(child));
}
break;
case TwinEventKeyDown:
case TwinEventKeyUp:
case TwinEventUcs4:
if (box->focus)
return box->focus->handler(box->focus, event,
box->focus->callback_data);
_twin_widget_callback_data(box->focus));
break;
case TwinEventPaint:
box->widget.paint = false;
Expand All @@ -215,7 +216,7 @@ twin_dispatch_result_t _twin_box_dispatch(twin_widget_t *widget,
twin_pixmap_set_clip(pixmap, child->extents);
twin_pixmap_origin_to_clip(pixmap);
child->paint = false;
child->handler(child, event, child->callback_data);
child->handler(child, event, _twin_widget_callback_data(child));
twin_pixmap_restore_clip(pixmap, clip);
twin_pixmap_set_origin(pixmap, ox, oy);
}
Expand Down
11 changes: 6 additions & 5 deletions src/button.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,12 @@ twin_dispatch_result_t _twin_button_dispatch(twin_widget_t *widget,
_twin_button_set_label_offset(button);

/* Invoke widget callback for button press */
if (widget->callback) {
if (_twin_widget_callback(widget)) {
twin_event_t press_event = *event;
press_event.kind = TwinEventButtonSignalDown;
press_event.u.button_signal.signal = TwinButtonSignalDown;
(*widget->callback)(widget, &press_event, widget->callback_data);
_twin_widget_callback(widget)(widget, &press_event,
_twin_widget_callback_data(widget));
}
return TwinDispatchDone;
break;
Expand All @@ -72,12 +73,12 @@ twin_dispatch_result_t _twin_button_dispatch(twin_widget_t *widget,
_twin_button_set_label_offset(button);

/* Invoke widget callback for button release (click) */
if (widget->callback) {
if (_twin_widget_callback(widget)) {
twin_event_t release_event = *event;
release_event.kind = TwinEventButtonSignalUp;
release_event.u.button_signal.signal = TwinButtonSignalUp;
(*widget->callback)(widget, &release_event,
widget->callback_data);
_twin_widget_callback(widget)(
widget, &release_event, _twin_widget_callback_data(widget));
}
}
return TwinDispatchDone;
Expand Down
Loading