How do I deal with Code Blocks?

LiveCode Builder is a language for extending LiveCode's capabilities, creating new object types as Widgets, and libraries that access lower-level APIs in OSes, applications, and DLLs.

Moderators: LCMark, LCfraser

Simon Knight
Posts: 574
Joined: Wed Nov 04, 2009 11:41 am
Location: Gunthorpe, North Lincs, UK

How do I deal with Code Blocks?

Post by Simon Knight » Thu Feb 13, 2020 7:06 pm

I am attempting to get my Calendar Library to run on iOS. Trevix has kindly tried it and reports that it requests that authorisation be granted for the application to access the users calendars. Unfortunately, unlike mac os, there does not appear to be a way for the user to grant access, which implies the application has to do it.

Looking at Apple's documentation reveals the following command from this page : https://developer.apple.com/documentati ... guage=objc
requestAccessToEntityType:completion:
The problem is the completion parameter. I believe that the method expects a block of objective-c code.

Here is an example from stack-overflow

Code: Select all

[eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {            
        if (granted) {
            dispatch_async(dispatch_get_main_queue(), ^{
                // You can use the event store now
            });
I have also read somewhere that sending Nil to "completion:" will cause a crash.

Any thoughts?
best wishes
Skids

Simon Knight
Posts: 574
Joined: Wed Nov 04, 2009 11:41 am
Location: Gunthorpe, North Lincs, UK

Re: How do I deal with Code Blocks?

Post by Simon Knight » Fri Feb 14, 2020 9:57 am

I found this post elsewhere, by Paul McClernan, that suggests that it can be done:
Still having problems with Block Callback. I've read up on these strange beasts, and I think I understand most of what is involved in wrapping LCB handlers to give them the required structure to pass them as callbacks for FFI. Please correct me if I'm wrong: create a block structure, include pointer to LCB Handler in the block, create pointer to the block for passing to FFI.
I'm still not sure how to tell the block what type of variables/data to expect to receive, which should be at the end of this structure, and I'd guess effect its size.
I'm having trouble trying to implement this (or reimplementing it with Monte/Trevor's update of Ali's hack, I thought I had it working at one point) with a very simple callback that doesn't need to receive anything, just post a message to LC.
I am unsure if blocks and callbacks are the same thing - anyone know ?
best wishes
Skids

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3361
Joined: Sun Jan 07, 2007 9:12 pm
Location: Bochum, Germany

Re: How do I deal with Code Blocks?

Post by bn » Fri Feb 14, 2020 10:44 am

Hi Simon,

I don't understand anything about this.

But it seems Paul McClernan wrestled with the same questions. In case you have not seen this:

https://github.com/PaulMcClernan/LCB_Ap ... Player.lcb

Maybe it gives you some ideas.

I admire your courage and persistence.

Kind regards
Bernd

Simon Knight
Posts: 574
Joined: Wed Nov 04, 2009 11:41 am
Location: Gunthorpe, North Lincs, UK

Re: How do I deal with Code Blocks?

Post by Simon Knight » Fri Feb 14, 2020 12:48 pm

Hi Bernd,

Thanks for the link to Paul's code. I think it is doing exactly what I need but it will take some study to work out how to apply. A quick look shows that he is using some LCB variable types that I have not seen, or most probably missed in the documentation.
I admire your courage and persistence.
.Hmmm, I'm not sure about that, but it is quite satisfying when a call to the library returns a result, even better when its the correct result!

One thing I am sure about is that I am glad I don't program in Objective-C.

best wishes

Simon
best wishes
Skids

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3361
Joined: Sun Jan 07, 2007 9:12 pm
Location: Bochum, Germany

Re: How do I deal with Code Blocks?

Post by bn » Sat Feb 15, 2020 1:42 pm

Hi Simon,

I _think_ what is confusing is

Code: Select all

public foreign handler type CallbackType(in pContext as Pointer) returns CBool
especially
public foreign handler type CallbackType
That is a special beast that I understand every time I look at it for about half a millisecond.

For a discussion of it look at
https://quality.livecode.com/show_bug.cgi?id=18734

it is kind of a metaHandler that accepts/processes handlers.

read it about 20 times and enjoy the half millisecond of understanding. :)

Kind regards
Bernd

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3361
Joined: Sun Jan 07, 2007 9:12 pm
Location: Bochum, Germany

Re: How do I deal with Code Blocks?

Post by bn » Sat Feb 15, 2020 1:44 pm

forgot to add:
have a look at Hermann's examples of it here

viewtopic.php?f=93&t=28225

Kind regard
Bernd

Simon Knight
Posts: 574
Joined: Wed Nov 04, 2009 11:41 am
Location: Gunthorpe, North Lincs, UK

Re: How do I deal with Code Blocks?

Post by Simon Knight » Sat Feb 15, 2020 1:59 pm

Hi Bernd,

Thanks although I doubt I will understand for even that long! I withdrew the post because I decided that the examples were in C and probably did not apply to objective-C. Ho Hum.

Thanks for the links,

here is a copy of the library which should work o.k. on Mac OS.

best wishes
Simon
Attachments
ArchiveV1-0-2.zip
Livecode Builder Library and LCS test harness
(14.57 KiB) Downloaded 42 times
best wishes
Skids

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3361
Joined: Sun Jan 07, 2007 9:12 pm
Location: Bochum, Germany

Re: How do I deal with Code Blocks?

Post by bn » Sat Feb 15, 2020 2:19 pm

Hi Simon,

it works well after I granted access to "Calendar" in System Preferences -> Security -> Privacy -> Calendar

It turns out that I lead a dull live, hardly any events...

Kind regards
Bernd

Simon Knight
Posts: 574
Joined: Wed Nov 04, 2009 11:41 am
Location: Gunthorpe, North Lincs, UK

Re: How do I deal with Code Blocks?

Post by Simon Knight » Sat Feb 15, 2020 3:00 pm

Hi Bernd,
It turns out that I lead a dull live, hardly any events...
surely not - you probably just remember them and have no need to commit them to a computer file!
it works well after I granted access to "Calendar" in System Preferences -> Security -> Privacy -> Calendar
Yes thats the problem I am trying to solve with this call back thing. The ideal is to have the app/library ask for permission and its the only way on iOS.
best wishes
Skids

PaulDaMacMan
Posts: 259
Joined: Wed Apr 24, 2013 4:53 pm
Contact:

Re: How do I deal with Code Blocks?

Post by PaulDaMacMan » Mon Feb 17, 2020 11:30 pm

Hi guys,
Just wanted to chime in to say, NO!!! I never really did get blocks working (except maybe one time when it was firing and then crashing the engine). Nor did I ever really fully understand them.
So a little background: a Block (denoted by the ^ symbol) is something that Apple added to Objective-C in Snow Leopard (macOS 10.6+) but from what I understand it isn't a concept that is unique to ObjC, it can be used with C-Foundation APIs (CoreMIDI) or C++ too and has been implemented in different ways in other languages as well. From what I understand, it's an encapsulated block of code, either separately from or inline within a larger chunk of code, similar to an OOP object but it can also be inline, and it can include local-scoped variables/data and can passed or substituted for a parameter variable. It seems that a bunch of Apple's API's want to use them for processing of callbacks. The actual structure of these ^blocks from a compiler's view looks like this:

Code: Select all

struct Block_literal_1 {
    void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock --- so a Pointer to your Handler goes here
    int flags; // -- flags bitfield as an integer
    int reserved; // another integer, probably ok to fill with null
    void (*invoke)(void *, ...);
// -- another structure that describes the block:
    struct Block_descriptor_1 {
    unsigned long int reserved;         // NULL long integer
        unsigned long int size;         // sizeof(struct Block_literal_1) -- the size of the first block as a long integer
        // optional helper functions
        void (*copy_helper)(void *dst, void *src);     // IFF (1<<25)
        void (*dispose_helper)(void *src);             // IFF (1<<25)
        // required ABI.2010.3.16
        const char *signature;                         // IFF (1<<30)
    } *descriptor;
    // imported variables
};
Further Reading: https://clang.llvm.org/docs/Block-ABI-Apple.html

A while back Ali put together some Hacky code to wrap LiveCode Builder handlers into a Block Structure that could be passed to these Apple APIs that require them. If I remember correctly Monte then refined it a bit into somewhat of a block-handling library. This all was posted in LiveCode's chatroom on GitHub's Gitter.im site, you'd probably have to scroll for about a half a mile to see all the relevant comments and code fragments. I tried to collect it all into some collection of code for trying to decipher better at a later date. So far that date has not arrived. I've been hoping they'd formally add these ^block wrapping handlers to the Objective C LCB module at some point, and then hopefully I wouldn't have to try to grok it anymore.

What I've been trying to find with the work I was doing with Apple's C (CF CoreFoundation) based CoreMIDI API is early versions of certain functions prior to when ^Blocks were introduced to the language to see what the equivalencies might be. It seems that ^Blocks were a popular choice to replace Delegates.
Last edited by PaulDaMacMan on Tue Feb 18, 2020 7:51 am, edited 1 time in total.
https://github.com/PaulMcClernan

PaulDaMacMan
Posts: 259
Joined: Wed Apr 24, 2013 4:53 pm
Contact:

Re: How do I deal with Code Blocks?

Post by PaulDaMacMan » Tue Feb 18, 2020 7:04 am

Here is some snippets of the relevant comments from the Gutter.im chatroom:

Code: Select all

-- This is our current block handling code. It adds a pointer that the block struct needs if it is treated as an objective c object:
/* BLOCK HANDLING */

type LibraryHandle is Pointer

private variable mNSConcreteGlobalBlock as optional Pointer

constant kRTLD_LAZY    is 1
private foreign handler OpenLibrary(in pPath as ZStringUTF8, in pFlag as CSInt) returns optional LibraryHandle binds to "dlopen"
private foreign handler GetSymbolPointer(in pHandle as LibraryHandle, in pSymbol as ZStringUTF8) returns optional Pointer binds to "dlsym"
private foreign handler CloseLibrary(in pHandle as LibraryHandle) returns CSInt binds to "dlclose"

private unsafe handler EnsureNSConcreteGlobalBlock() returns nothing
    if mNSConcreteGlobalBlock is not nothing then
        return
    end if

    variable tSystem as LibraryHandle
    put OpenLibrary("libSystem.dylib", kRTLD_LAZY) into tSystem
    if tSystem is not nothing then
        put GetSymbolPointer(tSystem, "_NSConcreteGlobalBlock") into mNSConcreteGlobalBlock
        CloseLibrary(tSystem)
    end if
end handler

public foreign type BlockDescriptor binds to "MCAggregateTypeInfo:ffrrr"
public foreign type Block binds to "MCAggregateTypeInfo:rEErr"
type BlockPointer is Pointer

foreign handler MCHandlerGetFunctionPtr(in pHandler as any, out rPtr as Pointer) returns CBool binds to "<builtin>"
private foreign handler CopyBlockDescriptor(inout pBlock as BlockDescriptor, in pSize as UIntSize, out rNewBlock as Pointer) returns CBool binds to "MCMemoryAllocateCopy"
private foreign handler CopyBlock(inout pBlock as Block, in pSize as UIntSize, out rNewBlock as Pointer) returns CBool binds to "MCMemoryAllocateCopy"
private foreign handler MCMemoryDelete(in pBlock as Pointer) returns nothing binds to "<builtin>"

private constant kBlockSize is 32
private constant kBlockDescriptorSize is 40

private handler CreateBlockPointerFromHandler(in pHandler as any, out rBlockPtr as optional BlockPointer) returns Boolean
    unsafe
        EnsureNSConcreteGlobalBlock()

        variable tFunctionPtr as Pointer
        if not MCHandlerGetFunctionPtr(pHandler, tFunctionPtr) then
            return false
        end if

        variable tBlock as Block
        variable tBlockDescriptor as BlockDescriptor

        put [0, kBlockSize, nothing, nothing, nothing] into tBlockDescriptor

        variable tBlockDescriptorPointer as Pointer
        if not CopyBlockDescriptor(tBlockDescriptor, kBlockDescriptorSize, tBlockDescriptorPointer) then
            return false
        end if

        put [mNSConcreteGlobalBlock, 0x30000000, 0, tFunctionPtr, tBlockDescriptorPointer] into tBlock

        variable tBlockPtr as BlockPointer
        if not CopyBlock(tBlock, kBlockSize, tBlockPtr) then
            MCMemoryDelete(tBlockDescriptorPointer)
            return false
        end if

        put tBlockPtr into rBlockPtr
        return true
    end unsafe
end handler

private handler DeleteBlockPointer(in pBlockPointer as BlockPointer)
    unsafe
        DeleteBlockDescriptorPointer(pBlockPointer)
        MCMemoryDelete(pBlockPointer)
    end unsafe
end handler

private unsafe handler DeleteBlockDescriptorPointer(inout pBlock as Block)
    MCMemoryDelete(pBlock[5])
end handler

-- This bit is particularly confusing to me:
-- MCHandlerGetFunctionPtr(in pHandler as any
-- I did try something with that for multiple callbacks, but I don't know how to pass the name of the handler, when I go to compile it thinks it's an undefined variable.


-- BerndN @BerndN Feb 07 05:29
-- maybe this helps
-- https://quality.livecode.com/show_bug.cgi?id=18734
-- and more examples
-- http://forums.livecode.com/viewtopic.php?f=93&t=28225


-- Monte Goulding @montegoulding Feb 07 18:58
-- This example adds a text field do an alert controller
variable mFieldConfigurationHandlerBlock as optional BlockPointer
public handler type FieldConfigurationHandlerType(in pTextField as ObjcId) returns nothing
public handler FieldConfigurationHandler(in pBlock as BlockPointer, in pTextField as ObjcId) returns nothing
    unsafe
        ObjC_UITextFieldSetText(pTextField, StringToNSString(mIOSFieldProperties["text"]))
        ObjC_UITextFieldSetPlaceholder(pTextField, StringToNSString(mIOSFieldProperties["placeholder"]))
        ObjC_UITextFieldSetSecureTextEntry(pTextField, mIOSFieldProperties["secureTextEntry"])

        if mIOSFieldProperties["label"] is not empty then
            variable tLabel as ObjcObject
            put Objc_UILabelInit(ObjC_UILabelAlloc()) into tLabel
            ObjC_UILabelSetText(tLabel, StringToNSString(mIOSFieldProperties["label"] & " "))
            ObjC_UILabelSetFont(tLabel, ObjC_UIFontSystemFontOfSize(14))
            ObjC_UITextFieldSetLeftView(pTextField, tLabel)
            ObjC_UITextFieldSetLeftViewMode(pTextField, kUITextFieldViewModeAlways)
            ObjC_UILabelSizeToFit(tLabel)
        end if
    end unsafe
end handler
-- In a library init handler:
if not CreateBlockPointerFromHandler(FieldConfigurationHandler, mFieldConfigurationHandlerBlock) then
    return false
end if
-- In a library finalize handler:
DeleteBlockPointer(mFieldConfigurationHandlerBlock)
put nothing into mFieldConfigurationHandlerBlock
-- Add the field:
                put { "text" : pContent, "placeholder" : pPlaceholder, "label" : pLabel, "secureTextEntry" : pPassword } into mIOSFieldProperties
        unsafe
            ObjC_UIAlertControllerAddTextFieldWithConfigurationHandler( mIOSDialog, mFieldConfigurationHandlerBlock)
        end unsafe
https://github.com/PaulMcClernan

PaulDaMacMan
Posts: 259
Joined: Wed Apr 24, 2013 4:53 pm
Contact:

Re: How do I deal with Code Blocks?

Post by PaulDaMacMan » Tue Feb 18, 2020 7:47 am

So one thing that may or may not be important in getting blocks to work is the scope. There's apparently 2 types of blocks that a compiler can create
1) Gobal block, which seems like it must be self contained and so doesn’t use any variables from outside its scope (local variables only). Notice the reference to NSConcreteGlobalBlock above.
2) Stack block, if the block references any variables that are declared outside the block then the block will be created as a stack object, which is apparently not like to normal Objective C objects. I've seen references to _NSConcreteStackBlock _NSConcreteMallocBlock (memory allocate a block from scratch?) as well as the _NSConcreteGlobalBlock above.

I really don't understand much of this, it all seems overly complicated to me. I wish I could just get and pass a simple pointer to my LCB Handler and have it work.

Some more reference links:
https://www.galloway.me.uk/2012/10/a-lo ... episode-1/
The actual Objective C 2.0 implementation from Apple:
https://opensource.apple.com/source/lib ... tation.txt
An interesting StackOverflow question about NSBlock objects that suggests they're like Objective C Objects that are allocated by the compiler
https://stackoverflow.com/questions/201 ... ts-created
https://github.com/PaulMcClernan

Simon Knight
Posts: 574
Joined: Wed Nov 04, 2009 11:41 am
Location: Gunthorpe, North Lincs, UK

Re: How do I deal with Code Blocks?

Post by Simon Knight » Tue Feb 18, 2020 9:17 am

Hi Paul,

May thanks for your detailed response to my question. I shall see what I can pick out of the code you have posted, it could be a long day!

To me the use of blocks and delegates seem to fly in the original concepts of object programming. If I have understood correctly delegates allow the calling of class methods independent of any objects and are thus almost the equivalent of Basic's GoTo command and blocks are an attempt to overcome this delegates kludge. Either way they are a concept that put a spanner in the works of the LCB foreign handler interface on Macs as Apple seem to use them in their updated method calls.
best wishes
Skids

LCMark
Livecode Staff Member
Livecode Staff Member
Posts: 1088
Joined: Thu Apr 11, 2013 11:27 am

Re: How do I deal with Code Blocks?

Post by LCMark » Thu Mar 19, 2020 4:24 pm

Just to let you know - we've taken the code which was hashed out on Gitter, cleaned it up, commented it and it will be included in the objc module from 9.6-DP-3 (PR is here: https://github.com/livecode/livecode/pull/7291). We've now used this code internally on both macOS and iOS so are reasonably confident it should work for many cases. The docs also contain example code fragments which are related to 'permissions requests' - so hopefully that will help with hooking into the calendar related permissions on iOS.

Simon Knight
Posts: 574
Joined: Wed Nov 04, 2009 11:41 am
Location: Gunthorpe, North Lincs, UK

Re: How do I deal with Code Blocks?

Post by Simon Knight » Thu Mar 19, 2020 5:42 pm

Excellent news, now I have something to do while isolating at home.

Stay well

Simon
best wishes
Skids

Post Reply

Return to “LiveCode Builder”