@@ -610,4 +610,212 @@ public handler CreateObjcInformalDelegateWithContext(in pProtocol as List, in pH
610610 end unsafe
611611end handler
612612
613+ /* Block handling */
614+
615+ private variable mNSConcreteGlobalBlock as optional Pointer
616+ private constant kRTLD_LAZY is 1
617+ private type LibraryHandle is Pointer
618+
619+ private foreign handler OpenLibrary(in pPath as ZStringUTF8, in pFlag as CSInt) \
620+ returns optional LibraryHandle \
621+ binds to "dlopen"
622+
623+ private foreign handler GetSymbolPointer(in pHandle as LibraryHandle, in pSymbol as ZStringUTF8) \
624+ returns optional Pointer \
625+ binds to "dlsym"
626+
627+ private foreign handler CloseLibrary(in pHandle as LibraryHandle) \
628+ returns CSInt \
629+ binds to "dlclose"
630+
631+ /* Fetch the `isa` pointer for the (internal) Obj-C concreate block class */
632+ private unsafe handler EnsureNSConcreteGlobalBlock() returns nothing
633+ if mNSConcreteGlobalBlock is not nothing then
634+ return
635+ end if
636+
637+ /* As we cannot bind to the pointer to a symbol as a variable, we use
638+ * dynamic library loading and resolution functions to do so directly. */
639+ variable tSystem as LibraryHandle
640+ put OpenLibrary("libSystem.dylib", kRTLD_LAZY) into tSystem
641+ if tSystem is not nothing then
642+ put GetSymbolPointer(tSystem, "_NSConcreteGlobalBlock") \
643+ into mNSConcreteGlobalBlock
644+ CloseLibrary(tSystem)
645+ end if
646+ end handler
647+
648+ private constant kBlockSize is 32
649+ private constant kBlockDescriptorSize is 40
650+
651+ public foreign type _ObjcBlockDescriptor binds to "MCAggregateTypeInfo:ffrrr"
652+ public foreign type _ObjcBlock binds to "MCAggregateTypeInfo:rEErr"
653+ public type ObjcBlockPointer is Pointer
654+
655+ foreign handler MCHandlerGetFunctionPtr(in pHandler as any, out rPtr as Pointer) \
656+ returns CBool \
657+ binds to "<builtin>"
658+
659+ private foreign handler CopyBlockDescriptor(inout pBlock as _ObjcBlockDescriptor, in pSize as UIntSize, out rNewBlock as Pointer) \
660+ returns CBool \
661+ binds to "MCMemoryAllocateCopy"
662+
663+ private foreign handler CopyBlock(inout pBlock as _ObjcBlock, in pSize as UIntSize, out rNewBlock as Pointer) \
664+ returns CBool \
665+ binds to "MCMemoryAllocateCopy"
666+
667+ private foreign handler MCMemoryDelete(in pBlock as Pointer) \
668+ returns nothing \
669+ binds to "<builtin>"
670+
671+ private foreign handler BlockPointerToBlock(out rBlock as _ObjcBlock, in pPointer as ObjcBlockPointer, in pSize as UIntSize) \
672+ returns nothing \
673+ binds to "c:memcpy"
674+
675+ /**
676+ Create an Objective-C block pointer that wraps an LCB handler.
677+
678+ Example:
679+ private variable sRequestPermissionsCompletionHandler as optional ObjcBlockPointer
680+ private variable sTarget as ScriptObject
681+
682+ public handler AudioLibraryInitialize() returns Boolean
683+ if not CreateObjcBlockPointerFromHandler(RequestPermissionsCompletionHandler, sRequestPermissionsCompletionHandler) then
684+ put nothing into sRequestPermissionsCompletionHandler
685+ return false
686+ end if
687+ put the caller into sTarget
688+ return true
689+ end handler
690+
691+ private foreign handler ObjC_AVCaptureDeviceRequestAccessForMediaType(in pMediaType as ObjcId, in pCompletionHandler as ObjcBlockPointer) \
692+ returns nothing \
693+ binds to "objc:AVCaptureDevice.+requestAccessForMediaType:completionHandler:"
694+
695+ public handler AudioLibraryRequestPermissions()
696+ unsafe
697+ ObjC_AVCaptureDeviceRequestAccessForMediaType(StringToNSString("soun"), sRequestPermissionsCompletionHandler)
698+ end unsafe
699+ end handler
700+
701+ public handler RequestPermissionsCompletionHandler(in pBlock as ObjcBlockPointer, in pGranted as CBool)
702+ post "AudioLibraryRequestPermissionsCallback" to sTarget with [pGranted]
703+ end handler
704+
705+ Parameters:
706+ pHandler: The handler the block pointer should wrap.
707+ rBlockPtr: The variable into which the block pointer should be returned.
708+
709+ Returns:
710+ True if the block pointer was successfully created, false otherwise.
711+
712+ Description:
713+ Use the <CreateObjcBlockPointerFromHandler> handler to create a pointer to an
714+ Objective-C block that wraps an LCB handler. The block pointer can be used in
715+ calls to Objective-C foreign functions that expect a block as a parameter.
716+
717+ The wrapped handler will be called whenever the block is invoked, with the first
718+ parameter of its signature being the block pointer. The remaining parameters
719+ should match those of the Objective-C block.
720+
721+ The lifetime of a created `ObjcBlockPointer` is not automatically managed. When
722+ such a value has no more references to it and it is no longer going to be used,
723+ <DeleteObjcBlockPointer> should be used to free the resources used by it.
724+
725+ References:
726+ DeleteObjcBlockPointer (handler)
727+
728+ */
729+ public handler CreateObjcBlockPointerFromHandler(in pHandler as any, out rBlockPtr as optional ObjcBlockPointer) returns Boolean
730+ variable tBlockPtr as ObjcBlockPointer
731+ unsafe
732+ /* Make sure the `isa` pointer has been initialized */
733+ EnsureNSConcreteGlobalBlock()
734+
735+ /* Fetch the (C) function pointer for the given handler - this is used
736+ * later when constructing the block's record and is called when the
737+ * block is invoked. */
738+ variable tFunctionPtr as Pointer
739+ if not MCHandlerGetFunctionPtr(pHandler, tFunctionPtr) then
740+ return false
741+ end if
742+
743+ /* Initialize the list of block descriptor aggregate members. The first
744+ * field is the flags where 0 means global block; the second field is
745+ * the size of the block record; the other members are not used (for our
746+ * purposes). */
747+ variable tBlockDescriptor as _ObjcBlockDescriptor
748+ put [0, kBlockSize, nothing, nothing, nothing] into tBlockDescriptor
749+
750+ /* Copy the block descriptor after being (implicitly) converted to an
751+ * aggregate into a heap allocated memory block. */
752+ variable tBlockDescriptorPointer as Pointer
753+ if not CopyBlockDescriptor(tBlockDescriptor, \
754+ kBlockDescriptorSize, \
755+ tBlockDescriptorPointer) then
756+ return false
757+ end if
758+
759+ /* Initialize the list of block members. The first field is the obj-c
760+ * objects 'isa' pointer; the second is the flags word; the third is
761+ * unused for our purposes; the fourth is the (C)function pointer it
762+ * wraps; and the fifth is the block descriptor (which mainly defines
763+ * the actual size of the block - as we don't add any block variables,
764+ * this is a fixed size. */
765+ variable tBlock as _ObjcBlock
766+ put [mNSConcreteGlobalBlock, \
767+ 0x30000000, \
768+ 0, \
769+ tFunctionPtr, \
770+ tBlockDescriptorPointer] into tBlock
771+
772+ /* Copy the block after being (implicitly) converted to an aggregate
773+ * into a heap allocated memory block. */
774+ if not CopyBlock(tBlock, kBlockSize, tBlockPtr) then
775+ MCMemoryDelete(tBlockDescriptorPointer)
776+ return false
777+ end if
778+ end unsafe
779+
780+ put tBlockPtr into rBlockPtr
781+ return true
782+ end handler
783+
784+ /**
785+ Delete an Objective-C block pointer.
786+
787+ Example:
788+ public handler AudioLibraryFinalize()
789+ if sRequestPermissionsCompletionHandler is not nothing then
790+ DeleteObjcBlockPointer(sRequestPermissionsCompletionHandler)
791+ put nothing into sRequestPermissionsCompletionHandler
792+ end if
793+ end handler
794+
795+ Parameters:
796+ pBlockPointer: An Objective-C block pointer created with
797+ <CreateObjcBlockPointerFromHandler>
798+
799+ Description:
800+ Use the <DeleteObjcBlockPointer> handler to delete an Objective-C block pointer
801+ created with <CreateObjcBlockPointerFromHandler>
802+
803+ References:
804+ CreateObjcBlockPointerFromHandler (handler)
805+
806+ */
807+ public handler DeleteObjcBlockPointer(in pBlockPointer as ObjcBlockPointer)
808+ unsafe
809+ /* Unpack the pointer to block aggregate as an aggregate. */
810+ variable tBlock as _ObjcBlock
811+ BlockPointerToBlock(tBlock, pBlockPointer, kBlockSize)
812+
813+ /* Delete the block's descriptor (fifth field). */
814+ MCMemoryDelete(tBlock[5])
815+
816+ /* Delete the block itself. */
817+ MCMemoryDelete(pBlockPointer)
818+ end unsafe
819+ end handler
820+
613821end module
0 commit comments