git.fiddlerwoaroof.com
Browse code

Make work + add some bells and whistles

Ed L authored on 06/01/2018 12:34:12
Showing 6 changed files
... ...
@@ -18,3 +18,4 @@ GTAGS
18 18
 *.old
19 19
 nsrect-expose.c
20 20
 test.c
21
+.[#]*
21 22
new file mode 100644
... ...
@@ -0,0 +1,18 @@
1
+* Intro
2
+  
3
+CCL and LispWorks and other implementations have their own bridges to
4
+the objective-c runtime.  This project is an attempt to create a
5
+bridge that only uses CFFI so that arbitrary lisp implementations can
6
+produce native mac GUIs.  In the long run, I hope to use this as the
7
+basis for a new mac-native backend for McClim: but we'll see if that
8
+ever happens.
9
+
10
+* Installing
11
+
12
+1. clone fwoar.lisputils from
13
+   https://github.com/fiddlerwoaroof/fwoar.lisputils and put it
14
+   somewhere quicklisp can find it (e.g. ~/quicklisp/local-projects)
15
+2. run the demo:
16
+   #+BEGIN_SRC sh
17
+make run CCL=/path/to/ccl
18
+   #+END_SRC
... ...
@@ -17,16 +17,25 @@
17 17
   (b :char)
18 18
   (c :boolean))
19 19
 
20
+(cffi:defcfun (init-with-frame "initWithFrame")
21
+    :pointer
22
+  (thing :pointer)
23
+  (rect :pointer))
24
+
20 25
 (cffi:defcfun (print-rect "printRect")
21 26
     :void
22 27
   (rect (:struct objc-runtime::ns-rect)))
23 28
 
24
-#+null
25 29
 (cffi:defcfun (set-uncaught-exception-handler "set_uncaught_exception_handler"
26 30
                                               :library objc-runtime::expose-stuff)
27 31
     :void
28 32
   (cb :pointer))
29 33
 
34
+(defun value-for-key (thing key)
35
+  (with-selectors ((vfk "valueForKey:"))
36
+    (let ((key (make-nsstring key)))
37
+      [thing vfk :string key])))
38
+
30 39
 (defun call-with-rect (x y w h cb)
31 40
   (check-type x real)
32 41
   (check-type y real)
... ...
@@ -80,8 +89,39 @@
80 89
                               ,(coerce h 'double-float)))
81 90
                            '(:struct objc-runtime:ns-rect)))
82 91
 
92
+(defun make-nsstring (str)
93
+  (with-selectors (alloc (init-with-encoding "initWithCString:length:"))
94
+    [[#@NSString alloc] init-with-encoding :string str :uint (length str)]))
95
+
96
+(defun show-alert (message)
97
+  (with-selectors ((set-message-text "setMessageText:")
98
+                   (set-informative-text "setInformativeText:")
99
+                   (add-button-with-title "addButtonWithTitle:")
100
+                   (run-modal "runModal")
101
+                   alloc init)
102
+    (let ((alert [[#@NSAlert alloc] init]))
103
+      [alert set-message-text :pointer (make-nsstring message)]
104
+      [alert set-informative-text :pointer (make-nsstring "Informative text.")]
105
+      [alert add-button-with-title :pointer (make-nsstring "Cancel")]
106
+      [alert add-button-with-title :pointer (make-nsstring "OK")]
107
+      [alert run-modal])))
108
+
109
+(cffi:defcallback button-action :void ((a :pointer) (b :pointer) (sender :pointer))
110
+  (declare (ignore a b sender))
111
+  (show-alert "Hello There!"))
112
+
113
+(defun make-button-delegate (button)
114
+  (let ((my-class (objc-runtime::objc-allocate-class-pair #@NSObject "ButtonDel" 0)))
115
+    (with-selectors ((do-magic "doMagic:") (set-target "setTarget:") (set-action "setAction:")
116
+                     alloc init)
117
+      (objc-runtime::class-add-method my-class do-magic (cffi:callback button-action)
118
+                                      "v@:@")
119
+      (fw.lu:prog1-bind (result [[my-class alloc] init])
120
+        [button set-target :pointer result]
121
+        [button set-action :pointer do-magic]))))
122
+
83 123
 (defun main ()
84
-  (break)
124
+  ;; (break)
85 125
   (trivial-main-thread:with-body-in-main-thread ()
86 126
     (with-selectors ((shared-application "sharedApplication")
87 127
                      (process-info "processInfo")
... ...
@@ -98,6 +138,11 @@
98 138
                      (init-with-title "initWithTitle:action:keyEquivalent:")
99 139
                      (set-submenu "setSubmenu:")
100 140
                      (init-with-encoding "initWithCString:length:")
141
+                     (content-view "contentView")
142
+                     (add-subview "addSubview:")
143
+                     (set-target "setTarget:")
144
+                     (set-action "setAction:")
145
+                     (set-title "setTitle:")
101 146
                      terminate?
102 147
                      ;; (application-should-terminate "applicationShouldTerminate:")
103 148
                      ;; (set-delegate "setDelegate:")
... ...
@@ -123,12 +168,19 @@
123 168
           [objc-runtime::ns-app set-main-menu :pointer menubar] )
124 169
 
125 170
         (with-point (p (20 20))
126
-          (let* ((the-window [#@NSWindow alloc])
127
-                 (foreign-rect (make-rect 10 10 120 120)))
171
+          (let* ((foreign-rect (make-rect 10 10 120 120))
172
+                 (the-window (init-window [#@NSWindow alloc] foreign-rect 1 2 nil))
173
+                 (the-button (init-with-frame [#@NSButton alloc] (make-rect 10 10 100 100))))
174
+            (format t "~{~&~a~%~}"
175
+                    (sort (objc-runtime::get-method-names (objc-runtime::object-get-class [the-window content-view]))
176
+                          #'string-lessp))
128 177
             (format t "~&My rect: ~s~%"
129 178
                     (cffi:convert-from-foreign foreign-rect
130 179
                                                '(:struct objc-runtime::ns-rect)))
131
-            (init-window the-window foreign-rect 1 2 nil)
180
+            
181
+            [the-button set-title :pointer (make-nsstring "Click me!")]
182
+            (make-button-delegate the-button)
183
+            [(value-for-key the-window "contentView") add-subview :pointer the-button]
132 184
             [the-window cascade-top-left-from-point :pointer p]
133 185
             [the-window set-title :pointer application-name]
134 186
             [the-window make-key-and-order-front :pointer (cffi:null-pointer)]
... ...
@@ -21,3 +21,6 @@ EXPORT id initWindow(NSWindow *window, NSRect *rect, char a, char b, Boolean c)
21 21
                            styleMask: a
22 22
                              backing: b
23 23
                                defer: c];}
24
+EXPORT id initWithFrame(id thing, NSRect *rect) {
25
+  printf("Got a rect: (%f %f), (%f %f)\n", rect->size.width, rect->size.height, rect->origin.x, rect->origin.y);
26
+  return [thing initWithFrame: *rect];}
... ...
@@ -83,6 +83,42 @@
83 83
     :string
84 84
   (sel o-selector))
85 85
 
86
+(defcfun (class-get-instance-variable "class_getInstanceVariable" :library foundation)
87
+    :pointer
88
+  (cls o-class)
89
+  (name :string))
90
+
91
+(defcfun (class-get-instance-variable "class_addMethod" :library foundation)
92
+    :pointer
93
+  (cls o-class)
94
+  (sel :pointer)
95
+  (imp :pointer)
96
+  (type :string))
97
+
98
+(defcfun (object-get-class "object_getClass" :library foundation)
99
+    :pointer
100
+  (object :pointer))
101
+
102
+(defcfun (object-get-ivar "object_getIvar" :library foundation)
103
+    :pointer
104
+  (object :pointer)
105
+  (ivar :pointer))
106
+
107
+(defcfun (class-get-property "class_getProperty" :library foundation)
108
+    :pointer
109
+  (cls o-class)
110
+  (name :string))
111
+
112
+(defcfun (property-copy-attribute-value "property_copyAttributeValue" :library foundation)
113
+    :string
114
+  (prop :pointer)
115
+  (name :string))
116
+
117
+
118
+(defcfun (property-get-attributes "property_getAttributes" :library foundation)
119
+    :string
120
+  (prop :pointer))
121
+
86 122
 (defgeneric get-methods (class)
87 123
   (:method ((class string))
88 124
     (get-methods (objc-look-up-class class)))
... ...
@@ -15,7 +15,8 @@
15 15
                           c b
16 16
                           (let ((class-name (coerce (loop for c = (read-char s nil nil t)
17 17
                                                        until (or (null c)
18
-                                                                 (serapeum:whitespacep c))
18
+                                                                 (serapeum:whitespacep c)
19
+                                                                 (member c '(#\) #\(  #\[ #\])))
19 20
                                                        collect c)
20 21
                                                     'string)))
21 22
                             `(objc-look-up-class ,class-name)))))