Skip to content

Commit f224621

Browse files
committed
Integrate LSF allocator and lazy widget extension
This introduces a twin_raw_* indirection layer between the memory stats tracking (memstats.c) and the underlying allocator. When CONFIG_MEM_TLSF is enabled, allocations route through a self-contained TLSF (Two-Level Segregated Fit) allocator backed by a static pool, providing O(1) bounded-time malloc/free for real-time embedded targets. When disabled, twin_raw_* inlines to libc malloc/free with zero overhead. The TLSF implementation is stripped to static-pool essentials (pool_init, malloc, realloc, free) and uses the portable twin_clz/twin_clzll helpers instead of raw compiler intrinsics. A key deviation from upstream TLSF: block_find_free() trims to the actual request size rather than the bin minimum, avoiding catastrophic internal fragmentation when small requests hit large bins. Move optional widget fields (callback, callback_data, want_focus) into a lazily-allocated twin_widget_ext_t block. Widgets that never register a callback keep ext == NULL and save 16 bytes per widget (64-bit) or 8 bytes (32-bit). The copy_geom field stays inline in the base struct for app-level direct access. All call sites in box.c, button.c, and widget.c updated to use inline accessors.
1 parent e116362 commit f224621

10 files changed

Lines changed: 714 additions & 60 deletions

File tree

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ src/shadow-gaussian-lut.h: scripts/gen-shadow-lut.py
9292
libtwin.a_files-$(CONFIG_LOGGING) += src/log.c
9393
libtwin.a_files-$(CONFIG_CURSOR) += src/cursor.c
9494
libtwin.a_files-y += src/memstats.c
95+
libtwin.a_files-$(CONFIG_MEM_TLSF) += src/mem-tlsf.c
9596

9697
# Rendering backends
9798
# Screen compositing operations (always needed for screen buffer management)

configs/Kconfig

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,31 @@ config MEMORY_STATS
180180
malloc/free call. Useful for profiling memory consumption
181181
on constrained devices.
182182

183+
config MEM_TLSF
184+
bool "Use TLSF allocator for heap"
185+
default n
186+
help
187+
Replace system malloc with the TLSF (Two-Level Segregated Fit)
188+
allocator backed by a fixed-size memory pool. Provides O(1)
189+
allocation and deallocation with bounded fragmentation, suitable
190+
for real-time embedded targets without a system heap.
191+
192+
When disabled, the system malloc/free is used directly.
193+
194+
config MEM_TLSF_POOL_SIZE
195+
int "TLSF pool size in bytes"
196+
default 65536
197+
range 4096 8388608
198+
depends on MEM_TLSF
199+
help
200+
Size of the static memory pool backing the TLSF allocator.
201+
Must be large enough for all runtime allocations including
202+
pixmap buffers. A 640x480 ARGB32 screen pixmap alone needs
203+
~1.2 MB; the SDL demo with multiple windows peaks at ~8 MB.
204+
Embedded targets with small displays need far less (e.g.
205+
320x240 RGB565 = ~150 KB framebuffer + widget overhead).
206+
Requests exceeding pool capacity return NULL.
207+
183208
comment "Logging is disabled"
184209
depends on !LOGGING
185210

include/twin.h

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -644,26 +644,32 @@ typedef enum _twin_shape {
644644
TwinShapeEllipse /**< Elliptical shape */
645645
} twin_shape_t;
646646

647+
/**
648+
* Optional widget attributes, lazily allocated on first use.
649+
* Widgets that never register a callback or request focus pay zero
650+
* RAM cost for these fields. Allocated on first callback/focus use.
651+
*/
652+
typedef struct _twin_widget_ext {
653+
twin_widget_proc_t callback; /**< Application callback (optional) */
654+
void *callback_data; /**< Callback user data */
655+
bool want_focus; /**< Focus request flag */
656+
} twin_widget_ext_t;
657+
647658
struct _twin_widget {
648659
/* Widget hierarchy */
649660
twin_window_t *window; /**< Parent window */
650661
twin_widget_t *next; /**< Next sibling widget */
651662
twin_box_t *parent; /**< Parent container */
652663

653-
/* Event handling:
654-
* - handler: Framework event handler (processes paint, configure, etc.)
655-
* - callback: Application callback (optional, receives button clicks, etc.)
656-
*/
657-
twin_widget_proc_t handler; /**< Widget event handler (framework) */
658-
twin_widget_proc_t callback; /**< Application callback (optional) */
659-
void *callback_data; /**< Callback user data */
660-
twin_rect_t extents; /**< Current geometry */
661-
twin_widget_t *copy_geom; /**< Geometry source widget */
664+
/* Event handling */
665+
twin_widget_proc_t handler; /**< Widget event handler (framework) */
666+
twin_widget_ext_t *ext; /**< Optional attributes (lazy, may be NULL) */
667+
twin_rect_t extents; /**< Current geometry */
668+
twin_widget_t *copy_geom; /**< Geometry source widget (optional) */
662669

663670
/* Widget state */
664-
bool paint; /**< Needs painting */
665-
bool layout; /**< Needs layout */
666-
bool want_focus; /**< Focus request flag */
671+
bool paint; /**< Needs painting */
672+
bool layout; /**< Needs layout */
667673

668674
/* Widget appearance */
669675
twin_argb32_t background; /**< Background color */
@@ -753,6 +759,15 @@ void twin_widget_set_callback(twin_widget_t *widget,
753759
twin_widget_proc_t callback,
754760
void *data);
755761

762+
/**
763+
* Set whether a widget should take focus on pointer interaction.
764+
* @widget : Widget to configure
765+
* @focusable : true to route key/UCS4 events to this widget after click
766+
*
767+
* This is primarily used by custom widgets that handle keyboard input.
768+
*/
769+
void twin_widget_set_focusable(twin_widget_t *widget, bool focusable);
770+
756771
/**
757772
* Create default mouse cursor pixmap
758773
* @hx : Output hotspot X coordinate

src/api.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ extern twin_backend_t g_twin_backend;
2222
*/
2323
twin_context_t *twin_create(int width, int height)
2424
{
25+
/* Initialize the memory pool before any allocations occur. */
26+
twin_mem_pool_init();
27+
2528
/* Runtime check for missing backend */
2629
if (!g_twin_backend.init) {
2730
log_error("Backend not registered - no init function");

src/box.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ static twin_dispatch_result_t _twin_box_query_geometry(twin_box_t *box)
4141
for (twin_widget_t *child = box->children; child; child = child->next) {
4242
if (child->layout) {
4343
ev.kind = TwinEventQueryGeometry;
44-
child->handler(child, &ev, child->callback_data);
44+
child->handler(child, &ev, _twin_widget_callback_data(child));
4545
}
4646
if (box->dir == TwinBoxHorz) {
4747
preferred.width += child->preferred.width;
@@ -129,7 +129,7 @@ static twin_dispatch_result_t _twin_box_configure(twin_box_t *box)
129129
extents.bottom != child->extents.bottom) {
130130
ev.kind = TwinEventConfigure;
131131
ev.u.configure.extents = extents;
132-
child->handler(child, &ev, child->callback_data);
132+
child->handler(child, &ev, _twin_widget_callback_data(child));
133133
}
134134
}
135135
return TwinDispatchContinue;
@@ -167,7 +167,7 @@ twin_dispatch_result_t _twin_box_dispatch(twin_widget_t *widget,
167167

168168
/* Send destroy event to child */
169169
ev.kind = TwinEventDestroy;
170-
child->handler(child, &ev, child->callback_data);
170+
child->handler(child, &ev, _twin_widget_callback_data(child));
171171
}
172172
break;
173173
case TwinEventQueryGeometry:
@@ -178,7 +178,7 @@ twin_dispatch_result_t _twin_box_dispatch(twin_widget_t *widget,
178178
twin_window_show(widget->window);
179179
box->button_down =
180180
_twin_box_xy_to_widget(box, event->u.pointer.x, event->u.pointer.y);
181-
if (box->button_down && box->button_down->want_focus)
181+
if (box->button_down && _twin_widget_want_focus(box->button_down))
182182
box->focus = box->button_down;
183183
fallthrough;
184184
case TwinEventButtonUp:
@@ -188,15 +188,16 @@ twin_dispatch_result_t _twin_box_dispatch(twin_widget_t *widget,
188188
ev = *event;
189189
ev.u.pointer.x -= child->extents.left;
190190
ev.u.pointer.y -= child->extents.top;
191-
return child->handler(child, &ev, child->callback_data);
191+
return child->handler(child, &ev,
192+
_twin_widget_callback_data(child));
192193
}
193194
break;
194195
case TwinEventKeyDown:
195196
case TwinEventKeyUp:
196197
case TwinEventUcs4:
197198
if (box->focus)
198199
return box->focus->handler(box->focus, event,
199-
box->focus->callback_data);
200+
_twin_widget_callback_data(box->focus));
200201
break;
201202
case TwinEventPaint:
202203
box->widget.paint = false;
@@ -215,7 +216,7 @@ twin_dispatch_result_t _twin_box_dispatch(twin_widget_t *widget,
215216
twin_pixmap_set_clip(pixmap, child->extents);
216217
twin_pixmap_origin_to_clip(pixmap);
217218
child->paint = false;
218-
child->handler(child, event, child->callback_data);
219+
child->handler(child, event, _twin_widget_callback_data(child));
219220
twin_pixmap_restore_clip(pixmap, clip);
220221
twin_pixmap_set_origin(pixmap, ox, oy);
221222
}

src/button.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,12 @@ twin_dispatch_result_t _twin_button_dispatch(twin_widget_t *widget,
4646
_twin_button_set_label_offset(button);
4747

4848
/* Invoke widget callback for button press */
49-
if (widget->callback) {
49+
if (_twin_widget_callback(widget)) {
5050
twin_event_t press_event = *event;
5151
press_event.kind = TwinEventButtonSignalDown;
5252
press_event.u.button_signal.signal = TwinButtonSignalDown;
53-
(*widget->callback)(widget, &press_event, widget->callback_data);
53+
_twin_widget_callback(widget)(widget, &press_event,
54+
_twin_widget_callback_data(widget));
5455
}
5556
return TwinDispatchDone;
5657
break;
@@ -72,12 +73,12 @@ twin_dispatch_result_t _twin_button_dispatch(twin_widget_t *widget,
7273
_twin_button_set_label_offset(button);
7374

7475
/* Invoke widget callback for button release (click) */
75-
if (widget->callback) {
76+
if (_twin_widget_callback(widget)) {
7677
twin_event_t release_event = *event;
7778
release_event.kind = TwinEventButtonSignalUp;
7879
release_event.u.button_signal.signal = TwinButtonSignalUp;
79-
(*widget->callback)(widget, &release_event,
80-
widget->callback_data);
80+
_twin_widget_callback(widget)(
81+
widget, &release_event, _twin_widget_callback_data(widget));
8182
}
8283
}
8384
return TwinDispatchDone;

0 commit comments

Comments
 (0)