From d26bd78644544151b42600c5358d0fecc84b14f1 Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Mon, 12 Oct 2026 22:43:23 +0300 Subject: feat: implement modern Approach A app runner, unified events, notepad, and game examples --- include/yasl/app.h | 297 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 include/yasl/app.h (limited to 'include/yasl/app.h') diff --git a/include/yasl/app.h b/include/yasl/app.h new file mode 100644 index 0000000..91de019 --- /dev/null +++ b/include/yasl/app.h @@ -0,0 +1,297 @@ +/** + * @file + * Unified application runner, event pipeline, and presentation context. + * + * Implements the inverted lifecycle architecture for modern cross-platform + * applications (games, IDEs, and desktop tools). Instead of managing manual + * dispatch/poll/flush pumps, the application provides event and frame callbacks + * to yasl_run(), which drives platform runloops, display pacing, and live resizing. + */ +#ifndef YASL_APP_H +#define YASL_APP_H + +#include "yasl/yasl.h" +#include "yasl/loop.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Application callback return status. */ +typedef enum yasl_app_result { + YASL_APP_CONTINUE = 0, /**< Continue running the application. */ + YASL_APP_QUIT, /**< Request graceful termination of yasl_run(). */ +} yasl_app_result; + +/** Pacing mode governing when frame() callbacks are invoked. */ +typedef enum yasl_pacing_mode { + /** + * Reactive / on-demand presentation (ideal for IDEs, text editors, tools). + * The application sleeps at 0% CPU when idle. frame() is only invoked when + * explicitly requested via yasl_surface_request_frame() or upon OS window + * expose/resize events. + */ + YASL_PACING_ON_DEMAND = 0, + + /** + * Continuous vsync-aligned tick (ideal for game engines and animations). + * frame() is driven continuously at the display's native refresh rate + * (e.g. 60/120/144 Hz) via DisplayLink, Wayland frame callbacks, or loop timers. + */ + YASL_PACING_CONTINUOUS, +} yasl_pacing_mode; + +/** Mouse capture and virtualization modes. */ +typedef enum yasl_mouse_mode { + YASL_MOUSE_NORMAL = 0, /**< Standard visible pointer. */ + YASL_MOUSE_HIDDEN, /**< Hidden cursor, normal bounded window coordinates. */ + YASL_MOUSE_RELATIVE, /**< Relative pointer lock: cursor hidden, unbounded deltas. */ +} yasl_mouse_mode; + +/** Unified event types delivered to the application event callback. */ +typedef enum yasl_event_type { + YASL_EVENT_NONE = 0, + + /* Input events */ + YASL_EVENT_KEY, + YASL_EVENT_TEXT, + YASL_EVENT_MOUSE_BUTTON, + YASL_EVENT_MOUSE_MOTION, + YASL_EVENT_MOUSE_SCROLL, + YASL_EVENT_MOUSE_LEAVE, + + /* Window lifecycle events */ + YASL_EVENT_WINDOW_RESIZE, + YASL_EVENT_WINDOW_SCALE_CHANGED, + YASL_EVENT_WINDOW_CLOSE_REQUEST, + YASL_EVENT_WINDOW_FOCUS, + + /* System & external events */ + YASL_EVENT_DROP_FILES, + YASL_EVENT_USER, +} yasl_event_type; + +/** Unified event payload delivered to yasl_app_desc.event(). */ +struct yasl_event { + struct yasl_surface *surface; /**< Associated surface, or NULL for display-wide events. */ + yasl_event_type type; + + union { + struct { + enum yasl_key key; + enum yasl_key_action action; + uint32_t mods; + uint32_t codepoint; + uint32_t shifted; + uint32_t native_code; + bool repeat; /**< true if this press is generated by OS autorepeat. */ + } key; + + struct { + const char *text; /**< UTF-8 text string; borrowed for callback duration. */ + size_t len; /**< Length in bytes excluding terminating NUL. */ + } text; + + struct { + struct yasl_vector2i pos; /**< Device-pixel position relative to surface. */ + int16_t button; /**< 0 primary, 1 middle, 2 secondary. */ + bool pressed; /**< true for press, false for release. */ + uint32_t mods; + struct yasl_pointer_pen pen; + } mouse_button; + + struct { + struct yasl_vector2i pos; /**< Device-pixel position relative to surface. */ + struct yasl_vector2i delta; /**< Relative motion delta (dx, dy). */ + uint32_t mods; + struct yasl_pointer_pen pen; + } mouse_motion; + + struct { + struct yasl_vector2i pos; + struct yasl_vector2d scroll; /**< Fractional line units; positive right/down. */ + uint32_t mods; + } mouse_scroll; + + struct { + struct yasl_vector2i size; /**< Configured content extent in device pixels. */ + uint32_t scale_120; + } resize; + + struct { + uint32_t scale_120; + struct yasl_vector2i size; + } scale; + + struct { + bool focused; + } focus; + + struct { + const char *files; /**< NUL-terminated text/uri-list. */ + size_t len; + } drop_files; + + struct { + uint32_t type; /**< Application-defined message type. */ + void *payload; /**< User-defined payload pointer. */ + size_t size; /**< Optional payload size. */ + } user; + }; +}; + +/** Frame context provided to yasl_app_desc.frame() during presentation. */ +struct yasl_frame_context { + struct yasl_surface *surface; + uint64_t frame_index; + uint64_t target_time_ns; /**< Monotonic timestamp aligned to display refresh. */ + struct yasl_vector2i size; /**< Surface content size in device pixels. */ + uint32_t scale_120; + + /** CPU software rendering target, if surface was created for CPU blitting. */ + struct { + uint32_t *pixels; /**< Writable premultiplied 0xAARRGGBB device pixels. */ + int stride_bytes; /**< Row stride in bytes. */ + } cpu; + + /** Native GPU rendering layer/surface, if surface was created for GPU rendering. */ + struct { + void *native_layer; /**< CAMetalLayer* on Cocoa, wl_surface* on Wayland, Window on X11. */ + } gpu; + + /** Internal handle used by yasl_frame_present_cpu. Applications must not touch. */ + void *internal_handle; +}; + +/** + * Present CPU software target pixels. + * @param ctx Active frame context passed to yasl_app_desc.frame(). + * @param dirty_rects Optional array of damaged rectangles in device pixels. + * Pass NULL or count <= 0 to indicate full-surface damage. + * @param count Number of rectangles in dirty_rects. + * @return true if accepted, false on presentation failure. + */ +YASL_API bool yasl_frame_present_cpu(struct yasl_frame_context *ctx, + const struct yasl_rect2i *dirty_rects, + int count); + +/** + * Configure the presentation pacing mode for a surface. + * @param s Surface. + * @param mode YASL_PACING_ON_DEMAND for reactive UI, YASL_PACING_CONTINUOUS for games. + */ +YASL_API void yasl_surface_set_pacing(struct yasl_surface *s, yasl_pacing_mode mode); + +/** + * Mark a surface as needing a redraw on the next frame pass. + * Wakes the event loop if sleeping under YASL_PACING_ON_DEMAND. + * @param s Surface. + */ +YASL_API void yasl_surface_request_frame(struct yasl_surface *s); + +/** + * Configure mouse pointer capture / virtualization mode. + * @param s Surface. + * @param mode Normal, hidden, or relative pointer lock. + */ +YASL_API void yasl_surface_set_mouse_mode(struct yasl_surface *s, yasl_mouse_mode mode); + +/** + * Safely post a custom user event to the main event loop from ANY thread. + * Wakes the display loop and delivers YASL_EVENT_USER to the event callback. + * @param d Display. + * @param s Optional target surface, or NULL. + * @param user_type Application-defined message type. + * @param payload Arbitrary pointer passed through to the event. + * @param size Optional payload size. + * @return true if queued successfully, false if queue is full or memory failed. + */ +YASL_API bool yasl_post_event(struct yasl_display *d, struct yasl_surface *s, + uint32_t user_type, void *payload, size_t size); + +/** + * Watch an open file descriptor or socket for poll-style readiness in the main loop. + * @param d Display. + * @param fd Open file descriptor (e.g. LSP stdout pipe or socket). + * @param poll_events Poll bitmask (e.g. POLLIN, POLLOUT). + * @param cb Readiness callback run on the main thread. + * @param ud Userdata. + * @return Positive watch ID, or -1 on failure. + */ +YASL_API int yasl_app_watch_fd(struct yasl_display *d, int fd, short poll_events, + int (*cb)(int fd, short revents, void *ud), void *ud); + +/** + * Unregister a file watch. + * @param d Display. + * @param watch_id Watch ID returned by yasl_app_watch_fd(). + */ +YASL_API void yasl_app_unwatch_fd(struct yasl_display *d, int watch_id); + +/** + * Schedule a repeating or one-shot timer on the display's event loop. + * @param d Display. + * @param interval_ns Delay in monotonic nanoseconds. + * @param cb Callback returning next interval in nanoseconds, or 0 to stop. + * @param ud Userdata. + * @return Positive timer ID, or 0 on failure. + */ +YASL_API yasl_timer_id yasl_app_add_timer(struct yasl_display *d, uint64_t interval_ns, + yasl_timer_cb cb, void *ud); + +/** + * Cancel an active timer. + * @param d Display. + * @param id Timer ID returned by yasl_app_add_timer(). + */ +YASL_API void yasl_app_del_timer(struct yasl_display *d, yasl_timer_id id); + +/** Application callbacks descriptor passed to yasl_run(). */ +struct yasl_app_desc { + /** Application identifier, e.g. "org.example.app". */ + const char *app_id; + /** Desired backend; YASL_BACKEND_AUTO chooses native with fallback. */ + enum yasl_backend backend; + /** User data passed to all callbacks. */ + void *userdata; + + /** + * Called once after display and event machinery initialization. + * Use this to create windows and initialize application resources. + * Return YASL_APP_CONTINUE or YASL_APP_QUIT. + */ + int (*init)(void *ud, struct yasl_display *d); + + /** + * Unified event handler called for input, resize, close, scale, and custom events. + * Event payloads and borrowed strings are valid for the duration of the callback. + * Return YASL_APP_CONTINUE or YASL_APP_QUIT. + */ + int (*event)(void *ud, const struct yasl_event *ev); + + /** + * Invoked when a surface needs to present a frame. + * Synchronized automatically with display pacing and live window resizing. + */ + void (*frame)(void *ud, struct yasl_surface *s, struct yasl_frame_context *ctx); + + /** + * Called right before the display is closed and yasl_run() exits. + * Destroy remaining surfaces and release application resources here. + */ + void (*quit)(void *ud, struct yasl_display *d); +}; + +/** + * Run the application with the inverted lifecycle runner. + * Handles the display event loop, live-resizing, pacing, and cleanup. + * @param desc Application callbacks and options. + * @return 0 on orderly exit, or non-zero on error. + */ +YASL_API int yasl_run(const struct yasl_app_desc *desc); + +#ifdef __cplusplus +} +#endif + +#endif /* YASL_APP_H */ -- cgit v1.2.3