From 3b834e42ee5887f85d10d7e50814b29d1b81a09f Mon Sep 17 00:00:00 2001 From: Steve Bennett Date: Wed, 30 Dec 2020 11:39:05 +1000 Subject: sdl: Add support for SDL2 Now we only support using pkg-config to find SDL, and prefer SDL2 over SDL. For compatibility between versions, the render surface is now cleared on flip. And closing the window now results in a JIM_EXIT return code from flip. Also supports [sdl clear] to clear the background to a given colour. Signed-off-by: Steve Bennett --- auto.def | 8 +-- autosetup/local.tcl | 57 +++++++++++++++------ examples/sdltest.tcl | 28 +++++++++++ jim-sdl.c | 137 +++++++++++++++++++++++++++++++++++++++------------ 4 files changed, 181 insertions(+), 49 deletions(-) create mode 100644 examples/sdltest.tcl diff --git a/auto.def b/auto.def index e95b26b..dc783f6 100644 --- a/auto.def +++ b/auto.def @@ -404,6 +404,10 @@ dict set extdb attrs { # dep=list of extensions which are required for this extension # check=[expr] expression to evaluate to determine if the extension can be used # libdep=list of 'define' symbols for dependent libraries +# pkg-config=name1 ?args?, name2* ?args? | name3 ?args? +# Any set of packages from the alternates is acceptable (e.g. name1 and name2, or name3) +# If the pkgname has a * appended, it is optional (so name1 without name2 is OK) +# The optional args are pkg-config specifications (e.g. name1 >= 1.3.4) dict set extdb info { binary { dep pack } exec { check {([have-feature vfork] && [have-feature waitpid]) || [have-feature system]} } @@ -417,9 +421,7 @@ dict set extdb info { readline { pkg-config readline check {[cc-check-function-in-lib readline readline]} libdep lib_readline} rlprompt { dep readline } tree { dep oo } - sdl { pkg-config SDL_gfx check {[cc-check-function-in-lib SDL_SetVideoMode SDL] && [cc-check-function-in-lib rectangleRGBA SDL_gfx]} - libdep {lib_SDL_SetVideoMode lib_rectangleRGBA} - } + sdl { pkg-config {SDL2_gfx | SDL_gfx} } signal { check {[have-feature sigaction]} } sqlite3 { pkg-config sqlite3 check {[cc-check-function-in-lib sqlite3_prepare_v2 sqlite3]} libdep lib_sqlite3_prepare_v2 } redis { pkg-config hiredis check {[cc-check-function-in-lib redisConnect hiredis]} libdep lib_redisConnect } diff --git a/autosetup/local.tcl b/autosetup/local.tcl index ba2bb3d..c31ba18 100644 --- a/autosetup/local.tcl +++ b/autosetup/local.tcl @@ -59,13 +59,34 @@ proc check-extension-status {ext required {asmodule 0}} { set use_pkgconfig 0 set pkgconfig [ext-get $ext pkg-config] if {$pkgconfig ne ""} { - # pkg-config support is optional, so explicitly initialse it here + # pkg-config support is optional, so explicitly initialise it here if {[pkg-config-init 0]} { - lassign $pkgconfig pkg args - - if {[pkg-config {*}$pkgconfig]} { - # Found via pkg-config so ignore check and libdep - set use_pkgconfig 1 + # Check for at least one set of alternates + foreach pinfo [split $pkgconfig |] { + set ok 1 + set pkgs {} + foreach pkg [split $pinfo ,] { + set args [lassign $pkg pkgname] + set pkg [string trim $pkg] + set optional 0 + if {[string match {*[*]} $pkg]} { + # This package is optional + set optional 1 + set pkg [string range $pkg 0 end-1] + } + if {![pkg-config $pkg {*}$args]} { + if {!$optional} { + set ok 0 + break + } + } else { + lappend pkgs $pkg + } + } + if {$ok} { + set use_pkgconfig 1 + break + } } } } @@ -124,10 +145,7 @@ proc check-extension-status {ext required {asmodule 0}} { } else { msg-result "Extension $ext...module" if {$use_pkgconfig} { - define-append LDLIBS_$ext [pkg-config-get $pkg LIBS] - define-append LDFLAGS [pkg-config-get $pkg LDFLAGS] - define-append CCOPTS [pkg-config-get $pkg CFLAGS] - define-append PKG_CONFIG_REQUIRES $pkg + add-pkgconfig-deps $ext $pkgs $asmodule } else { foreach i [ext-get $ext libdep] { define-append LDLIBS_$ext [get-define $i ""] @@ -149,10 +167,7 @@ proc check-extension-status {ext required {asmodule 0}} { return [ext-set-status $ext x] } if {$use_pkgconfig} { - define-append LDLIBS [pkg-config-get $pkg LIBS] - define-append LDFLAGS [pkg-config-get $pkg LDFLAGS] - define-append CCOPTS [pkg-config-get $pkg CFLAGS] - define-append PKG_CONFIG_REQUIRES $pkg + add-pkgconfig-deps $ext $pkgs $asmodule } else { foreach i [ext-get $ext libdep] { define-append LDLIBS [get-define $i ""] @@ -161,6 +176,20 @@ proc check-extension-status {ext required {asmodule 0}} { return [ext-set-status $ext y] } +# Add dependencies for a pkg-config module to the extension +proc add-pkgconfig-deps {ext pkgs asmodule} { + foreach pkg $pkgs { + if {$asmodule} { + define-append LDLIBS_$ext [pkg-config-get $pkg LIBS] + } else { + define-append LDLIBS [pkg-config-get $pkg LIBS] + } + define-append LDFLAGS [pkg-config-get $pkg LDFLAGS] + define-append CCOPTS [pkg-config-get $pkg CFLAGS] + define-append PKG_CONFIG_REQUIRES $pkg + } +} + # Examines the user options (the $withinfo array) # and the extension database ($extdb) to determine # what is selected, and in what way. diff --git a/examples/sdltest.tcl b/examples/sdltest.tcl new file mode 100644 index 0000000..6607574 --- /dev/null +++ b/examples/sdltest.tcl @@ -0,0 +1,28 @@ +package require sdl + +set xres 1024 +set yres 768 +set s [sdl.screen $xres $yres] + +proc drawlist {s list} { + foreach item $list { + $s {*}$item + } +} + +proc rand_circle {xres yres maxradius alpha} { + list fcircle [rand $xres] [rand $yres] [rand $maxradius] [rand 256] [rand 256] [rand 256] $alpha +} + +loop i 0 200 { + set commands {} + loop j 0 1000 { + lappend commands [rand_circle $xres $yres 40 100] + if {$j % 50 == 0} { + #$s clear 200 200 200 + drawlist $s $commands + $s flip + sleep 0.1 + } + } +} diff --git a/jim-sdl.c b/jim-sdl.c index 24ecef6..be4fe41 100644 --- a/jim-sdl.c +++ b/jim-sdl.c @@ -37,15 +37,24 @@ #include #include #include +#if SDL_MAJOR_VERSION == 2 +#include +#else #include +#endif #include -#define AIO_CMD_LEN 128 - typedef struct JimSdlSurface { +#if SDL_MAJOR_VERSION == 2 + SDL_Window *win; + SDL_Renderer *screen; + SDL_Texture *texture; +#else SDL_Surface *screen; +#endif + long background[4]; } JimSdlSurface; static void JimSdlSetError(Jim_Interp *interp) @@ -59,10 +68,38 @@ static void JimSdlDelProc(Jim_Interp *interp, void *privData) JIM_NOTUSED(interp); +#if SDL_MAJOR_VERSION == 2 + SDL_DestroyRenderer(jss->screen); + SDL_DestroyWindow(jss->win); +#else SDL_FreeSurface(jss->screen); +#endif Jim_Free(jss); } +static int JimSdlGetLongs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, long *dest) +{ + while (argc) { + if (Jim_GetLong(interp, *argv, dest) != JIM_OK) { + return JIM_ERR; + } + argc--; + argv++; + dest++; + } + return JIM_OK; +} + +static void JimSdlClear(JimSdlSurface *jss, int r, int g, int b, int alpha) +{ +#if SDL_MAJOR_VERSION == 2 + SDL_SetRenderDrawColor(jss->screen, r, g, b, alpha); + SDL_RenderClear(jss->screen); +#else + SDL_FillRect(jss->screen, NULL, SDL_MapRGBA(jss->screen->format, r, g, b, alpha)); +#endif +} + /* Calls to commands created via [sdl.surface] are implemented by this * C command. */ static int JimSdlHandlerCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) @@ -71,11 +108,11 @@ static int JimSdlHandlerCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar int option; static const char * const options[] = { "free", "flip", "pixel", "rectangle", "box", "line", "aaline", - "circle", "aacircle", "fcircle", NULL + "circle", "aacircle", "fcircle", "clear", NULL }; enum { OPT_FREE, OPT_FLIP, OPT_PIXEL, OPT_RECTANGLE, OPT_BOX, OPT_LINE, - OPT_AALINE, OPT_CIRCLE, OPT_AACIRCLE, OPT_FCIRCLE + OPT_AALINE, OPT_CIRCLE, OPT_AACIRCLE, OPT_FCIRCLE, OPT_CLEAR }; if (argc < 2) { @@ -86,22 +123,17 @@ static int JimSdlHandlerCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar return JIM_ERR; if (option == OPT_PIXEL) { /* PIXEL */ - long x, y, red, green, blue, alpha = 255; + /* x, y, red, green, blue, alpha = 255 */ + long vals[7]; if (argc != 7 && argc != 8) { Jim_WrongNumArgs(interp, 2, argv, "x y red green blue ?alpha?"); return JIM_ERR; } - if (Jim_GetLong(interp, argv[2], &x) != JIM_OK || - Jim_GetLong(interp, argv[3], &y) != JIM_OK || - Jim_GetLong(interp, argv[4], &red) != JIM_OK || - Jim_GetLong(interp, argv[5], &green) != JIM_OK || - Jim_GetLong(interp, argv[6], &blue) != JIM_OK) { + if (JimSdlGetLongs(interp, argc - 3, argv + 3, vals) != JIM_OK) { return JIM_ERR; } - if (argc == 8 && Jim_GetLong(interp, argv[7], &alpha) != JIM_OK) - return JIM_ERR; - pixelRGBA(jss->screen, x, y, red, green, blue, alpha); + pixelRGBA(jss->screen, vals[0], vals[1], vals[2], vals[3], vals[4], argc == 8 ? vals[5] : SDL_ALPHA_OPAQUE); return JIM_OK; } else if (option == OPT_RECTANGLE || option == OPT_BOX || @@ -180,13 +212,44 @@ static int JimSdlHandlerCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar Jim_DeleteCommand(interp, argv[0]); return JIM_OK; } + else if (option == OPT_CLEAR) { + long vals[4]; + if (argc != 5 && argc != 6) { + Jim_WrongNumArgs(interp, 2, argv, "red green blue ?alpha?"); + return JIM_ERR; + } + if (JimSdlGetLongs(interp, argc - 2, argv + 2, vals) != JIM_OK) { + return JIM_ERR; + } + if (argc == 5) { + vals[3] = SDL_ALPHA_OPAQUE; + } + JimSdlClear(jss, vals[0], vals[1], vals[2], vals[3]); + } else if (option == OPT_FLIP) { /* FLIP */ if (argc != 2) { Jim_WrongNumArgs(interp, 2, argv, ""); return JIM_ERR; } - SDL_Flip(jss->screen); + { + SDL_Event e; +#if SDL_MAJOR_VERSION == 2 + SDL_RenderPresent(jss->screen); +#else + SDL_Flip(jss->screen); +#endif + JimSdlClear(jss, 0, 0, 0, SDL_ALPHA_OPAQUE); + /* Throw away all events except quit, and pass this back as JIM_EXIT. + * If necessary, this can be caught with catch -exit { ... } + */ + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) { + Jim_SetResultInt(interp, 0); + return JIM_EXIT; + } + } + } return JIM_OK; } return JIM_OK; @@ -195,10 +258,8 @@ static int JimSdlHandlerCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar static int JimSdlSurfaceCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { JimSdlSurface *jss; - char buf[AIO_CMD_LEN]; - Jim_Obj *objPtr; - long screenId, xres, yres; - SDL_Surface *screen; + char buf[128]; + long xres, yres; if (argc != 3) { Jim_WrongNumArgs(interp, 1, argv, "xres yres"); @@ -208,25 +269,37 @@ static int JimSdlSurfaceCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar Jim_GetLong(interp, argv[2], &yres) != JIM_OK) return JIM_ERR; + jss = Jim_Alloc(sizeof(*jss)); + memset(jss, 0, sizeof(*jss)); + jss->background[3] = SDL_ALPHA_OPAQUE; + +#if SDL_MAJOR_VERSION == 2 /* Try to create the surface */ - screen = SDL_SetVideoMode(xres, yres, 32, SDL_SWSURFACE | SDL_ANYFORMAT); - if (screen == NULL) { + jss->win = SDL_CreateWindow("sdl", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, xres, yres, 0); + if (jss->win) { + jss->screen = SDL_CreateRenderer(jss->win, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); + if (jss->screen) { + /* Need an initial SDL_PollEvent() to make the window display */ + SDL_PollEvent(NULL); + } + else { + SDL_DestroyWindow(jss->win); + } + } +#else + jss->screen = SDL_SetVideoMode(xres, yres, 32, SDL_SWSURFACE | SDL_ANYFORMAT); +#endif + if (jss->screen) { + JimSdlClear(jss, 0, 0, 0, SDL_ALPHA_OPAQUE); + } + else { JimSdlSetError(interp); + Jim_Free(jss); return JIM_ERR; } - /* Get the next file id */ - if (Jim_EvalGlobal(interp, "if {[catch {incr sdl.surfaceId}]} {set sdl.surfaceId 0}") != JIM_OK) - return JIM_ERR; - objPtr = Jim_GetVariableStr(interp, "sdl.surfaceId", JIM_ERRMSG); - if (objPtr == NULL) - return JIM_ERR; - if (Jim_GetLong(interp, objPtr, &screenId) != JIM_OK) - return JIM_ERR; - /* Create the SDL screen command */ - jss = Jim_Alloc(sizeof(*jss)); - jss->screen = screen; - sprintf(buf, "sdl.surface%ld", screenId); + /* Create the SDL command */ + snprintf(buf, sizeof(buf), "sdl.surface%ld", Jim_GetId(interp)); Jim_CreateCommand(interp, buf, JimSdlHandlerCommand, jss, JimSdlDelProc); Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1))); return JIM_OK; -- cgit v1.1