Calling OBJC runtime from newlisp

Started by fdb, August 31, 2014, 03:40:49 AM

Previous topic - Next topic

fdb

#15
Well i got it working! However structs are not passed correctly by libffi or the objc runtime is to blame. In order to get past this I made a small objc dynamic library:

#import "bridge_newlisp_objc.h"

id create_window (id window , char * rect , NSUInteger mask ){
    NSRect myrect = NSRectFromString([NSString stringWithUTF8String:rect]);
    return [window initWithContentRect:myrect
                      styleMask:mask
                        backing:2
                          defer:NO];  
}

And know i can call the rest from new lisp as follows:(load "~/Documents/newlisprogjes/Modules/objc-bridge.lsp")

(setq nsapp (• NSApplication "sharedApplication"))
(• nsapp "setActivationPolicy:" NSApplicationActivationPolicyRegular)
(setq window (• NSWindow "alloc"))
(create_window window "200 200 300 300" 15)
(• window "makeKeyAndOrderFront:" window)
(setq title (@s "The Window title"))
(• window "setTitle:" title)
(• nsapp "activateIgnoringOtherApps:" YES)
(• nsapp "run")


and at last i got my window on screen! As you can see i can set the window title afterwards and also numbers are passed correctly only structs not because trying to change the size afterwards by passing a struct doesn't work.

TedWalther

#16
Very nice.
Cavemen in bearskins invaded the ivory towers of Artificial Intelligence.  Nine months later, they left with a baby named newLISP.  The women of the ivory towers wept and wailed.  \"Abomination!\" they cried.

fdb

#17
Coming back to this (after 4 years , time flies!) I've recently made a minimalistic objective-c/cocoa bridge which doesn't need any 'glue' c-code /libraries. You only need to import 3 functions from the libobjc.dylib and two frameworks (foundation and appkit) which are standard available on a Mac. It is only 22 loc... but gives you the ability to create native GUI's on a Mac with newLisp. In order to use it you need some basic understanding of Objective-C and Cocoa (Google is your friend) but I'm going to start by building a layer on top of this  conforming to the same API/functions as guiserver.lsp. So this is what the code looks like:
;;@module Minimalistic Cocoa / Objective-C bridge

(import "/System/Library/Frameworks/Foundation.framework/Foundation")
(import "/System/Library/Frameworks/AppKit.framework/AppKit")
(set 'get-class (import "libobjc.dylib" "objc_getClass"))
(set 'get-sel (import "libobjc.dylib" "sel_getUid"))
(import "libobjc.dylib" "objc_msgSend")

;; principal function sending a message to an instance or class with a variable
;; number of arguments use when the arguments are NOT floating points or structs

(define (-> ptr sel)
(when (string? ptr) (set 'ptr (get-class ptr)))
  (eval (flat (list 'objc_msgSend ptr (get-sel sel) (args))))
)

;; function sending a message to an instance or class using an invocation object,
;; use when the arguments contain floats and or structs. The address of the return
;; value is returned

(define (=> target selector)
(when (string? target)
(set 'target (get-class target)))
(set 'sel (get-sel selector))
(set 'method (-> target "methodSignatureForSelector:" sel))
(set 'invocation (-> "NSInvocation" "invocationWithMethodSignature:" method))
(-> invocation "setSelector:" sel)
(doargs (arg)
(-> invocation "setArgument:atIndex:" (address arg) (+ $idx 2)))
(-> invocation "invokeWithTarget:" target)
(set 'size (-> method "methodReturnLength"))
(set 'return (dup "00" size))
(-> invocation "getReturnValue:" (address return))
(address return)
)


To see an example see below code, which creates a window with a button on it.

(struct 'Point "double" "double")
(struct 'Rect "Point" "Point")

(define (makerect x y w h)
(pack Rect (pack Point x y) (pack Point w h)) )

(define (test)
(set 'nsapp (-> "NSApplication" "sharedApplication"))
(-> nsapp "setActivationPolicy:" 1)
(set 'window (-> "NSWindow" "alloc"))
(set 'rect (makerect 150 150 200 300))
(set 'mask 15 'backing 2 'defer 0)
(=> window "initWithContentRect:styleMask:backing:defer:" rect mask backing defer)
(set 'button (-> "NSButton" "alloc"))
(=> button "initWithFrame:" (makerect 100 150 80 40))
(-> button "setBezelStyle:" 1)
(-> (-> window "contentView") "addSubview:" button)
(-> window "makeKeyAndOrderFront:" window)
(-> nsapp "activateIgnoringOtherApps:" 1)
(-> nsapp "run")
)


TedWalther

#18
That is so beautiful it makes me want to cry.
Cavemen in bearskins invaded the ivory towers of Artificial Intelligence.  Nine months later, they left with a baby named newLISP.  The women of the ivory towers wept and wailed.  \"Abomination!\" they cried.

fdb

#19
And as probably the final piece I imported the objc function to dynamically add a method (= function) to an existing class with a given implementation (function pointer)  which gives you the possibility to execute a newlisp function when for instance clicking on a button. You'll have to  define the function first before adding the function to the class. See below code which executes the newlisp function "button-clicked" when you.. click the button! I think I've got everything in place now to copy the functionality of the guiserver for OSX , so no need for Java anymore.

(define (button-clicked id sel)
(println "button id:" id " function:" (get-string sel))
)

(define (test)
(set 'nsapp (-> "NSApplication" "sharedApplication"))
(-> nsapp "setActivationPolicy:" 1)
(set 'window (-> "NSWindow" "alloc"))
(set 'rect (make-rect 150 150 200 300))
(set 'mask 15 'backing 2 'defer 0)
(=> window "initWithContentRect:styleMask:backing:defer:" rect mask backing defer)
(set 'imp (callback 'button-clicked "void" "void*" "void*" ))
(@add-method (@class "NSButton") (@selector "button-clicked") imp "v@:")
(set 'button (-> "NSButton" "alloc"))
(=> button "initWithFrame:" (make-rect 100 150 80 40))
(-> button "setAction:" (@selector "button-clicked"))
(-> button "setTarget:" button)
(-> button "setBezelStyle:" 1)
(-> (-> window "contentView") "addSubview:" button)
(-> window "makeKeyAndOrderFront:" window)
(-> nsapp "activateIgnoringOtherApps:" 1)
(-> nsapp "run")
)