From d15970f236a215b4a655c2401ea72fd1571705d4 Mon Sep 17 00:00:00 2001 From: Jim Huang Date: Thu, 25 Jul 2024 04:43:53 +0800 Subject: [PATCH] Introduce interface to backends Functions twin_{create,destroy} are introduced along with the refined backend interface, allowing for an incoming Linux framebuffer backend. This commit simplifies backend development and consolidates consistent APIs, meaning that a single source can be built for supported backends without conditional compilation. --- Makefile | 41 +++++----- apps/{twin-sdl.c => main.c} | 20 ++--- backend/sdl.c | 153 ++++++++++++++++++++++-------------- backend/twin_sdl.h | 38 --------- include/twin.h | 13 +++ src/api.c | 24 ++++++ src/image-jpeg.c | 4 +- src/twin_backend.h | 26 ++++++ 8 files changed, 186 insertions(+), 133 deletions(-) rename apps/{twin-sdl.c => main.c} (79%) delete mode 100644 backend/twin_sdl.h create mode 100644 src/api.c create mode 100644 src/twin_backend.h diff --git a/Makefile b/Makefile index 59378f6..407beef 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # FIXME: make these entries configurable -CONFIG_BACKEND_SDL := y -CONFIG_LOADER_JPEG := y -CONFIG_LOADER_PNG := y +CONFIG_BACKEND_SDL ?= y +CONFIG_LOADER_JPEG ?= y +CONFIG_LOADER_PNG ?= y # Rules @@ -10,9 +10,9 @@ target.o-y := TARGET_LIBS := target.a-y = \ - libbackend.a \ libapps.a \ - libtwin.a + libtwin.a \ + libbackend.a # Core library @@ -46,7 +46,8 @@ libtwin.a_files-y = \ src/hull.c \ src/icon.c \ src/pixmap.c \ - src/timeout.c + src/timeout.c \ + src/api.c libtwin.a_includes-y := include @@ -77,32 +78,28 @@ libapps.a_files-y = \ libapps.a_includes-y := include # Graphical backends +BACKEND := none libbackend.a_files-y := libbackend.a_cflags-y := -libbackend.a_includes-y := include +libbackend.a_includes-y := \ + include \ + src ifeq ($(CONFIG_BACKEND_SDL), y) +BACKEND = sdl libbackend.a_files-y += backend/sdl.c libbackend.a_cflags-y += $(shell sdl2-config --cflags) TARGET_LIBS += $(shell sdl2-config --libs) endif # Platform-specific executables - -ifeq ($(CONFIG_BACKEND_SDL), y) -target-y += demo-sdl - -demo-sdl_depends-y += $(target.a-y) -demo-sdl_files-y = \ - apps/twin-sdl.c - -demo-sdl_includes-y := include -demo-sdl_includes-y += backend - -demo-sdl_cflags-y = $(shell sdl2-config --cflags) -demo-sdl_ldflags-y := $(target.a-y) -demo-sdl_ldflags-y += $(TARGET_LIBS) -endif # CONFIG_BACKEND_SDL +target-y += demo-$(BACKEND) +demo-$(BACKEND)_depends-y += $(target.a-y) +demo-$(BACKEND)_files-y = apps/main.c +demo-$(BACKEND)_includes-y := include +demo-$(BACKEND)_ldflags-y := \ + $(target.a-y) \ + $(TARGET_LIBS) include mk/common.mk diff --git a/apps/twin-sdl.c b/apps/main.c similarity index 79% rename from apps/twin-sdl.c rename to apps/main.c index 823537c..d9d30bd 100644 --- a/apps/twin-sdl.c +++ b/apps/main.c @@ -11,8 +11,6 @@ #include #include -#include "twin_sdl.h" - #include "apps_calc.h" #include "apps_clock.h" #include "apps_demo.h" @@ -70,18 +68,20 @@ static twin_pixmap_t *load_background(twin_screen_t *screen, int main(void) { - twin_sdl_t *sdl = twin_sdl_create(WIDTH, HEIGHT); + twin_context_t *x = twin_create(WIDTH, HEIGHT); twin_screen_set_background( - sdl->screen, load_background(sdl->screen, ASSET_PATH "/tux.png")); + x->screen, load_background(x->screen, ASSET_PATH "/tux.png")); - apps_demo_start(sdl->screen, "Demo", 100, 100, 400, 400); - apps_hello_start(sdl->screen, "Hello, World", 0, 0, 200, 200); - apps_clock_start(sdl->screen, "Clock", 10, 10, 200, 200); - apps_calc_start(sdl->screen, "Calculator", 100, 100, 200, 200); - apps_line_start(sdl->screen, "Line", 0, 0, 200, 200); - apps_spline_start(sdl->screen, "Spline", 20, 20, 400, 400); + apps_demo_start(x->screen, "Demo", 100, 100, 400, 400); + apps_hello_start(x->screen, "Hello, World", 0, 0, 200, 200); + apps_clock_start(x->screen, "Clock", 10, 10, 200, 200); + apps_calc_start(x->screen, "Calculator", 100, 100, 200, 200); + apps_line_start(x->screen, "Line", 0, 0, 200, 200); + apps_spline_start(x->screen, "Spline", 20, 20, 400, 400); twin_dispatch(); + + twin_destroy(x); return 0; } diff --git a/backend/sdl.c b/backend/sdl.c index 8c50869..26e5a60 100644 --- a/backend/sdl.c +++ b/backend/sdl.c @@ -4,10 +4,23 @@ * All rights reserved. */ +#include +#include #include +#include -#include "twin_private.h" -#include "twin_sdl.h" +#include "twin_backend.h" + +typedef struct { + SDL_Window *win; + int *pixels; + SDL_Renderer *render; + SDL_Texture *texture; + twin_coord_t width, height; + int image_y; +} twin_sdl_t; + +#define PRIV(x) ((twin_sdl_t *) ((twin_context_t *) x)->priv) static void _twin_sdl_put_begin(twin_coord_t left, twin_coord_t top, @@ -15,7 +28,7 @@ static void _twin_sdl_put_begin(twin_coord_t left, twin_coord_t bottom, void *closure) { - twin_sdl_t *tx = closure; + twin_sdl_t *tx = PRIV(closure); tx->width = right - left; tx->height = bottom - top; tx->image_y = top; @@ -27,29 +40,43 @@ static void _twin_sdl_put_span(twin_coord_t left, twin_argb32_t *pixels, void *closure) { - twin_sdl_t *tx = closure; + twin_screen_t *screen = ((twin_context_t *) closure)->screen; + twin_sdl_t *tx = PRIV(closure); for (twin_coord_t ix = left, iy = top; ix < right; ix++) { twin_argb32_t pixel = *pixels++; - - if (tx->depth == 16) - pixel = twin_argb32_to_rgb16(pixel); - - tx->pixels[iy * tx->screen->width + ix] = pixel; + tx->pixels[iy * screen->width + ix] = pixel; } if ((top + 1 - tx->image_y) == tx->height) { SDL_UpdateTexture(tx->texture, NULL, tx->pixels, - tx->screen->width * sizeof(uint32_t)); + screen->width * sizeof(uint32_t)); SDL_RenderCopy(tx->render, tx->texture, NULL, NULL); SDL_RenderPresent(tx->render); } } +static void _twin_sdl_destroy(twin_screen_t *screen, twin_sdl_t *tx) +{ + twin_screen_destroy(screen); + SDL_DestroyRenderer(tx->render); + SDL_DestroyWindow(tx->win); + SDL_Quit(); +} + +static void twin_sdl_damage(twin_screen_t *screen, twin_sdl_t *tx) +{ + int width, height; + SDL_GetWindowSize(tx->win, &width, &height); + twin_screen_damage(screen, 0, 0, width, height); +} + static bool twin_sdl_read_events(int file maybe_unused, twin_file_op_t ops maybe_unused, void *closure) { - twin_sdl_t *tx = closure; + twin_screen_t *screen = ((twin_context_t *) closure)->screen; + twin_sdl_t *tx = PRIV(closure); + SDL_Event ev; while (SDL_PollEvent(&ev)) { twin_event_t tev; @@ -57,11 +84,11 @@ static bool twin_sdl_read_events(int file maybe_unused, case SDL_WINDOWEVENT: if (ev.window.event == SDL_WINDOWEVENT_EXPOSED || ev.window.event == SDL_WINDOWEVENT_SHOWN) { - twin_sdl_damage(tx, &ev); + twin_sdl_damage(screen, tx); } break; case SDL_QUIT: - twin_sdl_destroy(tx); + _twin_sdl_destroy(screen, tx); return false; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: @@ -71,21 +98,21 @@ static bool twin_sdl_read_events(int file maybe_unused, ((ev.button.state >> 8) | (1 << (ev.button.button - 1))); tev.kind = ((ev.type == SDL_MOUSEBUTTONDOWN) ? TwinEventButtonDown : TwinEventButtonUp); - twin_screen_dispatch(tx->screen, &tev); + twin_screen_dispatch(screen, &tev); break; case SDL_KEYDOWN: case SDL_KEYUP: tev.u.key.key = ev.key.keysym.sym; tev.kind = ((ev.key.type == SDL_KEYDOWN) ? TwinEventKeyDown : TwinEventKeyUp); - twin_screen_dispatch(tx->screen, &tev); + twin_screen_dispatch(screen, &tev); break; case SDL_MOUSEMOTION: tev.u.pointer.screen_x = ev.motion.x; tev.u.pointer.screen_y = ev.motion.y; tev.kind = TwinEventMotion; tev.u.pointer.button = ev.motion.state; - twin_screen_dispatch(tx->screen, &tev); + twin_screen_dispatch(screen, &tev); break; } } @@ -94,85 +121,89 @@ static bool twin_sdl_read_events(int file maybe_unused, static bool twin_sdl_work(void *closure) { - twin_sdl_t *tx = closure; + twin_screen_t *screen = ((twin_context_t *) closure)->screen; - if (twin_screen_damaged(tx->screen)) - twin_sdl_update(tx); + if (twin_screen_damaged(screen)) + twin_screen_update(screen); return true; } -twin_sdl_t *twin_sdl_create(int width, int height) +twin_context_t *twin_sdl_init(int width, int height) { - static char *title = "twin-sdl"; - - twin_sdl_t *tx = malloc(sizeof(twin_sdl_t)); - if (!tx) + twin_context_t *ctx = calloc(1, sizeof(twin_context_t)); + if (!ctx) + return NULL; + ctx->priv = calloc(1, sizeof(twin_sdl_t)); + if (!ctx->priv) return NULL; - if (SDL_Init(SDL_INIT_VIDEO) < 0) + if (SDL_Init(SDL_INIT_VIDEO) < 0) { printf("error : %s\n", SDL_GetError()); + goto bail; + } + + twin_sdl_t *tx = ctx->priv; + + static char *title = "twin-sdl"; tx->win = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN); - if (!tx->win) + if (!tx->win) { printf("error : %s\n", SDL_GetError()); + goto bail; + } + tx->pixels = malloc(width * height * sizeof(uint32_t)); memset(tx->pixels, 255, width * height * sizeof(uint32_t)); tx->render = SDL_CreateRenderer(tx->win, -1, SDL_RENDERER_ACCELERATED); - if (!tx->render) + if (!tx->render) { printf("error : %s\n", SDL_GetError()); + goto bail_pixels; + } SDL_SetRenderDrawColor(tx->render, 255, 255, 255, 255); SDL_RenderClear(tx->render); tx->texture = SDL_CreateTexture(tx->render, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height); - tx->screen = twin_screen_create(width, height, _twin_sdl_put_begin, - _twin_sdl_put_span, tx); - - twin_set_file(twin_sdl_read_events, 0, TWIN_READ, tx); + ctx->screen = twin_screen_create(width, height, _twin_sdl_put_begin, + _twin_sdl_put_span, ctx); - twin_set_work(twin_sdl_work, TWIN_WORK_REDISPLAY, tx); + twin_set_file(twin_sdl_read_events, 0, TWIN_READ, ctx); - return tx; -} + twin_set_work(twin_sdl_work, TWIN_WORK_REDISPLAY, ctx); -void twin_sdl_destroy(twin_sdl_t *tx) -{ - SDL_DestroyRenderer(tx->render); - SDL_DestroyWindow(tx->win); - tx->win = 0; - twin_screen_destroy(tx->screen); - SDL_Quit(); -} + return ctx; -void twin_sdl_damage(twin_sdl_t *tx, SDL_Event *ev maybe_unused) -{ - int width, height; - SDL_GetWindowSize(tx->win, &width, &height); - twin_screen_damage(tx->screen, 0, 0, width, height); +bail_pixels: + free(tx->pixels); +bail: + free(ctx->priv); + free(ctx); + return NULL; } -void twin_sdl_configure(twin_sdl_t *tx, SDL_Event *ev maybe_unused) +static void twin_sdl_configure(twin_context_t *ctx) { int width, height; - SDL_GetWindowSize(tx->win, &width, &height); - twin_screen_resize(tx->screen, width, height); + SDL_GetWindowSize(PRIV(ctx)->win, &width, &height); + twin_screen_resize(ctx->screen, width, height); } -void twin_sdl_update(twin_sdl_t *tx) +static void twin_sdl_exit(twin_context_t *ctx) { - twin_screen_update(tx->screen); + if (!ctx) + return; + free(PRIV(ctx)->pixels); + free(ctx->priv); + free(ctx); } -bool twin_sdl_process_events(twin_sdl_t *tx) -{ - bool result; +/* Register the SDL backend */ - _twin_run_work(); - result = twin_sdl_read_events(0, 0, tx); - _twin_run_work(); - - return result; -} +const twin_backend_t g_twin_backend = { + .init = twin_sdl_init, + .configure = twin_sdl_configure, + .exit = twin_sdl_exit, +}; diff --git a/backend/twin_sdl.h b/backend/twin_sdl.h deleted file mode 100644 index a53a927..0000000 --- a/backend/twin_sdl.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Twin - A Tiny Window System - * Copyright (c) 2024 National Cheng Kung University, Taiwan - * All rights reserved. - */ - -#ifndef _TWIN_SDL_H_ -#define _TWIN_SDL_H_ - -#include -#include - -#include - -typedef struct _twin_sdl { - twin_screen_t *screen; - SDL_Window *win; - int depth; - int *pixels; - SDL_Renderer *render; - SDL_Texture *texture; - twin_coord_t width, height; - int image_y; -} twin_sdl_t; - -twin_sdl_t *twin_sdl_create(int width, int height); - -void twin_sdl_destroy(twin_sdl_t *tx); - -void twin_sdl_damage(twin_sdl_t *tx, SDL_Event *ev); - -void twin_sdl_configure(twin_sdl_t *tx, SDL_Event *ev); - -void twin_sdl_update(twin_sdl_t *tx); - -bool twin_sdl_process_events(twin_sdl_t *tx); - -#endif /* _TWIN_SDL_H_ */ diff --git a/include/twin.h b/include/twin.h index d5f62db..659dd72 100644 --- a/include/twin.h +++ b/include/twin.h @@ -1110,4 +1110,17 @@ twin_work_t *twin_set_work(twin_work_proc_t work_proc, void twin_clear_work(twin_work_t *work); +/* + * backend + */ + +typedef struct _twin_context { + twin_screen_t *screen; + void *priv; +} twin_context_t; + +twin_context_t *twin_create(int width, int height); + +void twin_destroy(twin_context_t *ctx); + #endif /* _TWIN_H_ */ diff --git a/src/api.c b/src/api.c new file mode 100644 index 0000000..0af4666 --- /dev/null +++ b/src/api.c @@ -0,0 +1,24 @@ +/* + * Twin - A Tiny Window System + * Copyright (c) 2024 National Cheng Kung University, Taiwan + * All rights reserved. + */ + +#include +#include + +#include "twin_backend.h" + +/* FIXME: Refine API design */ + +extern twin_backend_t g_twin_backend; + +twin_context_t *twin_create(int width, int height) +{ + return g_twin_backend.init(width, height); +} + +void twin_destroy(twin_context_t *ctx) +{ + return g_twin_backend.exit(ctx); +} diff --git a/src/image-jpeg.c b/src/image-jpeg.c index 7734f0f..8218d9e 100644 --- a/src/image-jpeg.c +++ b/src/image-jpeg.c @@ -1,12 +1,12 @@ -/* +/* * Twin - A Tiny Window System * Copyright (c) 2024 National Cheng Kung University, Taiwan * All rights reserved. */ #include -#include #include +#include #include diff --git a/src/twin_backend.h b/src/twin_backend.h new file mode 100644 index 0000000..793a57a --- /dev/null +++ b/src/twin_backend.h @@ -0,0 +1,26 @@ +/* + * Twin - A Tiny Window System + * Copyright (c) 2024 National Cheng Kung University, Taiwan + * All rights reserved. + */ + +#ifndef _TWIN_BACKEND_H_ +#define _TWIN_BACKEND_H_ + +#include + +#include "twin_private.h" + +typedef struct twin_backend { + /* Initialize the backend */ + twin_context_t *(*init)(int width, int height); + + /* Configure the device */ + /* FIXME: Refine the parameter list */ + void (*configure)(twin_context_t *ctx); + + /* Device cleanup when drawing is done */ + void (*exit)(twin_context_t *ctx); +} twin_backend_t; + +#endif /* _TWIN_BACKEND_H_ */