48df527e |
(defpackage :demo-app
(:use :cl :objc-runtime)
|
5e082198 |
(:export
#:get-method-names))
|
48df527e |
(in-package :demo-app)
(named-readtables:in-readtable :objc-readtable)
(cffi:defcallback exception-handler :void ((exception :pointer))
(with-selectors (reason)
(format t "~&Exxception: ~a~%" [exception reason])
(values)))
|
ae1d3bf8 |
(defun init-window (window rect a b c)
(format t "~&got rect: ~s" rect)
(cffi:foreign-funcall "objc_msgSend"
:pointer window
:pointer @(initWithContentRect:)
:pointer window
(:struct objc-runtime::ns-rect) rect
:char a
:char b
:boolean c
:pointer))
|
48df527e |
|
58a4213c |
(defmacro selector-lambda (selector &rest args)
`(lambda (receiver)
[receiver ,selector ,@args]))
|
ae1d3bf8 |
(defun init-with-frame (thing rect)
(format t "~&got rect: ~s" rect)
(cffi:foreign-funcall "objc_msgSend"
:pointer thing
:pointer @(initWithFrame:)
(:struct objc-runtime::ns-rect) rect
:pointer))
|
4abbc169 |
|
49baa990 |
(cffi:defcfun (print-rect "printRect")
:void
|
58a4213c |
(rect (:struct objc-runtime:ns-rect)))
|
49baa990 |
|
412089a6 |
#+(or)
|
48df527e |
(cffi:defcfun (set-uncaught-exception-handler "set_uncaught_exception_handler"
:library objc-runtime::expose-stuff)
:void
(cb :pointer))
|
4abbc169 |
(defun value-for-key (thing key)
(with-selectors ((vfk "valueForKey:"))
|
6b08248f |
(let ((key (objc-runtime::make-nsstring key)))
|
4abbc169 |
[thing vfk :string key])))
|
48df527e |
(defun call-with-rect (x y w h cb)
(check-type x real)
(check-type y real)
(check-type w real)
(check-type h real)
|
49baa990 |
(cffi:with-foreign-object (rect '(:struct objc-runtime::ns-rect))
|
48df527e |
(cffi:with-foreign-slots (((:pointer ns-rect-origin) (:pointer ns-rect-size))
|
49baa990 |
rect (:struct objc-runtime::ns-rect))
|
48df527e |
(cffi:with-foreign-slots ((ns-point-x ns-point-y) ns-rect-origin (:struct ns-point))
(setf ns-point-x (coerce x 'double-float)
ns-point-y (coerce y 'double-float)))
(cffi:with-foreign-slots ((ns-size-width ns-size-height)
ns-rect-size (:struct ns-size))
(setf ns-size-width (coerce w 'double-float)
ns-size-height (coerce h 'double-float))))
(funcall cb rect)))
(defun call-with-point (x y cb)
(check-type x real)
(check-type y real)
(cffi:with-foreign-object (point '(:struct ns-point))
(cffi:with-foreign-slots ((ns-point-x ns-point-y) point (:struct ns-point))
(setf ns-point-x (coerce x 'double-float)
ns-point-y (coerce y 'double-float)))
(funcall cb point)))
(defmacro with-rect ((rect (x y) (w h)) &body body)
`(call-with-rect ,x ,y ,w ,h
(lambda (,rect)
,@body)))
(defmacro with-point ((point (x y)) &body body)
`(call-with-point ,x ,y
(lambda (,point)
,@body)))
(defun make-rect (x y w h)
(check-type x real)
(check-type y real)
(check-type w real)
(check-type h real)
(cffi:convert-to-foreign `(ns-rect-origin
(objc-runtime:ns-point-x
,(coerce x 'double-float)
objc-runtime:ns-point-y
,(coerce y 'double-float))
ns-rect-size
(objc-runtime:ns-size-width
,(coerce w 'double-float)
objc-runtime:ns-size-height
,(coerce h 'double-float)))
'(:struct objc-runtime:ns-rect)))
|
fbc1e93a |
(defun show-alert (message &optional (informative-text "Informative Text!"))
|
6b08248f |
(let ((alert [[#@NSAlert @(alloc)] @(init)]))
|
fbc1e93a |
[alert @(setMessageText:) :pointer (objc-runtime::make-nsstring message)]
[alert @(setInformativeText:) :pointer (objc-runtime::make-nsstring informative-text)]
|
6b08248f |
[alert @(addButtonWithTitle:) :pointer @"OK"]
[alert @(addButtonWithTitle:) :pointer @"Cancel"]
[alert @(runModal)]))
|
4abbc169 |
|
fbc1e93a |
(cffi:defcallback do-things-action :void ((a :pointer) (b :pointer) (sender :pointer))
(declare (ignore a b sender))
(show-alert "Starting Swank"
"Loading Quicklisp from ~/quicklisp/setup.lisp + starting swank")
(load "~/quicklisp/setup.lisp")
(funcall (intern "QUICKLOAD" (find-package :QL)) :swank)
(funcall (intern "CREATE-SERVER" (find-package :swank)) :port 5060 :dont-close t)
|
ae1d3bf8 |
|
fbc1e93a |
(show-alert "Started swank on 5060"))
(cffi:defcallback alert-action :void ((a :pointer) (b :pointer) (sender :pointer))
|
4abbc169 |
(declare (ignore a b sender))
(show-alert "Hello There!"))
|
fbc1e93a |
(cffi:defcallback profit-action :void ((a :pointer) (b :pointer) (sender :pointer))
(declare (ignore a b sender))
(show-alert "That Was Profitable!"))
|
279ed7b0 |
(defun alloc-init (cls)
[[cls @(alloc)] @(init)])
|
fbc1e93a |
(defun make-button-delegate (button cb)
|
58a4213c |
(objc-runtime.data-extractors:objc-typecase button
(#@NSButton (let ((my-class (objc-runtime::objc-allocate-class-pair #@NSObject "ButtonDel" 0)))
(objc-runtime::class-add-method my-class @(doMagic) cb "v@:@")
(fw.lu:prog1-bind (result (alloc-init my-class))
[button @(setTarget:) :pointer result]
[button @(setAction:) :pointer @(doMagic)])))
(t (format t "~&The button is not a button~%"))))
|
4abbc169 |
|
fbc1e93a |
(defun make-app-delegate-class (outlets)
(let ((app-delegate-class (objc-runtime::objc-allocate-class-pair
#@NSObject "AppDelegate" 0)))
|
279ed7b0 |
(objc-runtime:add-pointer-ivar app-delegate-class "window")
(objc-runtime:add-pointer-ivar app-delegate-class "delegate")
|
fbc1e93a |
(loop for outlet in outlets do
|
279ed7b0 |
(objc-runtime:add-pointer-ivar app-delegate-class outlet))
|
fbc1e93a |
app-delegate-class))
|
58a4213c |
(defun make-app-delegate-class-with-props (foo outlets)
(let ((app-delegate-class (objc-runtime::objc-allocate-class-pair
#@NSObject foo 0)))
(objc-runtime:add-pointer-ivar app-delegate-class "window")
(objc-runtime:add-pointer-ivar app-delegate-class "delegate")
(loop for outlet in outlets do
(objc-runtime:add-pointer-ivar app-delegate-class outlet))
app-delegate-class))
|
279ed7b0 |
|
fbc1e93a |
(defun load-nib (name)
;; find and activate the nib
(let* ((bundle [#@NSBundle @(mainBundle)])
(nib [[#@NSNib @(alloc)] @(initWithNibNamed:bundle:)
|
58a4213c |
:pointer (objc-runtime::make-nsstring name)
:pointer bundle]))
|
fbc1e93a |
(cffi:with-foreign-object (p :pointer)
;; TODO: is dropping p a problem here? The docs say something relevant.
;; must investigate.
[nib @(instantiateWithOwner:topLevelObjects:)
:pointer objc-runtime::ns-app
:pointer p])))
|
279ed7b0 |
;#+null
|
bb1b4ba9 |
(defun main ()
|
eb0a174c |
#+sbcl
(sb-int:set-floating-point-modes :traps '())
|
bb1b4ba9 |
(load "~/quicklisp/setup.lisp")
(funcall (intern "QUICKLOAD"
(find-package :QL))
:swank)
#+nil
(funcall (intern "CREATE-SERVER"
(find-package :swank))
:port 5060
:dont-close t)
|
f3a3dce3 |
(trivial-main-thread:with-body-in-main-thread (:blocking t)
|
bb1b4ba9 |
[#@NSAutoreleasePool @(new)]
|
fbc1e93a |
[#@NSApplication @(sharedApplication)]
|
6f42890d |
#+nil
|
f3a3dce3 |
[objc-runtime::ns-app @(setActivationPolicy:) :int 0]
|
fbc1e93a |
;; Setup the app delegate class. We register this one because it's useful
;; When debugging via something like lldb
(objc-runtime::objc-register-class-pair
(make-app-delegate-class '("actionButton"
"alertButton"
"profitButton")))
|
116081f3 |
(load-nib "MainMenu.nib")
|
ae1d3bf8 |
|
fbc1e93a |
(let ((app-delegate [objc-runtime::ns-app @(delegate)]))
(make-button-delegate (value-for-key app-delegate "actionButton")
(cffi:callback do-things-action))
(make-button-delegate (value-for-key app-delegate "alertButton")
(cffi:callback alert-action))
(make-button-delegate (value-for-key app-delegate "profitButton")
(cffi:callback profit-action)))
|
ae1d3bf8 |
|
f3a3dce3 |
[objc-runtime::ns-app @(activateIgnoringOtherApps:) :boolean t]
|
fbc1e93a |
[objc-runtime::ns-app @(run)]))
|
279ed7b0 |
(defclass application-shim ()
((%main-view :initarg :main-view :accessor main-view)))
|
762358a6 |
(cffi:defcfun (%set-string-value "objc_msgSend")
:void
(cls objc-runtime::o-class)
(sel objc-runtime::o-selector)
(value :pointer))
|
1eec7700 |
|
762358a6 |
(defun set-string-value (control string)
(prog1 control
(%set-string-value control @(setStringValue:)
(objc-runtime:make-nsstring string))))
(defun label (text)
(let ((view [[#@NSTextField @(alloc)] @(init)]))
(prog1 view
(set-string-value view text))))
(defun button (title)
(trivial-main-thread:with-body-in-main-thread (:blocking t)
[#@NSButton @(buttonWithTitle:target:action:)
:pointer (objc-runtime:make-nsstring title)
:pointer #@NSButton
:pointer @(alloc)]))
(defun init-in-main-thread (instance)
(prog1 instance
[instance @(performSelectorOnMainThread:withObject:waitUntilDone:)
:pointer @(init)
:pointer (cffi:null-pointer)
:bool t]))
|
bb1b4ba9 |
(defvar *application-shim*
(make-instance 'application-shim))
|
5358419a |
(defun wait-for-events ()
(let ((event [objc-runtime::ns-app @(nextEventMatchingMask:untilDate:inMode:dequeue:)
:unsigned-long 18446744073709551615
:pointer [#@NSDate @(distantFuture)]
:pointer @"kCFRunLoopDefaultMode"
:int 1]))
[objc-runtime::ns-app @(sendEvent:) :pointer event]
event))
(defun tick ()
(wait-for-events))
(defun task-thread ()
(bt:make-thread (lambda ()
|
1eec7700 |
#+(or)
|
5358419a |
(trivial-main-thread:with-body-in-main-thread (:blocking t)
|
1eec7700 |
[#@NSEvent @(stopPeriodicEvents)]
|
5358419a |
[#@NSEvent @(startPeriodicEventsAfterDelay:withPeriod:) :double 0.0d0 :double 0.01d0])
(loop
(trivial-main-thread:with-body-in-main-thread (:blocking t)
(tick))))
:name "Cocoa Event Loop Feeder"))
|
bb1b4ba9 |
;;#+nil
(defun old-main ()
(trivial-main-thread:with-body-in-main-thread (:blocking nil)
|
6f42890d |
#+sbcl
(sb-int:set-floating-point-modes :traps '())
|
bb1b4ba9 |
[#@NSAutoreleasePool @(new)]
|
6f42890d |
[#@NSApplication @(sharedApplication)]
(format t "~&app: ~s~%" objc-runtime::ns-app)
#+nil
[objc-runtime::ns-app @(setActivationPolicy) :int 0]
(let* ((application-name [[#@NSProcessInfo @(processInfo)] @(processName)]))
(let* ((menubar [[#@NSMenu @(new)] @(autorelease)])
(app-menu-item [[#@NSMenuItem @(new)] @(autorelease)])
(app-menu [[#@NSMenu @(new)] @(autorelease)])
(quit-name @"Quit")
(key @"q")
(quit-menu-item
|
bb1b4ba9 |
[[[#@NSMenuItem @(alloc)]
@(initWithTitle:action:keyEquivalent:) :pointer quit-name :pointer @(terminate?) :string key]
@(autorelease)]))
|
6f42890d |
[menubar @(addItem:) :pointer app-menu-item]
[app-menu @(addItem:) :pointer quit-menu-item]
[app-menu-item @(setSubmenu:) :pointer app-menu]
[objc-runtime::ns-app @(setMainMenu:) :pointer menubar] )
(setf (main-view *application-shim*)
|
bb1b4ba9 |
[#@NSStackView @(stackViewWithViews:)
:pointer [[#@NSArray @(alloc)] @(init)]])
|
6f42890d |
(with-point (p (20 20))
(let* ((foreign-rect (make-rect 10 10 120 120))
|
bb1b4ba9 |
(the-window (init-window [#@NSWindow @(alloc)] foreign-rect 15 2 nil)))
|
ae1d3bf8 |
|
6f42890d |
[(value-for-key the-window "contentView") @(addSubview:) :pointer (main-view *application-shim*)]
[the-window @(cascadeTopLeftFromPoint:) :pointer p]
[the-window @(setTitle:) :pointer application-name]
[the-window @(makeKeyAndOrderFront:) :pointer (cffi:null-pointer)]
[ objc-runtime::ns-app @(activateIgnoringOtherApps:) :boolean t]
|
5358419a |
(task-thread))))))
|
1eec7700 |
(cffi:defcfun (%get-view-frame "objc_msgSend_stret")
:void
(out (:pointer (:struct objc-runtime:ns-rect)))
(class :pointer)
(sel :pointer))
(cffi:defcfun (%init-with-frame "objc_msgSend")
:pointer
(class :pointer)
(sel :pointer)
(frame (:struct objc-runtime:ns-rect)))
(defmacro new-msg-send (selector ((&rest arg-types) return-type))
(let ((arg-syms (mapcar (lambda (_) (gensym (symbol-name _)))
arg-types)))
`(lambda ,(cons 'target arg-syms)
(cffi:foreign-funcall "objc_msgSend"
:pointer target
:pointer ,selector
,@(mapcan #'list arg-types arg-syms)
,return-type))))
(defmacro make-view-dictionary (&rest objc-values)
(alexandria:with-gensyms (selector)
`(let ((,selector (new-msg-send @(dictionaryWithObjectsAndKeys:)
((,@(mapcar (lambda (_) _ :pointer) objc-values) :pointer)
:pointer))))
(funcall ,selector ,@objc-values (cffi:null-pointer)))))
#+(or)
(defun text-view (parent-view)
(let ((text-view [#@NSTextView @(alloc)]))
|
ae1d3bf8 |
|
1eec7700 |
(trivial-main-thread:with-body-in-main-thread (:blocking nil)
(cffi:with-foreign-pointer (v (cffi:foreign-type-size '(:struct objc-runtime::ns-rect)))
(%get-view-frame v *window-view* @(frame))
(init-with-frame *text-view* v))
[*window-view* @(addSubview:) :pointer *text-view*]?)
|
ae1d3bf8 |
(defparameter *view-dictionary*
|
1eec7700 |
)
))
#+(or)
(progn
(defparameter *window-view*
[(main-view *application-shim*) @(superview)])
(trivial-main-thread:with-body-in-main-thread (:blocking nil)
[(main-view *application-shim*) @(removeFromSuperview)]?
))
|