summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Thompson <dthompson2@worcester.edu>2015-11-15 10:47:24 -0500
committerDavid Thompson <dthompson2@worcester.edu>2015-11-15 10:47:24 -0500
commit77ddc09a80ca6ba07597adc0313062e6f8649ca4 (patch)
tree238720c35d1d2d9aabcd664033160132f2cc8b4c
parent168894b85b818d276968b8ab68800e3a1942bdf6 (diff)
Reimplement live-reload module.
* sly/live-reload.scm (watch-files): New procedure. (live-reload): Rename to... (with-live-reload): This. * doc/api/utils.tex ("Live Reloading"): Update docs.
-rw-r--r--doc/api/utils.texi38
-rw-r--r--sly/live-reload.scm55
2 files changed, 67 insertions, 26 deletions
diff --git a/doc/api/utils.texi b/doc/api/utils.texi
index 4858010..4c7c39f 100644
--- a/doc/api/utils.texi
+++ b/doc/api/utils.texi
@@ -42,12 +42,38 @@ Abort from the error handling loop prompt and resume the game loop.
(use-modules (sly utils live-reload))
@end example
-The live-reload module enables Sly programs to react to changes in the
-file system and reload assets automatically, which is useful when
-using external programs such as an image editor or map editor. This
-makes it easy to see the changes made to game assets quickly.
+The @code{(sly live-reload)} module enables Sly programs to react to
+changes in the file system and reload assets automatically, which is
+useful when using external programs such as an image editor or map
+editor. Live reloading reduces the time and effort needed to see the
+changes made to game assets.
-@defun live-reload @var{proc} [@var{polling-interval}]
+The following example uses the @code{with-live-reload} procedure to
+create a texturing loading procedure that automatically reloads
+textures when their associated file on disk changes:
+
+@example
+(use-modules (sly live-reload)
+ (sly signal)
+ (sly texture))
+
+(define load-texture/live (with-live-reload load-texture))
+
+(define-signal fox (load-texture/live "fox.png"))
+@end example
+
+The more generic @code{watch-files} procedure can be used to implement
+new types of live reloading procedures.
+
+@deffn {Scheme Procedure} watch-files @var{files} @var{thunk} [@var{polling-interval}]
+Watch @var{files}, a list of file names, and apply @var{thunk} each
+time one of them changes. A signal (@pxref{Signals}) is returned that
+contains the current result of @var{thunk}. The
+@var{polling-interval} flag determines how often @var{files} are
+polled for changes, defaulting to two seconds.
+@end deffn
+
+@deffn {Scheme Procedure} with-live-reload @var{proc} [@var{polling-interval}]
Return a new procedure that re-applies @code{proc} whenever the
associated file is modified. The new procedure returns a signal
(@pxref{Signals}) that contains the return value of @code{proc}. The
@@ -55,7 +81,7 @@ first argument to @code{proc} must be a file name string.
A simple polling method is used to test for updates. Files are polled
every @code{polling-interval} ticks (120 by default).
-@end defun
+@end deffn
@node Miscellaneous-Utilities
@subsection Miscellaneous
diff --git a/sly/live-reload.scm b/sly/live-reload.scm
index f354fda..cb40e7c 100644
--- a/sly/live-reload.scm
+++ b/sly/live-reload.scm
@@ -26,9 +26,40 @@
#:use-module (sly agenda)
#:use-module (sly coroutine)
#:use-module (sly signal)
- #:export (live-reload))
+ #:export (watch-files
+ with-live-reload))
-(define* (live-reload proc #:optional (polling-interval 120))
+(define* (watch-files files thunk #:optional (polling-interval 120))
+ "Watch FILES, a list of file names, and apply THUNK each time one of
+them changes. A signal is returned that contains the current result
+of THUNK. The POLLING-INTERVAL flag determines how often FILES are
+polled for changes, defaulting to two seconds."
+ (define (current-mtime file-name)
+ (let ((info (stat file-name)))
+ (max (stat:mtime info) (stat:ctime info))))
+
+ (define (max-current-mtime)
+ (and (every file-exists? files)
+ (reduce max 0 (map current-mtime files))))
+
+ (let ((asset (make-signal (thunk))))
+ (coroutine
+ (let loop ((last-mtime (max-current-mtime)))
+ (wait polling-interval)
+ (let ((mtime (max-current-mtime)))
+ (when (cond
+ ;; Some files do not exist anymore, can't reload.
+ ((not mtime) #f)
+ ;; Some files were missing, but are back now.
+ ((and mtime (not last-mtime)) #t)
+ ;; All the files exist and existed last time, too, so
+ ;; check if they have been modified since last time.
+ (else (> mtime last-mtime)))
+ (signal-set! asset (thunk)))
+ (loop mtime))))
+ asset))
+
+(define* (with-live-reload proc #:optional (polling-interval 120))
"Return a new procedure that re-applies PROC whenever the associated
file is modified. The new procedure returns a signal that contains
the return value of PROC. The first argument to PROC must be a
@@ -37,21 +68,5 @@ file name string.
A simple polling method is used to test for updates. Files are polled
every POLLING-INTERVAL ticks (120 by default)."
(lambda (file-name . args)
- (define (load-asset)
- (apply proc file-name args))
-
- (define (current-mtime)
- (let ((info (stat file-name)))
- (max (stat:mtime info) (stat:ctime info))))
-
- (let ((asset (make-signal (load-asset))))
- (coroutine
- (let loop ((last-mtime (current-mtime)))
- (wait polling-interval)
- (let ((mtime (if (file-exists? file-name)
- (current-mtime)
- last-mtime)))
- (when (> mtime last-mtime)
- (signal-set! asset (load-asset)))
- (loop mtime))))
- asset)))
+ (watch-files (list file-name)
+ (lambda () (apply proc file-name args)))))