diff options
-rw-r--r-- | doc/api/utils.texi | 38 | ||||
-rw-r--r-- | sly/live-reload.scm | 55 |
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))))) |