git.fiddlerwoaroof.com
Browse code

feat(notifications): initial support for distributed notifications

Edward Langley authored on 18/09/2022 10:15:42
Showing 3 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,70 @@
1
+(defpackage :objc.notification
2
+  (:use :cl )
3
+  (:export
4
+   #:main-loop-ticker
5
+   #:dnc
6
+   #:observe-notifications
7
+   #:*mailbox*))
8
+(in-package :objc.notification)
9
+(serapeum:eval-always
10
+  (named-readtables:in-readtable :objc-readtable))
11
+
12
+(defun main-loop-ticker ()
13
+  (let ((main-run-loop [#@NSRunLoop @(mainRunLoop)]))
14
+    (loop do
15
+      (sleep 0.1)
16
+      (trivial-main-thread:with-body-in-main-thread (:blocking t)
17
+        (objc-runtime::tick-ns-runloop main-run-loop
18
+                                       0.1)))))
19
+
20
+(defun dnc ()
21
+  [#@NSDistributedNotificationCenter
22
+   @(defaultCenter)])
23
+
24
+(defvar *mailbox*)
25
+(cffi:defcallback handle-notification :void
26
+    ((_ :pointer) (__ :pointer) (notification :pointer))
27
+  (declare (ignore _ __))
28
+  (sb-concurrency:send-message
29
+   *mailbox*
30
+   (objc-runtime.data-extractors:extract-from-objc
31
+    [notification @(userInfo)])))
32
+
33
+(defvar *notification-handler*)
34
+(defun setup-delegate ()
35
+  (if (boundp '*notification-handler*)
36
+      *notification-handler*
37
+      (let ((delegate-class
38
+              (objc-runtime::objc-allocate-class-pair
39
+               #@NSObject
40
+               (format nil "FWNotificationHandler~a"
41
+                       (gensym))
42
+               0)))
43
+        (objc-runtime::class-add-method
44
+         delegate-class
45
+         @(handle-notification:)
46
+         (cffi:callback handle-notification)
47
+         "v@:@")
48
+        (setf *mailbox*
49
+              (sb-concurrency:make-mailbox)
50
+
51
+              *notification-handler*
52
+              [[delegate-class @(alloc)] @(init)]))))
53
+
54
+(define-condition notifications-not-initialized (error)
55
+  ())
56
+
57
+(defun observe-notifications (dnc notification-name)
58
+  (tagbody start
59
+     (restart-case
60
+         (if (boundp '*notification-handler*)
61
+             [dnc @(addObserver:selector:name:object:)
62
+                  :pointer *notification-handler*
63
+                  :pointer @(handle-notification:)
64
+                  :pointer (objc-runtime::make-nsstring
65
+                            notification-name)
66
+                  :pointer (cffi:null-pointer)]
67
+             (error 'notifications-not-initialized))
68
+       (setup-and-retry ()
69
+         (setup-delegate)
70
+         (go start)))))
... ...
@@ -34,6 +34,15 @@
34 34
                :data-lens)
35 35
   :components ((:file "scripting-bridge")))
36 36
 
37
+(defsystem :objc-runtime/notifications
38
+  :description ""
39
+  :author "Ed L <edward@elangley.org>"
40
+  :license "MIT"
41
+  :depends-on (:objc-runtime
42
+               :trivial-main-thread
43
+               (:require :sb-concurrency))
44
+  :components ((:file "notification")))
45
+
37 46
 (defsystem :objc-runtime/clim-objc-browser
38 47
   :description ""
39 48
   :author "Ed L <edward@elangley.org>"
... ...
@@ -142,6 +142,10 @@
142 142
     :pointer
143 143
   (method :pointer))
144 144
 
145
+(defcfun (method-get-type-encoding "method_getTypeEncoding")
146
+    :string
147
+  (method :pointer))
148
+
145 149
 (defcfun (sel-get-name "sel_getName")
146 150
     :string
147 151
   (sel o-selector))
... ...
@@ -414,3 +418,17 @@
414 418
     `(let (,@(mapcar #'second expanded-defs))
415 419
        (flet (,@(mapcar #'first expanded-defs))
416 420
          ,@body))))
421
+
422
+(defun description (nsobject)
423
+  [nsobject @(description)]@)
424
+
425
+(defun future-ns-date (seconds)
426
+  [[#@NSDate @(alloc)]
427
+   @(initWithTimeIntervalSinceNow:)
428
+   :double (coerce seconds 'double-float)])
429
+
430
+(defun tick-ns-runloop (run-loop &optional (time 0.5d0))
431
+  (let ((date (future-ns-date time)))
432
+    (unwind-protect [run-loop @(runUntilDate:)
433
+                              :pointer date]
434
+      [date @(release)])))