forked from Shirakumo/trial
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathevent-loop.lisp
More file actions
152 lines (125 loc) · 5.6 KB
/
event-loop.lisp
File metadata and controls
152 lines (125 loc) · 5.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#|
This file is a part of trial
(c) 2016 Shirakumo http://tymoon.eu (shinmera@tymoon.eu)
Author: Nicolas Hafner <shinmera@tymoon.eu>
|#
(in-package #:org.shirakumo.fraf.trial)
(defgeneric add-handler (handler handler-container))
(defgeneric remove-handler (handler handler-container))
(defgeneric handle (event handler))
;; Default priority
(defmethod priority (object)
0)
(defmethod handle :around (event handler)
(with-simple-restart (abort "Don't handle ~a in ~a." event handler)
(call-next-method)))
(defclass handler-container ()
((handlers :initform () :accessor handlers)))
(defmethod handle (event (container handler-container))
(dolist (handler (handlers container))
(handle event handler)))
(defmethod add-handler (handler (container handler-container))
(setf (handlers container)
(sort (cons handler (delete handler (handlers container) :test #'matches))
#'> :key #'priority)))
(defmethod add-handler ((handlers list) (container handler-container))
(let ((handlers (copy-list handlers)))
(loop for cons on (handlers container)
for handler = (find (car cons) handlers :test #'matches)
do (when handler
(setf (car cons) handler)
(setf handlers (delete handler handlers))))
(setf (handlers container) (sort (nconc handlers (handlers container)) #'> :key #'priority)))
handlers)
(defmethod remove-handler (handler (container handler-container))
(setf (handlers container)
(delete handler (handlers container) :test #'matches))
handler)
(defmethod remove-handler ((handlers list) (container handler-container))
(setf (handlers container) (delete-if (lambda (el)
(find el handlers :test #'matches))
(handlers container)))
handlers)
(defmethod add-handler ((source handler-container) (container handler-container))
(add-handler (handlers source) container)
source)
(defmethod remove-handler ((source handler-container) (container handler-container))
(remove-handler (handlers source) container))
(defclass event-loop (handler-container)
((queue :initform (make-array 64 :initial-element NIL :adjustable T :fill-pointer 0) :reader queue)
(queue-index :initform 0 :accessor queue-index)))
(defun issue (loop event-type &rest args)
(let ((event (etypecase event-type
(event event-type)
((or class symbol)
(apply #'make-instance event-type args)))))
(vector-push-extend event (queue loop))))
(define-compiler-macro issue (&environment env loop event-type &rest args)
(cond ((and (constantp event-type env)
(listp event-type)
(eql (first event-type) 'quote)
(symbolp (second event-type)))
`(vector-push-extend (make-instance ,event-type ,@args) (queue ,loop)))
(T
(let ((eventg (gensym "EVENT")))
`(let* ((,eventg ,event-type)
(,eventg (etypecase ,eventg
(event ,eventg)
((or class symbol)
(make-instance ,eventg ,@args)))))
(vector-push-extend ,eventg (queue ,loop)))))))
;; FIXME: This will forget events if PROCESS or DISCARD-EVENTS is called
;; recursively (thus resetting the index) and new events are issued
;; beyond the point of the index where the recursive call happens.
;; The check will assume nothing has changed and it'll continue from
;; where it left off, thus missing events before the current index.
(defmethod process ((loop event-loop))
(with-simple-restart (discard-events "Discard all events.")
(loop for i = (1- (incf (queue-index loop)))
while (< i (length (queue loop)))
do (let ((event (aref (queue loop) i)))
(when event
(handle event loop)
(setf (aref (queue loop) i) NIL)))))
(setf (fill-pointer (queue loop)) 0
(queue-index loop) 0))
(defun discard-events (loop)
(loop for i = (1- (incf (queue-index loop)))
while (< i (length (queue loop)))
do (setf (aref (queue loop) i) NIL))
(setf (fill-pointer (queue loop)) 0
(queue-index loop) 0))
(defmethod handle (event (loop event-loop))
(with-simple-restart (skip-event "Skip handling the event entirely.")
(call-next-method)))
;; Force adding the loop directly.
(defmethod add-handler ((loop event-loop) (container handler-container))
(setf (handlers container) (cons loop (delete loop (handlers container) :test #'matches)))
loop)
(defmethod remove-handler ((loop event-loop) (container handler-container))
(setf (handlers container) (delete loop (handlers container) :test #'matches))
loop)
(defclass handler ()
((name :initarg :name :accessor name)
(event-type :initarg :event-type :accessor event-type)
(delivery-function :initarg :delivery-function :accessor delivery-function)
(priority :initarg :priority :accessor priority))
(:default-initargs
:name (error "NAME needed.")
:event-type 'event
:delivery-function (error "DELIVERY-FUNCTION needed.")
:priority 0))
(defmethod matches ((a handler) (b handler))
(eql (name a) (name b)))
(defmethod handle :around (event (handler handler))
(when (typep event (event-type handler))
(call-next-method)))
(defmethod handle (event (handler handler))
(funcall (delivery-function handler) event))
(defclass event ()
())
(defclass tick (event)
((tt :initarg :tt :accessor tt)
(dt :initarg :dt :accessor dt)))
(defclass class-changed (event)
((changed-class :initarg :changed-class :accessor changed-class)))