Extension Builder in plain English - foreign function binding

How to USE and/or how to create eXTension Builder Libraries and Custom UI Widgets
Post Reply
User avatar
OpenXTalkPaul
Posts: 2391
Joined: Sat Sep 11, 2021 4:19 pm
Contact:

Extension Builder in plain English - foreign function binding

Post by OpenXTalkPaul »

For anyone who doesn't go to the forums 'over there' any more I figured I'd continue this here.

I'm going to try to make a step by step guide to using Extension Builder and wrapping foreign code libraries.
This IS NOT a beginners guide to Extension Builder, you should learn the basics of Extension builder syntax before moving on to this. There are existing tutorials for that already. I'm concentrating on Foreign Function binding for now, since there really isn't any good tutorials available for this task.

I feel like I finally know this stuff well enough to teach most of the basics of it to others. I've used it plenty 'as-is' and I've used it to wrap C, C++ and Objective C libraries and make some 'native' widgets. I don't have a lot of experience wrapping Java stuff with it (I did do a Java Android extension many years ago, before switching to iPhone), nor do I have experience wrapping javascript stuff with it (which there aren't many examples of wrapping JS that are in the public sphere).

Before you begin It would be helpful if you have at least some knowledge of others programming languages (specially the ones you need to tap into) and the 'variable types' that are commonly used with them.

Unlike the xTalk Scripting language, Extension Builder is 'strongly typed' (although it does have some flexibility), which means EVERY variable you use MUST be declared before you can use the variable. When you declare a variable you are telling the compiler what type of data to expect that will be stored in that variable so that the appropriate amount of memory may be reserved for storing the contents of the variable.

Variable types may be one of several alien concepts to you if your've only ever used scripting languages. Popular scripting languages and xTalk(s) tend to be 'loosely typed', and so that you don't normally have to declare a variable type at all. Instead the script interpreter engine will infer a variable's type from the context that it's being used in. So if you have a number 2 in some variable and a string of the text character "2" in another variable you can still add them together and the script engine will automatically convert the string "2" into a number 2 and add them together resulting in the number 4. That's not the case with 'strongly typed' languages. The previous example would have resulted in an error because you can't add a number to a text character. Unlike xTalk scripting, Extension Builder is NOT a loosely typed language!

Extension Builder has its own generic equivalents for common variable types for types like Boolean, String, Integer, etc, These types may be equivalent to a 'C' variable type, but you can also use some variable types that are specific to a foreign language such as 'C'. For C language these types look like 'CInt' which is a C signed Integer that can hold an integer number between -127 and 127, which requires a single byte of memory to store its value, or it may look like 'CUInt' which is unsigned integer that can hold an integer number between 0 and 255, which also requires a single byte of memory to store its value. You can also create custom 'foreign' variable types and 'alias types' for other types, but I won't get too into that for now.
_____________________________________________________________________________________________________

I'll start by breaking down a C API foreign function binding string example from a thread 'over there':
private __safe foreign handler _ReadBackAnalogOut (out rBuffer as CInt) returns nothing binds to "c:K8055D>ReadBackAnalogOut!stdcall"
private -- means this handler will be invisible outside of the context of the current Extension module.

__safe -- there is safe and unsafe context, I'm a bit sketchy on this. The docs say: Foreign handlers are always considered unsafe, and thus may only be called from unsafe context - i.e. from within an unsafe handler, or unsafe statement block. The _safe keyword seems to be undocumented. I've only seen it used with C++ APIs. The bottom line is foreign handlers that have this keyword tells Extension Builder its OK to be called outside of the 'unsafe/end unsafe' structure.

foreign handler -- as opposed to a regular handler, this let's us know we are binding to some external code

_ReadBackAnalogOut -- the name we'll be using to call this foreign handler, naming conventions here are up to your own discretion

(out rBuffer as CInt) -- inside the parens are declaring what the the foreign handler is expecting to receive (in) or send back (out) or both (inout) when this external code is being called. In this case it's declaring that the foreign code will return a parameter that should be a C signed integer (as CInt) but you might also be able to use LCB's type 'as Number' or any other variable type that's compatible with a C signed int. When I say compatible what I mean is the data can fit, byte size wise, into the type you're declaring. So make sure you're using the correct type. A unsigned C integer (as CUint) can fit a signed int, but will give you the wrong number range. rBuffer is an arbitrarily named variable but the lead 'r' is a commonly used convention for indicating it's a variable that's being return.

returns nothing -- the foreign function will return nothing (null), at least not in the normal way, in the case of this handler the variable you pass for 'rBuffer' is will be filled (out) with the result of the foreign code. With many APIs the return value here will simply be 'returns CBool' (or Boolean) that indicates success or failure of the call.

binds to -- Always needed, just indicates the next quoted string is the actual binding string for the foreign handler, that gives libFFI some clues about the code being called.

"c:K8055D>ReadBackAnalogOut!stdcall" --
The binding string starts with "c:" which says the library or API we want is C based. It can be one of c: (which covert C++ too), java: or objc: (and maybe others? js:? And I see mention of Pascal in the docs).
Followed by K8055D>, so K8055D is the library or API name to look for, followed by '>'. The actual foreign code library can be included in platform_cpu specifically named folders in your extension project folder, or the foreign code library may be an API (or .Framework for MacOS) part of the OS or in user installed common shared library folder in the OS. I'm not sure of the exact order that memory or directories are searched for libraries / function symbols. Like if there were two copies of the same library available, I'm not sure if it would load the included copy first or another one in the system one first.
ReadBackAnalogOut -- ReadBackAnalogOut is the name of the actual function we want to use from the foreign code library.
!stdcall is the calling convention, with C on windows can be one of default, stdcall, thiscall, fastcall, cdecl, pascal, or register. All but default are Win32-only. I've only had to change this once so far, most of the time you probably don't need to worry about it. On Java / Android this can be one of instance, static, or nonvirtual. I haven't done much with Android in some time so I don't know anything about those calling conventions.

Now in some regular handler you then call like _ReadBackAnalogOut( tMyCint ), tMyInt would be an integer variable you've already declared in the calling handler with 'variable tMyInt as CInt' or variable tMyInt as Integer'.
A lot of times the the returned data or object may need further processing to get the data into a format that's useful for our scripting language. For example ObjC's NSColor Red Blue Green components might be floating point numbers between 0.00000 and 1.00000, so we would multiply them by 255 to get to be 8 bit RGB values we can use with our scripts language.

The binding strings look a bit different depending on if you're binding to c:, java:, or objc: foreign code. You can, and in the case of Apple probably need to, mix c: and objc: APIs together in the same module (C based CoreAudio AND Objective C based AVAudio APIs for example)

The foreign code is only resolved when it's called so you won't know if you got the foreign binding strings correct until you actually try to use that foreign handler.

Besides knowing how to bind to the foreign code, you will probably need to learn about the API you're trying to use too, although if you're like me you might stumble through learning about an API WHILE you're trying to write bindings for it. And of course there's bound to be some 'gotchas' along the way too that will test your spirit! Like for examples dealing with returned data that's in a different byte order, or maybe you get back a Pointer to data or an Object that exists elsewhere and need to parse the block of data a pointer points to (variable foreign type declarations is a whole other thing to know) or need to query an object for its relevant properties.

Once you start to get the feel of it, it does get a little easier. Like I've said, I've used ChatGPT to type out a lot of binding strings for me nowadays, and most of the time it even makes valid binding strings. If a stupid AI can get the hang of it then I know you can too! If xTalk is ever going to be on par with languages like Lua or Python, then I think it needs to have plenty of libraries available so people can use it to do a wide variety of things.

There's some undocumented things that I want to cover on on here too. Like binding the functions in the Scripting Engine itself, it's 'Foundation' stuff.

Here are all of the related reference docs from 'All Guides', although there isn't a lot of plain English:

https://github.com/OpenXTalk-org/OpenXT ... -naming.md
https://github.com/OpenXTalk-org/OpenXT ... form-id.md
https://github.com/OpenXTalk-org/OpenXT ... cedence.md
https://github.com/OpenXTalk-org/OpenXT ... ference.md
https://github.com/OpenXTalk-org/OpenXT ... 20Guide.md
https://github.com/OpenXTalk-org/OpenXT ... g%20OXT.md
https://github.com/OpenXTalk-org/OpenXT ... ference.md
User avatar
OpenXTalkPaul
Posts: 2391
Joined: Sat Sep 11, 2021 4:19 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by OpenXTalkPaul »

Next I'm going to do a walk through wrapping some Objective C, typically used with Apple macOS and iOS APIs, libraries, frameworks. Objective C can also be used with the cross-platform open-source framework called GNUStep, which like Apple's ObjC frameworks, is also descendant from NeXTStep/OpenStep, and so GNUStep offers some level of compatibility with Apple's versions in a cross-platform form.

Working with Objective C can be a bit more tricky than using C,C++ APIs.
We will need at least some basic level of understanding of Objective C...

Objective C is an Object Oriented Programming (OOP) language, it's basically a set of syntax that was added on to the C programming language to allow for the 'object' metaphor. In Objective C (I'll often shorten the name to 'ObjC') just about everything is a virtual 'object', even Numbers and Text data are considered to be objects. So we will mostly be retrieving, checking properties of, or manipulating these virtual objects.

In Objective C 'NSObject' is the main parent 'class'. 'Class' is just object-oriented speak for 'the type of object'.
A real world object example would be like an 'Apple' is in the 'Fruits' class of 'Foods'.
NSObject is the most generic type of object in Objective C. It's like a template object that all other objects are built from.
In our real world example above, the NSObject would equate to the top class 'Foods.

'Methods' are the functions or commands that are built into an object. If the object was a car, one of its 'methods' might be driveForward or It might be passengerCount

Most of the 'methods' that can be use with an NSObject can (normally) be used with most other objects since all other objects are built from NSObject. And so any child objects will include the attributes and methods of the parent class(s). This is called 'Inheritance'.

Don't worry too much about words like obj class, inheritance, delegates, etc.
Just think of objects like this: Objects are simply all-in-one containers for properties/data AND functions or commands to do stuff with that data, very much like what our 'classic' controls, widgets, or script objects are. These virtual objects exist in memory and may or may not have any visible representation on our screens.
__________________________________________________________________________________________________

So with the very basics of Object Oriented Programming and ObjC covered, let's try to wrap some ObjC API.
I'm going to use my most recent wrapping efforts as an example, discussed in this topic: viewtopic.php?p=6282#p6282

I wanted to get the Red,Blue,Green (RGB and possibly A, for Alpha channel / transparency) values for NSColor System color that the user has selected in the System Preferences "General Settings" Panel.

We need to declare any non-default Extension Builder language modules we need to use.
Since we are wrapping a Objective C Foreign Code Library we at least need to include:

Code: Select all

use com.livecode.foreign
Which is a library with common handlers and variable type definitions for working various foreign code languages.

Code: Select all

use com.livecode.objc
Which is a library with Objective C specific handlers and variable type definitions.

We want to be able to alpha-numerically sort any List type variable that we may generate so make sure we include the 'sort' module. Without looking it up, I'm not sure if 'sort' is one of the default language modules or not but it won't hurt to declare it anyway, since we know we will need it:

Code: Select all

use com.livecode.sort
The next step will likely be reading up on the API or library that we want to use to get an idea of how it is intended to be used. StackOverflow is often a good resource for finding answers about specific goals like this one. Search for something like 'how to I get the user highlight color on macOS in Objective C' and you may find some relevant ObjC code, but looking at examples written in Swift, AppleScriptObjC, C# (Mono), or code from other languages may also help you to get the work flow figured out.

My goal was to get the RGB color values for the accent color that the user has selected in the System's Preferences panel. Having done my research I found out that it's a special semantic System color that I'm after, and the color is called 'controlAccentColor' and it is part of a list of colors called 'System'. Searching Apple's site I find the relevant info, the NSColorList class https://developer.apple.com/documentati ... guage=objc ( Objective C, but you can also look at the Swift equivalent from a drop down menu at the top of the page).

There we find NSColorLists object class that has a method 'availableColorLists' here: https://developer.apple.com/documentati ... guage=objc which is defined like so:

Code: Select all

@property(class, readonly, copy) NSArray<NSColorList *> *availableColorLists;
This tells us that this is a read only property, and the property will contain an Array object (NSArray class), which in turn contains object(s) of type 'NSColorList', each individual element of this Array object will be a separate color list. We will receive the NSArray object by calling this property getter method named 'availableColorLists'
So here is how we wrap that for use in extension building:

Code: Select all

private foreign handler ObjC_NSColorListAvailableColorLists() returns ObjcId binds to "objc:NSColorList.+availableColorLists"

To break this down:
private foreign handler -- most will start like this, again the private just means it's a 'module' scoped handler, and so it won't be visible or callable from outside of your extension module.
ObjC_NSColorListAvailableColorLists -- This is an arbitrary name I made based on the language Objective C + protocol name (NSColorList) and the relevant method name (availableColorList) . You can use your own handler naming convention if it makes more sense to you. Whatever name you use here will be the name you use to call the foreign handler from within your extension handlers.
() -- this needs no parameters at all
returns ObjcId -- the returned value will be of an Objective C Object ID type, which is basically just a number that helps the system keep track of the object in memory.
binds to "objc:NSColorList.+availableColorLists" this is the actual binding string. The objective c binding breaks down similar to the 'C API example in the first post, but with some variation. objc: is obviously short for Objective C, the language that the foreign code we want to bind to was written in, followed by ':' NSColorList is the name of the library or system API that we want the compiler to look for, followed by a period. The + plus sign says this is a method that is going to return a new copy of the object, because this property object is read only, we can only work with a copy of the object. And lastly availableColorLists is the name of the method we want to use.

So now that we have the binding, we can call on this from Extension Builder handlers (because it's 'private ' scoped it can only be called from within this same file)

Here's the handler that uses the binding:

Code: Select all

public handler getNSColorListAvalableLists() returns optional any
   variable tAvailableColorLists as optional ObjcId
   variable tColorLists as optional List
   variable tNSColorList as optional ObjcId
   variable tNSStrObj as optional ObjcId
   variable tString as optional String
   variable tReturnString as optional String
   put "" into tReturnString
   unsafe
      put ObjC_NSColorListAvailableColorLists() into tAvailableColorLists
      if tAvailableColorLists is not nothing then
         put ListFromNSArray(tAvailableColorLists) into tColorLists
        -- log [tColorLists]
         repeat for each element tNSColorList in tColorLists
            put ObjC_NSColorListName( tNSColorList) into tNSStrObj
            put StringFromNSString(tNSStrObj) into tString
            log [tString]
            put tString & "\n" after tReturnString
         end repeat
         delete char -1 of tReturnString
      end if
   end unsafe
   return tReturnString
end handler
I'll break the handler down:
public handler -- this handler is public and do will be available to use from your xTalk scripts.
getNSColorListAvalableLists() arbitrary handler name, this will be the name you use to call this handler from your xTalk scripts. () there's no input parameters needed for this function handler. returns optional any this says we will optionally be returning some sort of data of any (or unknown) data type.The optional keyword says that this could also return nothing (null), instead of any thing. This is convenient when you aren't yet sure what format the data you pass back to the script engine will be in. It could be a Number, it could be a text string, it could be an array or any other type.

Next there are some variable type declarations for variables we are going to need to store data.
The compiler needs to know up-front what type of data variables will contain so that it can reserve enough memory to store the data.
variable tAvailableColorLists as optional ObjcId -- will store either nothing (optional) or our NSArray object's id
variable tColorLists as optional List -- will store an Extension Builder List type which the NSArray will be converted to.
variable tNSColorList as optional ObjcId this will temporarily store the individulal NSColorList objects are contained in the elements of the NSArray object, as we step through the list of available color list names.
variable tNSStrObj as optional ObjcId -- This will temporarily store NSString objects as we use to convert color lists names into normal text strings.
variable tString as optional String -- This will contain Extension Builder String type data of the individual color list names as converted to normal Strings.
variable tReturnString as optional String -- This will store the combined list of names as text string that will be returned to our xTalk scripts

put "" into tReturnString -- initialize our text string list with an empty string

unsafe -- foreign handlers normally can only be used inside of a unsafe/end unsafe block.

put ObjC_NSColorListAvailableColorLists() into tAvailableColorLists -- the foreign function we bound to earlier, the puts the returned color lists NSArray object into the tAvailableColorLists variable we declared above.

if tAvailableColorLists is not nothing then -- this ensures that we got back an object instead of nothing

put ListFromNSArray(tAvailableColorLists) into tColorLists -- this is a built in handler that convert the NSArray objects to Builder's List type

log [tColorLists] -- 'log' prints the value of variables inside the brackets to the Extension Builder stack's console field, useful for debugging purposes.

repeat for each element tNSColorList in tColorLists --- step through the elements of the List, which should contain NSColorList objects

put ObjC_NSColorListName( tNSColorList) into tNSStrObj -- this is another foreign function that gets the name property of NSColorList objects as NSString objects
here's the binding:

Code: Select all

private foreign handler ObjC_NSColorListName(in pNSColorList as ObjcId) returns ObjcId binds to "objc:NSColorList.name"
This expects an ObjcId for an NSColorList object as a parameter, and then sends back the value of the NSColorList's 'name' property as an ObjcId for an NSString object that contains the name of the NSColorList. How did I know what it expects and returns? From Apple's documentation here: https://developer.apple.com/documentati ... guage=objc

put StringFromNSString(tNSStrObj) into tString -- a 'built-in' function that converts NSString objects into normal text String

put tString & "\n" after tReturnString -- add the name string to the return string list. &"\n" is the equivalent to & cr in normal xTslk script.

delete char -1 of tReturnString -- deletes the trailing newline from the list so that there ism't an empty line at the end of a list

end unsafe --- were done using foreign handlers.

return tReturnString -- returns the combined text list

continued...
User avatar
OpenXTalkPaul
Posts: 2391
Joined: Sat Sep 11, 2021 4:19 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by OpenXTalkPaul »

richmond62 wrote: Tue Feb 13, 2024 2:18 pm If any of that is anything vaguely close to 'plain English', I, for one, have obviously been speaking something other than English for coming on for 59 years (I am 62, and I learnt English when I was about 3).
This may be as plain English as I can make it. Words like 'signed integer' have specific English meanings, but you''re also going to need to learn about some commonly used programming language variable primitive types. By that I mean what the parameters of a 'C' integer type (lcb type CInt or Integer) are. It's going to be some specific number range, likely based on some multiple of 8. You're probably already familiar with the CUInt range, its a number in the range of 0-255, so it will fit into a single byte of memory.

I think there's only so much it can be simplified, so I think you must be willing to learn some slightly-lower-level programming terms if you're going to try to tap into C, C++, ObjC, JAVA (and perhaps to a lesser degree JS) libraries and APIs.
Mind you the stuff I'm covering here only apples to the libFFI (Foreign Function Interface) stuff.

if you just want to make custom controls widgets using the stuff that Extension Builder and the Engine already comes with, that may be a bit easier to learn, but there is already some tutorials and quite a few examples that can be examined, that are available for the Widget stuff. But maybe I'll try to cover that in my own way later.

It would be helpful if you told me what specific terms that are not plain English enough in your opinion (for an adult with some experience as the main target audience), so that I can revise this as I go. Or let me know if there's some specific thing I should elaborate more on. But this is NOT going to be a general 'Beginners Guide'. Beginners should start with the xTalk scripting language. Beginners and the xTalk-curious will benefit from there being a wider variety of capabilities available to use (IMO, that IS why Python is popular), perhaps a few people will learn enough to collaborate on wrapping some real GPU accelerated game engine(s) so that the kids can use them with xTalk script, that would be a real...game changer :lol:
User avatar
OpenXTalkPaul
Posts: 2391
Joined: Sat Sep 11, 2021 4:19 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by OpenXTalkPaul »

richmond62 wrote: Tue Feb 13, 2024 8:54 pm
for an adult with some experience as the main target audience
'some experience' has to be more tightly defined as, maybe, 'some experience with non-xTalk languages within the last 10-15 years'.
OK, I'm editing the posts as we go, so I'll try to get that in the next edits.
I just now tried to clean up the intro to object oriented programming.
User avatar
OpenXTalkPaul
Posts: 2391
Joined: Sat Sep 11, 2021 4:19 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by OpenXTalkPaul »

OpenXTalkPaul wrote: Tue Feb 13, 2024 2:12 pm ....
return tReturnString -- returns the combined text list

continued...
IF you've made it this far, you might be thinking why use Object metaphor at all?
To some degree OOP is about encapsulation. An Object may contain both categorized data elements like a database record AND code needed to do stuff with that data, all in one thing. Because an object will inherit attributes (properties) AND methods (functions and commands) of its parent object class(es), object oriented programming encourages code reusability. For example NSColor object doesn't need to have its own memory allocation routine because it inherits one that already exists in the top NSObject parent class that all of these objects have in common.

So with that as prelude, I'm going to post some 'boiler plate' handlers that I use A LOT to help me inspect some basic attributes for ANY Objective C objects I may be working with.

Code: Select all

 private foreign handler objC_NSObjectClassName(in pNSObj as ObjcId) returns ObjcId binds to "objc:NSObject.className"
 
 public handler getNSObjectClassName(in tNSObj as optional ObjcId) returns optional String
     variable tNSStrObj as optional ObjcId
     variable tStr as optional String
     if tNSObj is not nothing then
        unsafe
           put objC_NSObjectClassName(tNSObj) into tNSStrObj
           put StringFromNSString(tNSStrObj) into tStr
           return tStr
        end unsafe
     else
        return nothing
     end if
 end handler
This uses NSObject's className method that all other objects inherit. You pass in any sort of object's ObjcId and get back a regular text string that contains the name of the Object's class property. I use this handler to check that the object I'm dealing with is the sort of object that I expected to be dealing with. Sometimes it may not be an object in the class of object that I had expected. For example, if I'm using NSImage objects I might expect to receive an object that has some pixel data in it, but instead I get back an NSArray object that contains a bunch of objects that have pixel data for different resolutions. For example an Icon image may have very tiny 16x16 pixel representation AND huge 1024x1024 version of the icon contained within the same NSImage object. So if I use this handler to inspect the object's class, then I'll know that what I'm dealing with is an NSArray class of object instead of what I thought it would be, and then I can adjust my code to deal with that possibility.
User avatar
OpenXTalkPaul
Posts: 2391
Joined: Sat Sep 11, 2021 4:19 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by OpenXTalkPaul »

Here's another, slightly more elaborate utility handler for inspecting Objective C objects:

Code: Select all

 
 private foreign handler objC_NSObjectClassName(in pNSObj as ObjcId) returns ObjcId binds to "objc:NSObject.className"
  private foreign handler c_NSClassFromString(in pClassNameString as ObjcId) returns ObjcId binds to "c:NSClassFromString"
  private foreign handler objC_NSObjectGetDescription(in pNSObj as ObjcId) returns ObjcId binds to "objc:NSObject.description"
  private foreign handler ObjC_NSObjectGetAttributeKeys( in pNSObjct as ObjcId) returns optional ObjcId binds to "objc:NSObject.-attributeKeys"

 public handler LogNSObjectReflection(in pNSObj as ObjcId)
     variable tNSStrObj as optional ObjcId
     variable tNSObj as optional ObjcId
     variable tStr as optional String
     variable tClass as ObjcId
     variable tPropsCount as optional UInt32
     variable tListNSObj as optional ObjcId
     variable tListOpaquePtr as optional Pointer -- points to C array of 'objc_property_t'

     if pNSObj is not nothing then
        unsafe
           put objC_NSObjectClassName(pNSObj) into tNSStrObj
           put StringFromNSString(tNSStrObj) into tStr
           log ["Class Name" , tStr]
           put c_NSClassFromString(tNSStrObj) into tClass

           put objC_NSObjectGetSuperClass(pNSObj) into tNSObj
           put objC_NSObjectClassName(tNSObj) into tNSStrObj
           put StringFromNSString(tNSStrObj) into tStr
           log ["SuperClass" , tStr]

           put objC_NSObjectGetDescription(pNSObj) into tNSStrObj
           put StringFromNSString(tNSStrObj) into tStr
           log ["Description" , tStr]

           put ObjC_NSObjectGetAttributeKeys(pNSObj) into tListNSObj
           if tListNSObj is not nothing then
              put StringFromNSString(tListNSObj) into tStr
              log ["AttributeKeys" , tStr]
           end if
           put c_class_copyPropertyList(tClass,tPropsCount) into tListOpaquePtr
           log ["Props List Count and Pointer", tPropsCount,tListOpaquePtr ]
        end unsafe
     else
        log "No Object"
     end if
  end handler
 
This prints information about any class of NSObject that you pass to it as its ObjcId parameter and then logs the information it finds out about that object to the log field in the extension builder stack.
That is the Object's Class Name, its SuperClass name which is the parent class of this object's class, a text description of the object, its attributes keys, the number of properties the object contains and a pointer to a property list where the object's properties can be found.

Notice that here I have bindings for C based APIs for Apple's CoreFoundation framework, mixed in with Objective C based bindings, which is perfectly fine and sometimes necessary to do. Many of the older or more 'low level' frameworks (CoreFoundation, CoreGraphics, CoreAudio, etc.) from NeXTStep / Apple are C based, not Objective C based. This means we're not using NSObjects when dealing with those APIs, instead they return C variable types such as CBool for a Boolean, or CInt for 0-255 numbers instead of NSNumbers, or Pointers instead of ObjId, for a few examples, and of it means that the binding strings will be formatted as C and C++ binding strings (as seen in the first post of this thread). One advantage to using C and C++ API's is that because our Engine is written in C and C++ there may be less converting or formatting of data into formats that we can send back to our xTalk scripts.
User avatar
OpenXTalkPaul
Posts: 2391
Joined: Sat Sep 11, 2021 4:19 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by OpenXTalkPaul »

Getting back to the mission of getting that color information....
Now that we have bindings and handler written that calls the NSColorList 'availableColorLists' method we can test it out by compiling the module from Extension Builder in our IDE. Once the module compiles successfully we can use it immediately from the message box.

Code: Select all

put getNSColorListAvalableLists()
Returns the list of Color Lists that come with macOS.
The result:
Apple
System
Crayons
Web Safe Colors

Yay! The list includes the color list I was looking for which is called 'System'.

It is kind of important to check for the list of color lists for the list that I'm after before asking the system for contents of that list, because that particular list may not be available on the current macOS. The 'Apple' color list is available on macOS 10.0+, but the 'System' color list wasn't introduced until around Mac 10.10+, so if you're still running macOS 10.9 Mavericks or lower, the 'System' color list did not yet exist.

The same goes for the controlAccentColor that I'm after, which wasn't available in macOS versions before 10.14 Mohave.
How do I know that? Because Apple's documentation shows when some syntax or feature was introduced in macOS.
Screenshot 2024-02-14 at 4.09.47 PM.png
Screenshot 2024-02-14 at 4.09.47 PM.png (87.31 KiB) Viewed 11517 times
I'm testing this code on macOS 11 (BigSur) so the 'System' color list doe exists in the avaialableColorLists property.
Now I need to get that list to see if a color named 'controlAccentColor' is included in its list of colors.

Code: Select all

private foreign handler ObjC_NSColorListColorListNamed(in pNSColorLists as ObjcId) returns optional ObjcId binds to "objc:NSColorList.+colorListNamed:"
private foreign handler ObjC_NSColorListColorListAllKeys(in pNSColorList as ObjcId) returns optional ObjcId binds to "objc:NSColorList.allKeys"

public handler getNSColorListNamed(in pColorListName as optional String) returns optional any
   variable tNSColorList as optional ObjcId
   variable tNSArray as optional ObjcId
   variable tColorKeysList as optional List
   variable tString as optional String
   variable tReturnString as String
   variable tColorName as String
   put "" into tReturnString
   unsafe
      put ObjC_NSColorListColorListNamed(StringToNSString(pColorListName)) into tNSColorList
      if tNSColorList is not nothing then
         -- LogNSObjectReflection(tNSColorList)
         put ObjC_NSColorListColorListAllKeys(tNSColorList) into tNSArray
         put ListFromNSArray( tNSArray ) into tColorKeysList
         sort tColorKeysList in ascending text order
         repeat for each element tString in tColorKeysList
            put tString & "\n" after tReturnString
         end repeat
         delete char -1 of tReturnString
      end if
   end unsafe
   return tReturnString
end handler
This looks a lot like the previous handler, probably because I copy/pasted that handler to use as a template.
We can get an NSColorList object by name using the NSColorList's 'colorListNamed' method, which we will pass the name "System" to, but the name string needs to be an NSString object for Objective C to accept it, so we use StringToNSString to convert it as we are passing it to the colorListNamed method.
Next the If/then checks the return value to make sure it's not null / nothing ('empty' would be the xTalk script version of this).
-- LogNSObjectReflection is my utility handler I posted previously. it's commented out now.
Next we pass our NSColorList Object to NSColorList's allKeys method in order to get back an NSArray that contains elements that are the name keys for the colors in the color list.
Next the tNSArray is converted to an xTalk-native Array using the 'built in' function ListFromNSArray
Next we use sort our Array keys so that they're in nice alphabetically order.
Next we step through the array of name keys, adding each name and a new line character ( "\n" ) to the string list that we will be returning. Lastly we delete the trailing new line character so that we don't have an empty line at the end of our returned list and finally we return the line delimited list as our result.

The result:

Code: Select all

alternateSelectedControlTextColor
alternatingContentBackgroundColor
controlAccentColor   <------------------------- BINGO!!! There's the color I wanted
controlBackgroundColor
controlColor
controlTextColor
disabledControlTextColor
findHighlightColor
gridColor
headerTextColor
keyboardFocusIndicatorColor
labelColor
linkColor
placeholderTextColor
quaternaryLabelColor
secondaryLabelColor
selectedContentBackgroundColor
selectedControlColor
selectedControlTextColor
selectedMenuItemTextColor
selectedTextBackgroundColor
selectedTextColor
separatorColor
systemBlueColor
systemBrownColor
systemGrayColor
systemGreenColor
systemIndigoColor
systemOrangeColor
systemPinkColor
systemPurpleColor
systemRedColor
systemTealColor
systemYellowColor
tertiaryLabelColor
textBackgroundColor
textColor
underPageBackgroundColor
unemphasizedSelectedContentBackgroundColor
unemphasizedSelectedTextBackgroundColor
unemphasizedSelectedTextColor
windowBackgroundColor
windowFrameTextColor
And there's our special color!

Now that we know the "System" color list and the "controlAccentColor" both exist on our system, we can retrieve that color so we can inspect its Red, Blue, Green values. From the documentation in the screenshot above we can see that the color will be returned to us as an NSColor object.
User avatar
OpenXTalkPaul
Posts: 2391
Joined: Sat Sep 11, 2021 4:19 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by OpenXTalkPaul »

So the next part should be a piece of cake, right? WRONG!

Code: Select all

private foreign handler ObjC_NSColorListColorListNamed(in pNSColorLists as ObjcId) returns optional ObjcId binds to "objc:NSColorList.+colorListNamed:"
private foreign handler ObjC_NSColorListColorWithKey(in pNSColorList as ObjcId, in pNSColorName as ObjcId) returns optional ObjcId binds to "objc:NSColorList.-colorWithKey:"
private foreign handler c_CGColorSpaceCreateDeviceRGB() returns optional Pointer binds to "c:CGColorSpaceCreateDeviceRGB"
private foreign handler ObjC_NSColorSpaceAlloc() returns ObjcRetainedId binds to "objc:NSColorSpace.+alloc"
private foreign handler ObjC_NSColorSpaceInitWithCGColorSpace(in pObj as ObjcRetainedId, in CGColorSpaceRef as optional Pointer) returns ObjcId binds to "objc:NSColorSpace.-initWithCGColorSpace:"
private foreign handler ObjC_NSColorUsingColorSpace (in pNSColor as ObjcId, in pNSColorSpace as ObjcId) returns ObjcId binds to "objc:NSColor.-colorUsingColorSpace:"
private foreign handler ObjC_NSColorNumberOfComponents(in pColor as ObjcId) returns CUInt binds to "objc:NSColor.numberOfComponents"
private foreign handler ObjC_NSColorGetRGBA(in pNSColor as ObjcId, out rRed as optional CDouble, out rGreen as optional CDouble, out rBlue as optional CDouble, out rAlpha as optional CDouble ) returns nothing binds to "objc:NSColor.getRed:green:blue:alpha:"

public handler getNSColorGetSystemColorRGB(in pColorcNameStr as String) returns optional any
   variable tNSColorList as optional ObjcId
   variable tNSColor as optional ObjcId
   variable tNSColorSpace as optional ObjcId
   variable tCGColorSpaceRef as optional Pointer
   variable tColorKeysList as optional List
   variable tColorName as String
   variable rRed as optional Number
   variable rBlue as optional Number
   variable rGreen as optional Number
   variable rAlpha as optional Number

   variable tInteger as optional Integer
   variable tString as optional String
   variable tReturnString as String
   variable tAny as optional any

   put "" into tReturnString
   unsafe
      put ObjC_NSColorListColorListNamed(StringToNSString("System")) into tNSColorList
      if tNSColorList is not nothing then

         put ObjC_NSColorListColorWithKey(tNSColorList, StringToNSString(pColorcNameStr)) into tNSColor
         
          put c_CGColorSpaceCreateDeviceRGB() into tCGColorSpaceRef
          log tCGColorSpaceRef -- check that we received a C Pointer
          
          put ObjC_NSColorSpaceAlloc() into tNSColorSpace -- creates a new NSColorSpace object
          put ObjC_NSColorSpaceInitWithCGColorSpace(tNSColorSpace,tCGColorSpaceRef) into tNSColorSpace
          LogNSObjectReflection(tNSColorSpace) -- Check object
          
          put ObjC_NSColorUsingColorSpace(tNSColor,tNSColorSpace) into tNSColor
          LogNSObjectReflection(tNSColor)
          
          put ObjC_NSColorNumberOfComponents(tNSColor) into tInteger
          log ["Number of Color Components",tInteger]
          
          ObjC_NSColorGetRGBA(tNSColor,rRed,rGreen,rBlue,rAlpha)
          put rRed * 255 into rRed
          put rGreen * 255 into rGreen
          put rBlue * 255 into rBlue
          put rAlpha * 255 into rAlpha
          
          if rAlpha = 255 then -- opacity is 100%
             return rRed formatted as string &","& rGreen formatted as string &","&  rBlue formatted as string
          else
             return rRed formatted as string &","& rGreen formatted as string &","&  rBlue formatted as string &","&  rAlpha formatted as string
          end if
      else
         return "Error: Color Name Not Found"
      end if
   end unsafe

   return tReturnString
end handler
We start out by getting the "System" color list by passing that key name to NSColorList's method 'colorListNamed', again converting the String to an ObjC NSString object. This method returns an NSColorList object.

We already know that the color we want is in that object, so we can ask for the color by the name key that we provided as a string parameter to when calling this function, pColorcNameStr (we will pass in the string "controlAccentColor" when calling this handler later). We ask for the color by using another NSColorList method, 'colorWithKey', which takes two parameters, the NSColorList object and the name key of the color we want. As usual we must convert the name string to an NSString object so that the Objective C method will accept it as a valid parameter ("every thing is an object"). Once again we convert the string to an NSString object as it gets passed using the built in function StringToNSString(). StringToNSString and StringFromNSString get used a LOT when working with Objective C stuff!

The colorWithKey method returns an NSColor object. We're getting closer!
However, if we try to get some RGB values from that NSColor object we will get an error message saying that's it's not a valid ask in this object's current color space. That's because these System colors are special 'Semantic colors' and so it doesn't come with any color space attributes, the NSColor does not currently have any Red,Blue,Green components (I think Apple could've done something here to save us some trouble, but they didn't)...

What is a 'color space'? Well that's a big topic in itself and I really don't want to get into color theory here, but just know that it has to do with color perception and color calibration and applies to various forms or color output such as your computer monitor's colors vs CMYK printing colors and that sort of thing. Some Color Spaces you may have seen mention of in various graphics applications such as PhotoShop, they have names like 'Device RGB' or 'Adobe RGB 1998'.

We need our special NSColor object to have an RGB color space representation. So how do we get that? We can't add it to the NSColor object (Apple could have), it's a read only (immutable) thing, but we are allowed to make copies of the object. So that's what we want, a copy of the NSColor object that does have RGB color space. We can make one with the aloc (short for allocate) method, which can be used to make any class of object. The newly allocated object has not been initialized with any contents yet. We need to follow any alloc method with some kind of initialization method before we can do anything useful with an object. NSColor has an initializer method 'InitWithCGColorSpace' that takes two parameters, an allocated NSColor object, and CoreGraphics Color Space reference (which will be a C Pointer type variable).

So before we can use the InitWithCGColorSpace method we need to make that CGColorSpace to pass to it, and we need that to be a color space that will have RGB components. CoreGraphics has a function 'CreateDeviceRGB' that returns a pointer to the sort of color space we need. So then we need a 'C' binding string (CoreGraphics is a C based API, not Objective C ) so we can use that function.

Once we have our pointer to Device RGB color space, we must then create (with alloc method) a new NSColorSpace object, only because we needs to be an Objective C object, and not a C pointer to a color space that it currently is, in order foe NSColor to accept it as a parameter. (Again, Apple could've did something here to save us some trouble)

So we pass our color space C Pointer to initialize the newly created NSColorSpace object as the second parameter to the NSColorSpace's method 'initWithCGColorSpace' to finish making our NSColorSpace object which is now set to a RGB color space.

So how does that help our pre-existing special color that lacks RGB? NSColor has a method 'colorUingColorSpace' that takes an NSColor object as first parameter and a NSColorSpace object as its second parameter and returns a new NSColor object that has the RGB color space included.

We can check that our new RGB NSColor object has some color components by checking NSColor's property 'numberOfComponents' which returns the integer 4, which is correct (R+G+B+Alpha), hooray!!!

Now we just need to get the values of the color components. We could get the NSColor object's components properties one at a time, but there's also a method to get them all at once. The binding string for that looks like this: "NSColor.getRed:green:blue:alpha:". So this takes an NSColor object, and 4 other parameters. When binding to Objective C the parameters are separated by ':', the colon character in the binding string (which the same as it appears in Apple's ObjC docs).
But these are OUT parameters, up until now we've only dealt with IN parameters and at most a single return value.
Here's the full binding I used for this method so you can examine how OUT parameters are declared:

Code: Select all

private foreign handler ObjC_NSColorGetRGBA(in pNSColor as ObjcId, out rRed as optional CDouble, out rGreen as optional CDouble, out rBlue as optional CDouble, out rAlpha as optional CDouble ) returns nothing binds to "objc:NSColor.getRed:green:blue:alpha:"
We call this method like so:

Code: Select all

ObjC_NSColorGetRGBA(tNSColor,rRed,rGreen,rBlue,rAlpha)
When we call this foreign handler there is no single return value, meaning it doesn't 'return' anything in the way we are used to, there is nothing to 'put' into some variable. Instead each parameter variables passed to the foreign handler gets filled OUT by the method. There is another variation of this mechanism you may encounter, INOUT, which transforms existing values of variables that are passed in and then fills the variables back out with transformed/new values.

So now our rRed,rGreen,rBlue,rAlpha variables filled out, we're good right? That depends on what you want. The values that we get are floating point numbers with the range of 0.0 to 1.0 (they're percentages), but we want our RGBA to be 24bit (+8bit Alpha) color values, which means integers in the range of 0-255, so that we can use them with our xTalk scripting language. To get them to be what we need, we must multiply these values by 255.

FINALLY! We have what we wanted and can return the values as the result to the calling xTalk script.
We can't pass back a 3 or 4 of number as a single return value, so we must combine them in some way. We could return them as an Array with these values as elements, but then we would then need to parse that Array on the xTalk scripting side. Instead we can return these values already formatted as a comma delimited string of numbers, ready to use with xTalk scripting color properties. Because these values are integers and not strings and because Extension Builder is strongly typed, we must convert each integer to string with the Extension Builder syntax 'formatted as string' and add in some commas while we're at it.

Lastly I added a check for the Alpha (transparency) component to see if it's 100% opaque, and if it is don't bother providing it in the return string. I decided to do that because xTalk script color properties normally only use R,G,B values, but don't normally support Alpha (R,G,B,A) values directly, most script objects do have a separate opacity property.

We could get fancier, add option for returning values as web-ready hexadecimal values (like "#FF007F"), and it probably should have more error checking, but my goal is complete!

It was a convoluted process, but it's done now, and these Objective C methods are now wrapped with Extension Builder bindings and can be re-used for other color related tasks for other projects!
User avatar
OpenXTalkPaul
Posts: 2391
Joined: Sat Sep 11, 2021 4:19 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by OpenXTalkPaul »

Comments?
Suggestions?
Corrections?
Questions?
Was anyone even following along?
Did anyone learn anything?
User avatar
OpenXTalkPaul
Posts: 2391
Joined: Sat Sep 11, 2021 4:19 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by OpenXTalkPaul »

We should let MacOS xTalk scripters know about these new function handlers we've just added to provide some clues about how they can use it. We can do that by adding some metadata to our module which will get added to the Syntax dictionary dynamically when the Extension Builder module is loaded into our IDE. It's easy to do.

Inline docs can be added in a block-comment above the handler that it relates to. The beginning comment characters must have an extra asterix, /** (TWO) as opposed to /*, this lets the Extension compiler/loader know that this comment block should be parsed as documentation and not ignored as it would ignore a normal comment block.

Here's a basic template with generic names:

Code: Select all

/**
begin with a short description

Type: function

Syntax: myFunctionName pParam1, pParam2 etc. etc.

Example:
provide a basic working example of using the syntax here

Parameters:
pParam1: The number range or type of value that should be passed in for pParam1
pParam2: The number range or type of value that should be passed in for pParam2
etc. 

Description:
A more detailed description should go here
*/
There are some more optional fields that can be used (such as 'Platform:') and the docs parsing mechanism may also add some fields automagically.

Lets add documentation for 'getNSColorGetSystemColorRGB' as an example

/**
getNSColorGetSystemColorRGB retrieves RGB color value of a color from the list of macOS special System colors.

Type: function

Syntax: getNSColorGetSystemColorRGB( pColorcNameStr )

Example:
set the background color of graphic 1 to getNSColorGetSystemColorRGB("controlAccentColor")

Parameters:
pColorcNameStr: The name of a color that is part of the macOS special System colors list

Description:
macOS includes a set of special system colors that may be used throughout the operating system and can be used by developers to make their apps user interface be consistent with the macOS appearance. getNSColorGetSystemColorRGB returns the RGB color values of one of these System colors for the name that is passed to this function. A list of System color names can be obtained using getNSColorListNamed("System").
*/
public handler getNSColorGetSystemColorRGB(in pColorcNameStr as String) returns optional any --> this actually returns String

.... snip...

I try to use a full line space to seperate between the 'fields'. I'm not sure if that's required or not, but it seems to help.

... to be continued ...
xAction
Posts: 285
Joined: Thu Sep 16, 2021 1:40 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by xAction »

InlineDocumentationBuilder_0001.oxtStack
(11.88 KiB) Downloaded 258 times
inlineDocumentationManager.png
inlineDocumentationManager.png (33.55 KiB) Viewed 11391 times
I need to have things to do to learn new things. This is just something I think the Builder needs...the steps laid out, everything that is required . I foresee some magical file reading wizardry to solve what is unsolved... lol.

NSMacStuff never stuck with me.

Okay here's a maze program in c++, it shows no window, it just dumps a bunch of files depicting :
the topleft corner xy coordinate of any Wall, or Clear space of a maze
A text file consisting of 1s and 0s representing maze grid states


How do we turn this speedy recursion into a widget or extension to just get a bunch of numbers at c++ speeds?
How do we turn c++ gobbleygook into inline Documenation? It says what it does right there, and we want it to do it's thing for us.

Code: Select all

#include <vector>
#include <algorithm>
#include <random>
#include <fstream>
#include <string>

const int mazeWidth = 32;
const int mazeHeight = 32;

int maze[mazeHeight][mazeWidth];

// Define directions for movement
std::vector<std::pair<int, int>> directions = {
    {0, 1},   // Right
    {0, -1},  // Left
    {1, 0},   // Down
    {-1, 0}   // Up
};

// Function to initialize the maze with walls
void initmaze() {
    for (int i = 0; i < mazeHeight; ++i) {
        for (int j = 0; j < mazeWidth; ++j) {
            maze[i][j] = 1;
        }
    }
}

bool isValid(int x, int y) {
    return x >= 0 && x < mazeWidth && y >= 0 && y < mazeHeight;
}

// Function to carve the maze using recursive backtracking
void recursiveBacktrack(int x, int y) {
    maze[y][x] = 0; // Mark the current cell as open

    std::random_device rd;
    std::mt19937 g(rd());
    std::shuffle(directions.begin(), directions.end(), g);

    for (auto& [dx, dy] : directions) {
        int newX = x + dx * 2;
        int newY = y + dy * 2;

        if (isValid(newX, newY) && maze[newY][newX] == 1) {
            maze[y + dy][x + dx] = 0; // Open the wall between current and next cell
            recursiveBacktrack(newX, newY);
        }
    }
}

// Function to add random rooms to the maze
void addRandomRooms() {
    std::random_device rd;
    std::mt19937 g(rd());
    std::uniform_int_distribution<int> roomCount(10, 16);

    int numRooms = roomCount(g);
    while (numRooms > 0) {
        int roomX = std::uniform_int_distribution<int>(1, mazeWidth - 6)(g);
        int roomY = std::uniform_int_distribution<int>(1, mazeHeight - 6)(g);

        int roomWidth = std::uniform_int_distribution<int>(2, 8)(g);
        int roomHeight = std::uniform_int_distribution<int>(2, 8)(g);

        for (int y = roomY; y < roomY + roomHeight; ++y) {
            for (int x = roomX; x < roomX + roomWidth; ++x) {
                maze[y][x] = 0; // Mark the room as open
            }
        }

        numRooms--;
    }
}



// Function to save the maze wall data to a CSV file
void saveMazeWallData(const std::string& filename) {
    std::ofstream outfile(filename);
    if (outfile.is_open()) {
        for (int y = 0; y < mazeHeight; ++y) {
            for (int x = 0; x < mazeWidth; ++x) {
                if (maze[y][x] == 1) { // If the cell is a wall
                    outfile << y << "," << x << "\n"; // top,left
                }
            }
        }
        outfile.close();
    }
}

// Function to save the maze clear unit data to a CSV file
void saveMazeClearUnitData(const std::string& filename) {
    std::ofstream outfile(filename);
    if (outfile.is_open()) {
        for (int y = 0; y < mazeHeight; ++y) {
            for (int x = 0; x < mazeWidth; ++x) {
                if (maze[y][x] == 0) { // If the cell is clear
                    outfile << y << "," << x << "\n"; // top,left
                }
            }
        }
        outfile.close();
    }
}

// Function to save the maze data to a text file
void saveMazeDataAsText(const std::string& filename) {
    std::ofstream outfile(filename);
    if (outfile.is_open()) {
        for (int y = 0; y < mazeHeight; ++y) {
            for (int x = 0; x < mazeWidth; ++x) {
                outfile << maze[y][x];
            }
            outfile << "\n";
        }
        outfile.close();
    }
}

int main() {
    initmaze();

    recursiveBacktrack(1, 1);

    addRandomRooms();

    // Save the maze wall data to CSV files
    saveMazeWallData("maze_WALL.csv");
	
	// Save the maze clear data to a CSV file
    saveMazeClearUnitData("maze_CLEAR.csv");
	
    // Save the maze data to a text file
    saveMazeDataAsText("maze_binary_bits.txt");
	
    return 0;
}
But the forums are awful ugly for this stuff.

We should make MULTI-MEDIA STACKS WITH HYPER-LINK TECHNOLOGY explaining everything.
User avatar
OpenXTalkPaul
Posts: 2391
Joined: Sat Sep 11, 2021 4:19 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by OpenXTalkPaul »

xAction wrote: Fri Feb 16, 2024 8:48 am InlineDocumentationBuilder_0001.oxtStack
inlineDocumentationManager.png
I need to have things to do to learn new things. This is just something I think the Builder needs...the steps laid out, everything that is required . I foresee some magical file reading wizardry to solve what is unsolved... lol.
Something like this would certainly be useful to have, particularly with Widget modules since there can be a lot of property inspector related metadata that should be added and specific identifier names that must be used, specific names for mirroring the IDE's 'classic controls' properties.

I was trying to collect a concise list of all of the meta data and inline docs markup that can be used.
I'll have to dig that list out of whatever folder I left it in and add it.
NSMacStuff never stuck with me.
It took a long time and 2 scripting languages to sink in, but now that I'm finally getting the hang of it I've warmed up to Objective C. So much so that I've started looking at the GNUStep base and libraries, they're basic the only cross-platform FOSS version of Apple's APIs (plus some other FOSS libraries for like PDFs and stuff that try to mirror Apple's).

I still don't really care for the way that ObjC reads in AppleScriptObjC.

With Extension Builder we can wrap all the Objective C and make xTalkObjC... you know?
It would be like PyObjC or LauObjC but better because it's xTalk!!! :-D

The first thing I'd do with that code is try to compile it and see what it outputs.
Then I would look at the code and see if I can understand how it works.
For something like this (CSV 0,1 s? ASCII text maze generator?) that's not a lot of code and it outputs plain text, I'd probably just try to rewrite it with plain old xTalk script, and if I needed one of those #include 'd math type libraries to make it work, then I'd try to tap those functions or wrap some other math library that does the same sort of functions with Extension Builder binding strings and then call on them from my xtalk version.

I'll try to look at this code later and get back to you.

EDIT:
I just tried to compile it real quick with> gcc Maze.cpp -lstdc++ -o maze
But there are 2 errors and 3 warnings:

Code: Select all

Maze.cpp:13:31: error: a space is required between consecutive right angle brackets (use '> >')
std::vector<std::pair<int, int>> directions = {
                              ^~
                              > >
Maze.cpp:13:34: error: non-aggregate type 'std::vector<std::pair<int, int> >' cannot be initialized with an initializer list
std::vector<std::pair<int, int>> directions = {
                                 ^            ~
Maze.cpp:41:10: warning: 'auto' type specifier is a C++11 extension [-Wc++11-extensions]
    for (auto& [dx, dy] : directions) {
         ^
Maze.cpp:41:16: warning: decomposition declarations are a C++17 extension [-Wc++17-extensions]
    for (auto& [dx, dy] : directions) {
               ^~~~~~~~
Maze.cpp:41:25: warning: range-based for loop is a C++11 extension [-Wc++11-extensions]
    for (auto& [dx, dy] : directions) {
                        ^
3 warnings and 2 errors generated.
I agree, about forums not being the best for presenting information, but I was linking I could write it all down here, let other people give feedback, and collect it into a better presentation format when it's all together, and concise, and hopefully nicely edited.
xAction
Posts: 285
Joined: Thu Sep 16, 2021 1:40 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by xAction »

Well the idea behind converting something simple like this to Builder instead of straight xTalk is to see the logic and the steps that go behind converting a tiny C++ program to Builder to identify the patterns in language and thinking.

Then to use that simple example to expand on, ie, expand the C++ program to do something different, capture/manipulate that something through xTalk...like add a scripting language to the maze program...oop now it's a game engine!


But yeah, it's totally doable in xTalk, at least at the 32x32 scale. The slowest part may be the redraw.
Maze_Generator_v1_02_16_2024.oxtStack
(116.11 KiB) Downloaded 249 times
MazeGen01.png
MazeGen01.png (7.91 KiB) Viewed 11330 times
User avatar
OpenXTalkPaul
Posts: 2391
Joined: Sat Sep 11, 2021 4:19 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by OpenXTalkPaul »

xAction wrote: Fri Feb 16, 2024 11:14 pm Well the idea behind converting something simple like this to Builder instead of straight xTalk is to see the logic and the steps that go behind converting a tiny C++ program to Builder to identify the patterns in language and thinking.

Then to use that simple example to expand on, ie, expand the C++ program to do something different, capture/manipulate that something through xTalk...like add a scripting language to the maze program...oop now it's a game engine!


But yeah, it's totally doable in xTalk, at least at the 32x32 scale. The slowest part may be the redraw.
Maze_Generator_v1_02_16_2024.oxtStack
MazeGen01.png
What you want is a tutorial on using the core Extension Builder language, that's not what I was trying to do here. This was about binding to foreign code libraries/frameworks (you can also bind to some internal Engine functions with: binds to "builtin" or something like binds to "MCWhateverInternalEngineFunction", the MC stands for MetaCard).

Additionally, you seem to have some misperceptions. That's not what Extension Builder is really about. It wasn't created so much to be a standalone language (although there is the lc-run command line that can run modules without the xtalk script engine, lc-run I think was created more for automated testing purposes). And it was not meant to replace xTalk Script, or C++, Objective C, or Java, although it does share some of their traits (and variable types) of all of those.

The libFFI capabilities are more for use in linking to precompiled code libraries built with those other languages.
libFFI is used by Python, Lua, and others in much the same way. That's how those got PyObjC, LuaObjC, or PyGTK, and the like. Builder's FFI is like a dynamic library loader. https://en.wikipedia.org/wiki/Dynamic_loading (in fact with it, you can even directly tap into dlopen, as seen in the examples on that Wikipedia page)

The foreign function interface is more like an easier way to create add-ons than the old Externals SDK was.
Externals were typically coded with that SDK in something like straight C++ ( someone did make a Pascal version SDK a long time ago) and going even further back in time (macOS Classic era() I've gathered that it was compatible with or maybe even based on the XCMDs/XFCNs glue code (HyperCard/SuperCard stuff). The history gets a little fuzzy back that far and a I wasn't a MetaCard user so I wasn't there. Extension

Although, I am sure that it IS possible to code a library with a function that returns a randomly generated ascii text maze and also build a widget with a property that could be set to that text maze to render it, all using nothing but Extension Builder language, without xTalk script and without binding to some external code library (beyond what Extension Builder is already built on which includes libraries like libFFI and libSkia ).

Underneath all of this stuff: the xtalk script interpreter engine, the rendering of unicode text in fields, graphic polygon shapes on a card, Extension Builder, its FFI capabilities, etc. etc. it is compiled code & lots of open source libraries mostly (all of it?) coded with C/C++
User avatar
OpenXTalkPaul
Posts: 2391
Joined: Sat Sep 11, 2021 4:19 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by OpenXTalkPaul »

The Extension Builder's module compiler itself (lc-compile) is built on The GENTLE Compiler Construction System
As seen in the code repository here: https://github.com/livecode/livecode/tr ... ain/gentle


Here's a PDF about GENTLE Compiler Construction System: https://citeseerx.ist.psu.edu/document? ... d1182e3e0d
And a primer here: https://fossies.org/linux/gentle/html/primer.html

The Extension Builder language is extensible with language modules.
There are actually three kinds of Extension Builder modules: Widgets, Libraries, and Language Modules.
Unfortunately LC provided no documentation for making those modules ourselves, but the definition mechanism for adding new syntax in these language modules looks a bit similar to other common language defining notation such as BNF https://en.wikipedia.org/wiki/Backus–Naur_form
User avatar
OpenXTalkPaul
Posts: 2391
Joined: Sat Sep 11, 2021 4:19 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by OpenXTalkPaul »

I should add that much of what I was saying about Object Oriented programming applies not only to the NSMacStuff (and GNUStep Stuff), but many other Object Oriented programming languages including C++ things as well. Many well known open-source libraries use GLib GObject (https://docs.gtk.org/gobject/index.html) libraries like GStreamer and GTK.

You will still need to get properties values from those objects or send messages to those objects similar to using NSObjects. But with C/C++ instead of ObjcId you'll be using 'as Pointer' for the variable type for the variable that stores the reference to the object.

An object may be independently working on its own thing in its own thread the it created with GThread / Pthread. This is that's how I can use my wrapper for libFluidSynth to play MIDI files all day without the engine blocking or lagging and I can still ask it for the name of the file it's currently playing, or find out what time its at in the playback, or tell it to pause playing, etc. etc.
xAction
Posts: 285
Joined: Thu Sep 16, 2021 1:40 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by xAction »

EDIT:
I just tried to compile it real quick with> gcc Maze.cpp -lstdc++ -o maze
But there are 2 errors and 3 warnings:
Something to do with compiler flags I think:
ChatGPT isn't entirely useless:
Certainly! Let’s take a look at your maze-generating program. To compile this program using GCC++ on a Mac, you’ll need to specify some compiler flags. Here are the recommended flags:

-std=c++11: This flag specifies the C++11 standard, which is necessary for some of the features used in your code.

-Wall: Enables all compiler warnings. It’s good practice to catch potential issues during compilation.

-Wextra: Provides additional warnings beyond what -Wall covers.

-O2: Enables level 2 optimization. This improves the performance of the generated code.

-o output_filename: Specifies the output file name (replace output_filename with your desired output file name).

You can compile your program using the following command:

Code: Select all

g++ -std=c++11 -Wall -Wextra -O2 -o maze_generator maze_generator.cpp
Command works for windows too. I have none of that in my original compiler script which is full of lua and FLTK flags.
User avatar
OpenXTalkPaul
Posts: 2391
Joined: Sat Sep 11, 2021 4:19 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by OpenXTalkPaul »

xAction wrote: Sat Feb 17, 2024 10:44 pm EDIT:
I just tried to compile it real quick with> gcc Maze.cpp -lstdc++ -o maze
But there are 2 errors and 3 warnings:
Something to do with compiler flags I think:
ChatGPT isn't entirely useless:
Certainly AI is not without its uses! In fact, I think it's a bit like having an intern or trainee working for you, that you can give all of the mundane or repetitive crap work to do, lol. I've used it to type out scripts or large lists of constants and for generating these binding strings for Extension Builder. A lot of times you have to correct its mistakes, but if you train it, it gets better at tasks very quickly...probably eventually taking all of our jobs !!!
RT.jpg
RT.jpg (8.56 KiB) Viewed 11229 times
But it's here now, and so a programmer that refuses to use AI I think is a bit like a modern equivalent to refusing to use a pocket calculator in the late 1970s 'because that's cheating'.
User avatar
tperry2x
Posts: 2782
Joined: Tue Dec 21, 2021 9:10 pm
Location: Somewhere in deepest darkest Norfolk, England
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by tperry2x »

xAction wrote: Fri Feb 16, 2024 8:48 am We should make MULTI-MEDIA STACKS WITH HYPER-LINK TECHNOLOGY explaining everything.
I'd second this, as that was my thinking with the user guides stack. To make stacks explaining everything, rather than syntax that is a bit of a mystery.

I appreciate Paul has poured a lot of time and effort into trying to explain builder syntax, but I have to admit, I'm no closer to understanding it. I feel like I need to say sorry for this. That's not meant to cause offense, and probably says more about my inability to grasp LCB syntax than anything.

If we could have a stack that actually put the syntax together for you. An ide within an ide, so you choose what you want your lcb extension to do, using drop-down menus / presets, and it'll create the lcb syntax and required files for you?

Would that be the recommended way to build widgets?
I ask, as I'm looking to build a new browser widget for Linux, but honestly — c++ makes more sense right now.
I'm struggling to find anything that details the lcb syntax, and it's not like there are many people out there writing things in it, or detailing how it's put together.

So much so, that I'm thinking of an alternative system to lcb syntax, that can read regular xtalk script. My idea is that it would read these uWidget files (user widgets), which would be in xTalk script, and create widgets in the tools palette that way at startup.

I was thinking of a companion stack that creates these for you. Like I detailed above: allowing you to choose a PNG / SVG for the tool icon, and the script that runs when you drag it off the tools palette. This way, regular users can add their own plugins easily to the tools palette without learning an entirely supplemental language.

Perhaps an idea like this?
easy-widget.png
easy-widget.png (297.33 KiB) Viewed 11002 times
Yes, I know that LCB is a lower level language than xtalk, and as such has possibilities of running things that normal xtalk scripting can't - and I'm not for doing away with lcb or anything.
In fact, quite the opposite if it were in a format the rest of us mere mortals could use and understand. As xAction has mentioned above, there just needs to be a stack that can assist in writing the syntax for you.
Perhaps a "LCB Assistant" stack or something?
User avatar
OpenXTalkPaul
Posts: 2391
Joined: Sat Sep 11, 2021 4:19 pm
Contact:

Re: Extension Builder in plain English - foreign function binding

Post by OpenXTalkPaul »

tperry2x wrote: Fri Apr 05, 2024 8:48 pm
xAction wrote: Fri Feb 16, 2024 8:48 am We should make MULTI-MEDIA STACKS WITH HYPER-LINK TECHNOLOGY explaining everything.
I'd second this, as that was my thinking with the user guides stack. To make stacks explaining everything, rather than syntax that is a bit of a mystery.

I appreciate Paul has poured a lot of time and effort into trying to explain builder syntax, but I have to admit, I'm no closer to understanding it. I feel like I need to say sorry for this. That's not meant to cause offense, and probably says more about my inability to grasp LCB syntax than anything.

If we could have a stack that actually put the syntax together for you. An ide within an ide, so you choose what you want your lcb extension to do, using drop-down menus / presets, and it'll create the lcb syntax and required files for you?

Would that be the recommended way to build widgets?
I ask, as I'm looking to build a new browser widget for Linux, but honestly — c++ makes more sense right now.
I'm struggling to find anything that details the lcb syntax, and it's not like there are many people out there writing things in it, or detailing how it's put together.

So much so, that I'm thinking of an alternative system to lcb syntax, that can read regular xtalk script. My idea is that it would read these uWidget files (user widgets), which would be in xTalk script, and create widgets in the tools palette that way at startup.

I was thinking of a companion stack that creates these for you. Like I detailed above: allowing you to choose a PNG / SVG for the tool icon, and the script that runs when you drag it off the tools palette. This way, regular users can add their own plugins easily to the tools palette without learning an entirely supplemental language.

Perhaps an idea like this?
easy-widget.png

Yes, I know that LCB is a lower level language than xtalk, and as such has possibilities of running things that normal xtalk scripting can't - and I'm not for doing away with lcb or anything.
In fact, quite the opposite if it were in a format the rest of us mere mortals could use and understand. As xAction has mentioned above, there just needs to be a stack that can assist in writing the syntax for you.
Perhaps a "LCB Assistant" stack or something?
I don't disagree, I would love for there to be some sort of assistant, at least to take care of writing the mundane set-up shell project for you, but beyond that I'm not sure you could make a system that generates code for widgets for you. Although we can certainly add more libraries and extend the language to make it easier to do some things, all of the control and UI rendering stuff is largely up to the coder to impliment.

Maybe I'll take some time this week to revisit the Builder-Helper stack I had started.
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest