Skip to content

Commit 6b3681f

Browse files
authored
Rewrite error handling (#150)
* Rewrite error handling Removes old utilities: DEBUG-MODE-P, EXIT-DEBUG-MODE, %RESTART slot, *RESTART-FRAMES*, GL-CATCH, DRAW-SKETCH. Adds %SETUP-CALLED slot, WITH-GL-DRAW, MAYBE-CHANGE-VIEWPORT, ON-ERROR generic function (can be exported for the user to extend). * DEBUG-KEY-PRESSED is set only for :MOUSEBUTTONUP events, so that from one click restarts are shown only once. * ON-ERROR is called after unwinding the stack when the debug key is not pressed. It accepts three parameters: the sketch instance, a keyword that indicates where an error occured (:SETUP or :DRAW), and the error object. The default method draws a red screen and sets (ENV-RED-SCREEN *ENV*) to T. * If debug key is pressed, restarts are shown. A :RED-SCREEN restart is established, which calls ON-ERROR as if the debug key was not pressed. * Refactor HANDLER-BIND + TAGBODY into a macro * More refactoring * More WITH-ERROR-HANDLING/STAGE refactoring
1 parent b1cee59 commit 6b3681f

File tree

3 files changed

+75
-57
lines changed

3 files changed

+75
-57
lines changed

src/controllers.lisp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@
103103
state timestamp button x y)
104104
(with-slots (%env) (%sketch instance)
105105
(when (env-red-screen %env)
106-
(setf (env-debug-key-pressed %env) t))))
106+
(when (eq state :mousebuttonup)
107+
(setf (env-debug-key-pressed %env) t)))))
107108

108109
;;; Keyboard
109110

src/environment.lisp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,6 @@
7575
(gl:clear :color-buffer :depth-buffer)
7676
(gl:flush)))
7777

78-
(defun debug-mode-p ()
79-
(and (env-red-screen *env*)
80-
(env-debug-key-pressed *env*)))
81-
82-
(defun exit-debug-mode ()
83-
(setf (env-red-screen *env*) nil
84-
(env-debug-key-pressed *env*) nil))
85-
8678
(defmacro with-environment (env &body body)
8779
`(let ((*env* ,env))
8880
,@body))

src/sketch.lisp

Lines changed: 73 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,9 @@
2626
(defparameter *default-height* 400
2727
"The default height of sketch window")
2828

29-
(defparameter *restart-frames* 2)
30-
3129
(defclass sketch ()
3230
((%env :initform (make-env) :reader sketch-%env)
33-
(%restart :initform *restart-frames*)
31+
(%setup-called :initform nil :accessor sketch-%setup-called)
3432
(%viewport-changed :initform t)
3533
(%entities :initform (make-hash-table) :accessor sketch-%entities)
3634
(%window :initform nil :accessor sketch-%window :initarg :window)
@@ -151,28 +149,55 @@
151149
((instance sketch) added-slots discarded-slots property-list &rest initargs)
152150
(declare (ignore added-slots discarded-slots property-list))
153151
(apply #'prepare instance initargs)
154-
(setf (slot-value instance '%restart) *restart-frames*)
152+
(setf (sketch-%setup-called instance) nil)
155153
(setf (slot-value instance '%entities) (make-hash-table)))
156154

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)))))
158199

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
176201

177202
(defmacro with-sketch ((sketch) &body body)
178203
`(with-environment (sketch-%env ,sketch)
@@ -181,34 +206,34 @@
181206
(with-identity-matrix
182207
,@body)))))
183208

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
186217
(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)))
189219
(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))))))
212237

213238
(defmethod kit.sdl2:render ((instance sketch))
214239
(kit.sdl2:render (sketch-%window instance)))

0 commit comments

Comments
 (0)