Browse code
Make work + add some bells and whistles
Ed L authored on 06/01/2018 12:34:12
Showing 6 changed files
Showing 6 changed files
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))))) |