;;; Sly ;;; Copyright (C) 2014 David Thompson ;;; ;;; This program is free software: you can redistribute it and/or ;;; modify it under the terms of the GNU General Public License as ;;; published by the Free Software Foundation, either version 3 of the ;;; License, or (at your option) any later version. ;;; ;;; This program is distributed in the hope that it will be useful, ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;; General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with this program. If not, see ;;; . ;;; Commentary: ;; ;; Live asset reloading. ;; ;;; Code: (define-module (sly live-reload) #:use-module (srfi srfi-1) #:use-module (sly agenda) #:use-module (sly coroutine) #:use-module (sly game) #:use-module (sly signal) #:export (watch-files with-live-reload)) (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 ;; Game hasn't started yet. ((not (signal-ref game-started?)) #f) ;; 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 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) (watch-files (list file-name) (lambda () (apply proc file-name args)))))