summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-13 15:43:04 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-13 15:43:04 +0300
commite24b8a8cef001e1c7e50ab12b739486f59b2a850 (patch)
treef4678f9c7081c2bfd2dad56c8ca456c01447a116
parent38533c81e7b7ebfc5f281c2523188aeaa38d09c3 (diff)
Let yasl_app_desc describe its initial window
A new optional yasl_app_desc.window spec makes yasl_run() create that toplevel and request its first frame before init(), which is now optional too. If creation fails, yasl_run() closes the display and returns an error without calling init(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--docs/guide.md7
-rw-r--r--docs/migrating.md5
-rw-r--r--include/yasl/app.h10
-rw-r--r--src/common/app.c17
-rw-r--r--tests/common/test_app.c24
5 files changed, 55 insertions, 8 deletions
diff --git a/docs/guide.md b/docs/guide.md
index f195e5e..a65972f 100644
--- a/docs/guide.md
+++ b/docs/guide.md
@@ -40,8 +40,11 @@ it advertises CSD. Check each result and provide an application fallback.
## Window creation and callbacks
All examples include `<yasl/app.h>` and call `yasl_run()` with a
-`yasl_app_desc`. Create surfaces in `init`, handle `YASL_EVENT_*` notifications
-in `event`, and render into `frame->cpu.pixels` in `frame`. Present with
+`yasl_app_desc`. Set `window` to a `yasl_window_spec` for a single toplevel,
+which the runner creates before `init` with its first frame requested; `yasl_run()` returns an
+error if creation fails. Create further surfaces in the optional `init`, handle
+`YASL_EVENT_*` notifications in `event`, and render into `frame->cpu.pixels` in
+`frame`. Present with
`yasl_frame_present_cpu(frame, NULL, 0)` and return `YASL_APP_QUIT` from an event
handler to exit. The runner handles dispatch, input polling, resize redraws,
and remaining surface/display cleanup. Display quit stops the runner; connection
diff --git a/docs/migrating.md b/docs/migrating.md
index e1f5442..86804c8 100644
--- a/docs/migrating.md
+++ b/docs/migrating.md
@@ -6,8 +6,9 @@ See the [application guide](guide.md) for the full window and input workflow.
## Application callbacks
-All examples now use `<yasl/app.h>` and `yasl_run()`. Move window creation to
-`yasl_app_desc.init`, input handling to `.event`, and drawing to `.frame`.
+All examples now use `<yasl/app.h>` and `yasl_run()`. Describe a single window
+in `yasl_app_desc.window`, or create windows in `.init`; move input handling to
+`.event` and drawing to `.frame`.
Replace manual dispatch/poll/flush loops with the runner and request redraws with
`yasl_surface_request_frame()`. Present the frame context with
`yasl_frame_present_cpu()`; the runner destroys remaining surfaces on exit.
diff --git a/include/yasl/app.h b/include/yasl/app.h
index f067d5b..79b2519 100644
--- a/include/yasl/app.h
+++ b/include/yasl/app.h
@@ -148,10 +148,16 @@ struct yasl_app_desc {
enum yasl_backend backend;
/** User data passed to all callbacks. */
void *userdata;
+ /**
+ * Optional toplevel created before init(), with its first frame requested.
+ * Borrowed for the call; spec cbs and ud are ignored as for any runner window.
+ * yasl_run() returns an error without calling init() if creation fails.
+ */
+ const struct yasl_window_spec *window;
/**
- * Called once after display and event machinery initialization.
- * Use this to create windows and initialize application resources.
+ * Optional; called once after display and event machinery initialization.
+ * Use this to create further windows and initialize application resources.
* Return YASL_APP_CONTINUE or YASL_APP_QUIT.
*/
enum yasl_app_result (*init)(void *ud, struct yasl_display *d);
diff --git a/src/common/app.c b/src/common/app.c
index 9ec1f6a..9d52e4a 100644
--- a/src/common/app.c
+++ b/src/common/app.c
@@ -332,7 +332,7 @@ static void app_runner_lost(void *ud)
int yasl_run(const struct yasl_app_desc *desc)
{
- if (!desc || !desc->init)
+ if (!desc)
return -1;
struct app_runner runner = {.desc = desc, .running = true};
@@ -358,7 +358,20 @@ int yasl_run(const struct yasl_app_desc *desc)
}
d->app_runner = &runner;
- if (desc->init(desc->userdata, d) == YASL_APP_QUIT) {
+ if (desc->window) {
+ struct yasl_surface *s = yasl_surface_create_toplevel(d, desc->window);
+ if (!s) {
+ int create_error = errno;
+ d->app_runner = NULL;
+ yasl_mutex_destroy(&runner.user_event_mutex);
+ yasl_display_close(d);
+ errno = create_error;
+ return -1;
+ }
+ yasl_surface_request_frame(s);
+ }
+
+ if (desc->init && desc->init(desc->userdata, d) == YASL_APP_QUIT) {
runner.running = false;
}
diff --git a/tests/common/test_app.c b/tests/common/test_app.c
index 6972921..f61a957 100644
--- a/tests/common/test_app.c
+++ b/tests/common/test_app.c
@@ -109,6 +109,29 @@ static void on_quit(void *ud, struct yasl_display *d)
state->quit_called = true;
}
+static void present_once(void *ud, struct yasl_surface *s, struct yasl_frame_context *ctx)
+{
+ (*(int *)ud)++;
+ if (ctx->cpu.pixels)
+ assert(yasl_frame_present_cpu(ctx, NULL, 0));
+ yasl_surface_destroy(s);
+}
+
+/* desc.window replaces init: the runner creates the toplevel and draws it. */
+static void test_window_without_init(void)
+{
+ int frames = 0;
+ struct yasl_window_spec window = {.title = "App Runner Window", .size = {160, 120}};
+ int res = yasl_run(&(struct yasl_app_desc){
+ .app_id = "org.yasl.test.app.window",
+ .userdata = &frames,
+ .window = &window,
+ .frame = present_once,
+ });
+ assert(res == 0);
+ assert(frames == 1);
+}
+
int main(void)
{
struct test_state state = {0};
@@ -129,6 +152,7 @@ int main(void)
assert(state.user_event_called);
assert(state.timer_called);
assert(state.quit_called);
+ test_window_without_init();
printf("App runner tests passed!\n");
return 0;
}