|
26 | 26 | (defparameter *default-height* 400 |
27 | 27 | "The default height of sketch window") |
28 | 28 |
|
29 | | -(defparameter *restart-frames* 2) |
30 | | - |
31 | 29 | (defclass sketch () |
32 | 30 | ((%env :initform (make-env) :reader sketch-%env) |
33 | | - (%restart :initform *restart-frames*) |
| 31 | + (%setup-called :initform nil :accessor sketch-%setup-called) |
34 | 32 | (%viewport-changed :initform t) |
35 | 33 | (%entities :initform (make-hash-table) :accessor sketch-%entities) |
36 | 34 | (%window :initform nil :accessor sketch-%window :initarg :window) |
|
151 | 149 | ((instance sketch) added-slots discarded-slots property-list &rest initargs) |
152 | 150 | (declare (ignore added-slots discarded-slots property-list)) |
153 | 151 | (apply #'prepare instance initargs) |
154 | | - (setf (slot-value instance '%restart) *restart-frames*) |
| 152 | + (setf (sketch-%setup-called instance) nil) |
155 | 153 | (setf (slot-value instance '%entities) (make-hash-table))) |
156 | 154 |
|
157 | | -;;; Rendering |
| 155 | +;;; Error handling |
| 156 | + |
| 157 | +(defvar *%unwind-and-call-on-error-function*) |
| 158 | +(defmacro unwind-and-call-on-error () `(funcall *%unwind-and-call-on-error-function*)) |
| 159 | + |
| 160 | +(defmethod on-error-handler ((sketch sketch) stage error) |
| 161 | + (declare (ignorable sketch stage)) |
| 162 | + (when (env-debug-key-pressed *env*) |
| 163 | + (with-simple-restart (:red-screen "Show red screen") |
| 164 | + (signal error))) |
| 165 | + (unwind-and-call-on-error)) |
| 166 | + |
| 167 | +(defmethod on-error ((sketch sketch) stage error) |
| 168 | + (declare (ignorable sketch)) |
| 169 | + (background (ecase stage |
| 170 | + (:setup (rgb 0.4 0.2 0.1)) |
| 171 | + (:draw (rgb 0.7 0 0)))) |
| 172 | + (with-font (make-error-font) |
| 173 | + (with-identity-matrix |
| 174 | + (text (format nil "Error in ~A~%---~%~a~%---~%Click for restarts." stage error) 20 20))) |
| 175 | + (setf (env-red-screen *env*) t)) |
| 176 | + |
| 177 | +(defmacro with-error-handling ((sketch) &body body) |
| 178 | + (alexandria:with-gensyms (%error %stage) |
| 179 | + `(let (,%error ,%stage) |
| 180 | + (tagbody |
| 181 | + (handler-bind ((error |
| 182 | + (lambda (e) |
| 183 | + (setf ,%error e) |
| 184 | + (let ((*%unwind-and-call-on-error-function* |
| 185 | + (lambda () (go :error)))) |
| 186 | + (on-error-handler ,sketch |
| 187 | + ,%stage |
| 188 | + ,%error))))) |
| 189 | + (macrolet ((with-stage (stage &body body) |
| 190 | + `(progn |
| 191 | + (setf ,',%stage ,stage) |
| 192 | + ,@body))) |
| 193 | + ,@body) |
| 194 | + (go :end)) |
| 195 | + :error |
| 196 | + (on-error ,sketch ,%stage ,%error) |
| 197 | + :end |
| 198 | + (setf (env-debug-key-pressed *env*) nil))))) |
158 | 199 |
|
159 | | -(defmacro gl-catch (error-color &body body) |
160 | | - `(handler-case |
161 | | - (progn |
162 | | - ,@body) |
163 | | - (error (e) |
164 | | - (progn |
165 | | - (background ,error-color) |
166 | | - (with-font (make-error-font) |
167 | | - (with-identity-matrix |
168 | | - (text (format nil "ERROR~%---~%~a~%---~%Click for restarts." e) 20 20))) |
169 | | - (setf %restart *restart-frames* |
170 | | - (env-red-screen *env*) t))))) |
171 | | - |
172 | | -(defun draw-sketch (sketch) |
173 | | - (start-draw) |
174 | | - (draw sketch) |
175 | | - (end-draw)) |
| 200 | +;;; Rendering |
176 | 201 |
|
177 | 202 | (defmacro with-sketch ((sketch) &body body) |
178 | 203 | `(with-environment (sketch-%env ,sketch) |
|
181 | 206 | (with-identity-matrix |
182 | 207 | ,@body))))) |
183 | 208 |
|
184 | | -(defmethod kit.sdl2:render ((win sketch-window) &aux (instance (%sketch win))) |
185 | | - (with-slots (%env %restart width height copy-pixels %viewport-changed) instance |
| 209 | +(defmacro with-gl-draw (&body body) |
| 210 | + `(progn |
| 211 | + (start-draw) |
| 212 | + ,@body |
| 213 | + (end-draw))) |
| 214 | + |
| 215 | +(defun maybe-change-viewport (sketch) |
| 216 | + (with-slots (%env %viewport-changed width height) sketch |
186 | 217 | (when %viewport-changed |
187 | | - (kit.gl.shader:uniform-matrix |
188 | | - (env-programs %env) :view-m 4 (vector (env-view-matrix %env))) |
| 218 | + (kit.gl.shader:uniform-matrix (env-programs %env) :view-m 4 (vector (env-view-matrix %env))) |
189 | 219 | (gl:viewport 0 0 width height) |
190 | | - (setf %viewport-changed nil)) |
191 | | - (with-sketch (instance) |
192 | | - (unless copy-pixels |
193 | | - (background (gray 0.4))) |
194 | | - ;; Restart sketch on setup and when recovering from an error. |
195 | | - (when (> %restart 0) |
196 | | - (decf %restart) |
197 | | - (when (zerop %restart) |
198 | | - (gl-catch (rgb 1 1 0.3) |
199 | | - (start-draw) |
200 | | - (setup instance) |
201 | | - (end-draw)))) |
202 | | - ;; If we're in the debug mode, we exit from it immediately, |
203 | | - ;; so that the restarts are shown only once. Afterwards, we |
204 | | - ;; continue presenting the user with the red screen, waiting for |
205 | | - ;; the error to be fixed, or for the debug key to be pressed again. |
206 | | - (if (debug-mode-p) |
207 | | - (progn |
208 | | - (exit-debug-mode) |
209 | | - (draw-sketch instance)) |
210 | | - (gl-catch (rgb 0.7 0 0) |
211 | | - (draw-sketch instance)))))) |
| 220 | + (setf %viewport-changed nil)))) |
| 221 | + |
| 222 | +(defmethod kit.sdl2:render ((win sketch-window) &aux (sketch (%sketch win))) |
| 223 | + (maybe-change-viewport sketch) |
| 224 | + (with-sketch (sketch) |
| 225 | + (with-gl-draw |
| 226 | + (with-error-handling (sketch) |
| 227 | + (unless (sketch-copy-pixels sketch) |
| 228 | + (background (gray 0.4))) |
| 229 | + (when (or (env-red-screen *env*) |
| 230 | + (not (sketch-%setup-called sketch))) |
| 231 | + (setf (env-red-screen *env*) nil |
| 232 | + (sketch-%setup-called sketch) t) |
| 233 | + (with-stage :setup |
| 234 | + (setup sketch))) |
| 235 | + (with-stage :draw |
| 236 | + (draw sketch)))))) |
212 | 237 |
|
213 | 238 | (defmethod kit.sdl2:render ((instance sketch)) |
214 | 239 | (kit.sdl2:render (sketch-%window instance))) |
|
0 commit comments