git.fiddlerwoaroof.com
Browse code

Miscellaneous changes

Ed Langley authored on 08/04/2018 01:26:06
Showing 5 changed files
... ...
@@ -29,3 +29,4 @@ demo.app
29 29
 NSRect-Expose
30 30
 *.nib
31 31
 .*.sw?
32
+venv
32 33
\ No newline at end of file
... ...
@@ -166,7 +166,7 @@
166 166
                                 "alertButton"
167 167
                                 "profitButton")))
168 168
 
169
-    (load-nib "MainMenu")
169
+    (load-nib "MainMenu.nib")
170 170
     
171 171
     (let ((app-delegate [objc-runtime::ns-app @(delegate)]))
172 172
       (make-button-delegate (value-for-key app-delegate "actionButton")
... ...
@@ -122,11 +122,6 @@
122 122
   (object :pointer)
123 123
   (ivar :pointer))
124 124
 
125
-(defcfun (class-get-instance-variable "class_getInstanceVariable" :library foundation)
126
-    :pointer
127
-  (class :pointer)
128
-  (name :string))
129
-
130 125
 (defcfun (object-get-instance-variable "object_getInstanceVariable" :library foundation)
131 126
     :pointer
132 127
   (object :pointer)
... ...
@@ -170,6 +165,7 @@
170 165
             (push (mem-aref methods :pointer n)
171 166
                   result)))))))
172 167
 
168
+
173 169
 (defun make-nsstring (str)
174 170
   [[#@NSString @(alloc)] @(initWithCString:encoding:) :string str :uint 1])
175 171
 
... ...
@@ -200,18 +196,6 @@
200 196
                              *selector-cache*
201 197
                              (sel-register-name name)))
202 198
 
203
-(defmacro with-selectors ((&rest selector-specs) &body body)
204
-  `(let (,@(mapcar (fw.lu:destructuring-lambda ((sym foreign-selector))
205
-                     `(,sym (ensure-selector ,foreign-selector)))
206
-                   (mapcar (fw.lu:glambda (spec)
207
-                             (:method ((spec symbol))
208
-                               (list spec (normalize-selector-name
209
-                                           (string-downcase spec))))
210
-                             (:method ((spec cons))
211
-                               (list (car spec) (cadr spec))))
212
-                           selector-specs)))
213
-     ,@body))
214
-
215 199
 (defmacro with-objc-classes ((&rest class-defs) &body body)
216 200
   `(let (,@(mapcar (fw.lu:destructuring-lambda ((lisp-name foreign-name))
217 201
                      `(,lisp-name (objc-look-up-class ,foreign-name)))
... ...
@@ -219,6 +203,90 @@
219 203
      ,@body))
220 204
 
221 205
 
206
+
207
+(cffi:defcvar (ns-app "NSApp" :library appkit) :pointer)
208
+
209
+(defclass objc-class ()
210
+  ((%objc-class-name :initarg :name :reader name)
211
+   (%class-pointer :initarg :pointer :reader class-pointer)
212
+   (%cache :initform (make-hash-table :test 'equal) :allocation :class :reader objc-class-cache)))
213
+
214
+(defclass objc-selector ()
215
+  ((%objc-selector-name :initarg :name :reader name)
216
+   (%selector-pointer :initarg :pointer :reader selector-pointer)
217
+   (%args :initarg :args :reader args)
218
+   (%result-type :initarg :result-type :reader result-type)
219
+   (%cache :initform (make-hash-table :test 'equal) :allocation :class :reader objc-selector-cache))
220
+  (:metaclass closer-mop:funcallable-standard-class))
221
+
222
+(defun make-message-lambda-form (args rettype)
223
+  (alexandria:with-gensyms ((target :target))
224
+    (fw.lu:with (arg-syms (mapcar (serapeum:op _ (gensym "arg")) args))
225
+      `(lambda (selector)
226
+         (lambda (,target ,@arg-syms)
227
+           (cffi:foreign-funcall
228
+            "objc_msgSend"
229
+            :pointer ,target
230
+            :pointer selector
231
+            ,@(mapcan #'list args arg-syms)
232
+            ,rettype))))))
233
+
234
+(defmethod initialize-instance :after ((sel objc-selector) &key &allow-other-keys)
235
+  (with-accessors ((pointer selector-pointer)
236
+                   (args args)
237
+                   (rettype result-type))
238
+      sel
239
+    (closer-mop:set-funcallable-instance-function
240
+     sel
241
+     (funcall (compile nil (make-message-lambda-form args rettype))
242
+              pointer))))
243
+
244
+(defgeneric reset-class-cache (class)
245
+  (:method ((class symbol))
246
+    (reset-class-cache (find-class class)))
247
+  (:method ((class class))
248
+    (setf (slot-value (closer-mop:class-prototype class) '%cache)
249
+          (make-hash-table :test 'equal))))
250
+
251
+
252
+(define-condition no-such-objc-class (serious-condition)
253
+  ((%wanted-name :initarg :wanted-name :reader wanted-name))
254
+  (:report (lambda (c s)
255
+             (format s "No such Objective-C class: ~a" (wanted-name c)))))
256
+
257
+(defun %ensure-wrapped-objc-class (name)
258
+  (let* ((class-cache (objc-class-cache (closer-mop:class-prototype (find-class 'objc-class))))
259
+         (cached (gethash name class-cache)))
260
+    (if cached
261
+        cached 
262
+        (let ((objc-class (objc-look-up-class name)))
263
+          (if (null-pointer-p objc-class)
264
+              (error 'no-such-objc-class :wanted-name name)
265
+              (setf (gethash name class-cache)
266
+                    (make-instance 'objc-class
267
+                                   :name name
268
+                                   :pointer objc-class)))))))
269
+
270
+;; TODO: should this error if there is no corresponding selector? Or should we let that fall through to message sending?
271
+(defun %ensure-wrapped-objc-selector (name target-class result-type args)
272
+  (assert (= (count #\: name)
273
+             (length args))
274
+          (name args)
275
+          "Invalid number of arg types for selector ~s" name)
276
+
277
+  (let* ((class-cache (objc-selector-cache (closer-mop:class-prototype (find-class 'objc-selector))))
278
+         (cached (gethash (list name target-class)
279
+                          class-cache)))
280
+    (if cached
281
+        cached 
282
+        (let ((objc-selector (ensure-selector name)))
283
+          (setf (gethash (list name target-class) class-cache)
284
+                (make-instance 'objc-selector
285
+                               :name name
286
+                               :pointer objc-selector
287
+                               :result-type result-type
288
+                               :args args))))))
289
+
222 290
 (defgeneric make-objc-instance (class &rest args)
223 291
   (:method ((class string) &rest args)
224 292
     (apply #'make-objc-instance (objc-look-up-class class) args))
... ...
@@ -233,18 +301,36 @@
233 301
     (with-selectors (alloc init)
234 302
       [[class alloc] init])))
235 303
 
304
+(defun ensure-wrapped-objc-class (name)
305
+  (tagbody
306
+   retry (restart-case (return-from ensure-wrapped-objc-class
307
+                         (%ensure-wrapped-objc-class name))
308
+           (use-value (new)
309
+             :interactive (lambda ()
310
+                            (format t "New Objective-C class name: ")
311
+                            (multiple-value-list (read)))
312
+             :report "Retry with new class name"
313
+             (setf name new)
314
+             (go retry)))))
236 315
 
237
-(cffi:defcvar (ns-app "NSApp" :library appkit) :pointer)
238
-
239
-#|
240
-(uiop:nest (with-selectors (alloc init drain))
241
-           (with-objc-classes ((nsobject "NSAutoreleasepool")))
242
-           (eval-objc (objc-msg-send
243
-                       (objc-msg-send
244
-                        (objc-msg-send nsobject alloc)
245
-                        init)
246
-                       drain)))
247
-(with-selectors (alloc init))
316
+(defmacro with-selectors ((&rest selector-specs) &body body)
317
+  `(let (,@(mapcar (fw.lu:destructuring-lambda ((sym foreign-selector))
318
+                     `(,sym (ensure-selector ,foreign-selector)))
319
+                   (mapcar (fw.lu:glambda (spec)
320
+                             (:method ((spec symbol))
321
+                               (list spec (normalize-selector-name
322
+                                           (string-downcase spec))))
323
+                             (:method ((spec cons))
324
+                               (list (car spec) (cadr spec))))
325
+                           selector-specs)))
326
+     ,@body))
248 327
 
249
-|#
250 328
 
329
+(defmacro with-typed-selectors ((&rest defs) &body body)
330
+  (let ((expanded-defs (loop for ((name objc-name) args ret-type) in defs
331
+                          collect
332
+                            `((,name (&rest r) (apply ,name r))
333
+                              (,name (%ensure-wrapped-objc-selector ,objc-name ',ret-type ',args))))))
334
+    `(let (,@(mapcar #'second expanded-defs))
335
+       (flet (,@(mapcar #'first expanded-defs))
336
+         ,@body))))
... ...
@@ -22,4 +22,5 @@
22 22
    #:ns-point
23 23
    #:ns-size
24 24
    #:ns-rect
25
-   #:get-method-names))
25
+   #:get-method-names
26
+   #:ensure-wrapped-objc-class))
... ...
@@ -1,5 +1,10 @@
1 1
 (in-package :objc-runtime)
2 2
 
3
+#+ccl
4
+(defgeneric send-message (object message &rest args)
5
+  (:method ((object ccl:macptr) (message (eql 'alloc)) &rest args)
6
+    (apply #'objc-msg-send object (ensure-selector "alloc") args)))
7
+
3 8
 (defun read-until (test symbol-prefix &optional stop-before-chars)
4 9
   "Read from a string until"
5 10
   (lambda (s c b)