aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Bennett <steveb@workware.net.au>2020-12-30 11:39:05 +1000
committerSteve Bennett <steveb@workware.net.au>2021-01-01 16:54:24 +1000
commit3b834e42ee5887f85d10d7e50814b29d1b81a09f (patch)
tree7458adbd80a56028addff1b879d726187d4fe81b
parent982ec4f524bc81a240cb729cf09bd3c677aea485 (diff)
downloadjimtcl-3b834e42ee5887f85d10d7e50814b29d1b81a09f.zip
jimtcl-3b834e42ee5887f85d10d7e50814b29d1b81a09f.tar.gz
jimtcl-3b834e42ee5887f85d10d7e50814b29d1b81a09f.tar.bz2
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 <steveb@workware.net.au>
-rw-r--r--auto.def8
-rw-r--r--autosetup/local.tcl57
-rw-r--r--examples/sdltest.tcl28
-rw-r--r--jim-sdl.c137
4 files changed, 181 insertions, 49 deletions
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 <string.h>
#include <errno.h>
#include <SDL.h>
+#if SDL_MAJOR_VERSION == 2
+#include <SDL2_gfxPrimitives.h>
+#else
#include <SDL_gfxPrimitives.h>
+#endif
#include <jim.h>
-#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;